Java/Micronaut Development Standards
Core Principles
1. Domain-Driven Design (DDD)
Structure code around business capabilities, not technical layers. See docs/ddd-patterns.md for patterns and examples.
Avoid: technical-layer slicing
com.acme.app.controller
com.acme.app.service
com.acme.app.repository
Prefer: business modules (bounded contexts)
com.acme.app/
+-- billing/
| +-- domain/ # Invoice, Payment, Money, BillingPolicy
| +-- application/ # IssueInvoiceUseCase, RecordPaymentUseCase
| +-- infrastructure/ # JooqInvoiceRepository, PaymentGatewayClient
| `-- api/ # BillingController, request/response DTOs
+-- catalog/
| +-- domain/
| +-- application/
| +-- infrastructure/
| `-- api/
`-- identity/
`-- ...
Key rules:
- Top-level packages are business modules (bounded contexts)
- Technical layers (
domain/application/infrastructure/api) are inside each module
- A change like "adjust invoice rules" should touch one module (
billing/), not hop across layers
- Cross-module calls go through explicit APIs (use cases, ports, domain events)
2. No Lombok
Write explicit Java code. Do not use Lombok annotations (@Data, @Getter, @Builder, etc.).
Why: Explicit code is debuggable, IDE-friendly, and avoids compile-time magic issues.
Instead of Lombok:
- Use Java records for immutable data carriers
- Generate getters/setters via IDE
- Write explicit builders when needed
- Use
Objects.equals() and Objects.hash() for equals/hashCode
3. Conservative Dependency Management
Before adding a new library:
- Check if functionality exists in current dependencies
- Check if Micronaut modules already provide it
- Check if Java standard library covers the use case
Analyze classpath first:
./mvnw dependency:tree
# or
./gradlew dependencies
4. jOOQ Persistence
Use jOOQ with Micronaut SQL for relational persistence. Do not introduce Micronaut Data, JPA, or Hibernate for persistence in this skill.
- Schema: Model persistence from migrations/DDL and generated jOOQ schema types
- Queries: Use explicit
DSLContext queries, joins, projections, and locking
- Repositories: Keep domain repository interfaces as ports; implement them with jOOQ in infrastructure
- Mapping: Convert between jOOQ records and domain objects at the adapter boundary
- Transactions: Use
@Transactional on application service methods; use explicit version columns for optimistic locking
See docs/data-access.md for jOOQ repository and mapping patterns.
See docs/transactions.md for transaction management rules.
5. Testcontainers for Integration Testing
Use Testcontainers or Micronaut Test Resources for tests requiring external dependencies (databases, message brokers, Redis, etc.).
See docs/testing.md for setup and patterns.
6. MapStruct for DTO Mapping
Use MapStruct for structural conversions between API DTOs, use-case commands, and responses. Enforce strict boundaries:
- Controllers: Accept/return DTOs only, never touch domain objects or jOOQ records directly
- Services/Use cases: Invoke mappers, resolve relationships by ID, call domain behavior
- Mappers: Pure structural conversion only, no repository access, no business logic
- Domain: Independent of DTOs, web layer, jOOQ records, and SQL details
See docs/mapping.md for patterns and examples.
7. Micronaut Redis Access
Use Micronaut Redis module according to official docs: https://micronaut-projects.github.io/micronaut-redis/6.9.0/guide/
- Prefer Micronaut-provided Redis clients/configuration before introducing custom wrappers
- Keep Redis usage behind ports/adapters in the infrastructure layer
- Use serialization and key naming conventions explicitly
- For tests that depend on Redis, use Testcontainers instead of shared local instances
See docs/redis-access.md for usage guidance.
Workflow
Before Writing Code
Analyze existing dependencies:
./mvnw dependency:tree | grep -E "(micronaut-jooq|jooq|micronaut-data|hibernate|jakarta.persistence|micronaut-validation|micronaut-redis|lombok|mapstruct|test)"
Verify jOOQ stack: Ensure micronaut-jooq, jOOQ code generation, a DataSource, and migrations are configured.
Check for forbidden persistence stack: If Micronaut Data, JPA, or Hibernate are present, discuss removal or isolation before adding persistence code.
Check for MapStruct: If not present and DTOs are needed, add mapstruct + mapstruct-processor.
Check for Lombok: If lombok is in dependencies, discuss removal strategy with user before proceeding.
When Implementing Features
- Start with domain model - domain entities and value objects
- Define repository interfaces (ports) - in domain/application layer
- Create DTOs - request/response records per use case
- Create MapStruct mappers - pure DTO/command/response conversion
- Implement application services/use cases - orchestrate domain operations and transactions
- Implement infrastructure adapters - jOOQ repositories, Redis adapters, external clients
- Add API layer last - controllers are thin adapters, DTOs only
When Writing Tests
- Unit tests - domain logic, no Micronaut context
- Integration tests - use
@MicronautTest + Testcontainers or Micronaut Test Resources
- Focused tests - test jOOQ repository/client behavior with minimal required context
Quick Reference
| Scenario |
Action |
| Need a DTO |
Use Java record |
| Need domain entity |
Write explicit domain class with invariants and behavior |
| Need builder |
Write static inner Builder class |
| Need JSON mapping |
Use Jackson annotations on records |
| Need validation |
Use Jakarta Bean Validation (@NotNull, etc.) via Micronaut Validation |
| Need database |
Use jOOQ through a Micronaut-managed DSLContext |
| Need caching |
Check Micronaut Cache support before adding new lib |
| Need HTTP client |
Prefer Micronaut HTTP client before adding external client libraries |
| Need DTO to command |
Use MapStruct mapper; resolve relationships in service |
| Need domain to DTO |
Use MapStruct mapper or explicit response assembler |
| Need jOOQ record to domain |
Use an explicit persistence mapper in infrastructure |
| Need partial update |
Use @MappingTarget with IGNORE null strategy |
| Controller needs domain object or jOOQ record |
No - return DTO from service, never expose internals |
| Service modifies data |
Add @Transactional on public use-case/service method |
| Read-only query |
Use read-only transactional semantics for query use cases |
| Concurrent modifications |
Use explicit version column predicates in jOOQ updates |
| External call in transaction |
No - use outbox/event-driven patterns |
| Need Redis access |
Use Micronaut Redis integration behind infrastructure port |
Reference Files
1---2name: java-micronaut-ddd3description: Use when working on Java Micronaut applications, Micronaut HTTP APIs, jOOQ persistence, Micronaut Validation, Micronaut Security, Micronaut Messaging, DDD module boundaries, Testcontainers integration tests, MapStruct DTO mapping, transaction management, Redis adapters, or Lombok-free backend code.4---56# Java/Micronaut Development Standards78## Core Principles910### 1. Domain-Driven Design (DDD)1112Structure code **around business capabilities**, not technical layers. See [docs/ddd-patterns.md](docs/ddd-patterns.md) for patterns and examples.1314**Avoid: technical-layer slicing**1516```text17com.acme.app.controller18com.acme.app.service19com.acme.app.repository20```2122**Prefer: business modules (bounded contexts)**2324```text25com.acme.app/26+-- billing/27| +-- domain/ # Invoice, Payment, Money, BillingPolicy28| +-- application/ # IssueInvoiceUseCase, RecordPaymentUseCase29| +-- infrastructure/ # JooqInvoiceRepository, PaymentGatewayClient30| `-- api/ # BillingController, request/response DTOs31+-- catalog/32| +-- domain/33| +-- application/34| +-- infrastructure/35| `-- api/36`-- identity/37 `-- ...38```3940**Key rules:**4142- Top-level packages are **business modules** (bounded contexts)43- Technical layers (`domain/application/infrastructure/api`) are **inside** each module44- A change like "adjust invoice rules" should touch **one module** (`billing/`), not hop across layers45- Cross-module calls go through **explicit APIs** (use cases, ports, domain events)4647### 2. No Lombok4849Write explicit Java code. Do not use Lombok annotations (`@Data`, `@Getter`, `@Builder`, etc.).5051**Why:** Explicit code is debuggable, IDE-friendly, and avoids compile-time magic issues.5253**Instead of Lombok:**5455- Use Java records for immutable data carriers56- Generate getters/setters via IDE57- Write explicit builders when needed58- Use `Objects.equals()` and `Objects.hash()` for equals/hashCode5960### 3. Conservative Dependency Management6162Before adding a new library:63641. Check if functionality exists in current dependencies652. Check if Micronaut modules already provide it663. Check if Java standard library covers the use case6768**Analyze classpath first:**6970```bash71./mvnw dependency:tree72# or73./gradlew dependencies74```7576### 4. jOOQ Persistence7778Use jOOQ with Micronaut SQL for relational persistence. Do not introduce Micronaut Data, JPA, or Hibernate for persistence in this skill.7980- **Schema:** Model persistence from migrations/DDL and generated jOOQ schema types81- **Queries:** Use explicit `DSLContext` queries, joins, projections, and locking82- **Repositories:** Keep domain repository interfaces as ports; implement them with jOOQ in infrastructure83- **Mapping:** Convert between jOOQ records and domain objects at the adapter boundary84- **Transactions:** Use `@Transactional` on application service methods; use explicit version columns for optimistic locking8586See [docs/data-access.md](docs/data-access.md) for jOOQ repository and mapping patterns.87See [docs/transactions.md](docs/transactions.md) for transaction management rules.8889### 5. Testcontainers for Integration Testing9091Use Testcontainers or Micronaut Test Resources for tests requiring external dependencies (databases, message brokers, Redis, etc.).9293See [docs/testing.md](docs/testing.md) for setup and patterns.9495### 6. MapStruct for DTO Mapping9697Use MapStruct for structural conversions between API DTOs, use-case commands, and responses. Enforce strict boundaries:9899- **Controllers:** Accept/return DTOs only, never touch domain objects or jOOQ records directly100- **Services/Use cases:** Invoke mappers, resolve relationships by ID, call domain behavior101- **Mappers:** Pure structural conversion only, no repository access, no business logic102- **Domain:** Independent of DTOs, web layer, jOOQ records, and SQL details103104See [docs/mapping.md](docs/mapping.md) for patterns and examples.105106### 7. Micronaut Redis Access107108Use Micronaut Redis module according to official docs: https://micronaut-projects.github.io/micronaut-redis/6.9.0/guide/109110- Prefer Micronaut-provided Redis clients/configuration before introducing custom wrappers111- Keep Redis usage behind ports/adapters in the infrastructure layer112- Use serialization and key naming conventions explicitly113- For tests that depend on Redis, use Testcontainers instead of shared local instances114115See [docs/redis-access.md](docs/redis-access.md) for usage guidance.116117---118119## Workflow120121### Before Writing Code1221231. **Analyze existing dependencies:**124125 ```bash126 ./mvnw dependency:tree | grep -E "(micronaut-jooq|jooq|micronaut-data|hibernate|jakarta.persistence|micronaut-validation|micronaut-redis|lombok|mapstruct|test)"127 ```1281292. **Verify jOOQ stack:** Ensure `micronaut-jooq`, jOOQ code generation, a `DataSource`, and migrations are configured.1301313. **Check for forbidden persistence stack:** If Micronaut Data, JPA, or Hibernate are present, discuss removal or isolation before adding persistence code.1321334. **Check for MapStruct:** If not present and DTOs are needed, add `mapstruct` + `mapstruct-processor`.1341355. **Check for Lombok:** If `lombok` is in dependencies, discuss removal strategy with user before proceeding.136137### When Implementing Features1381391. **Start with domain model** - domain entities and value objects1402. **Define repository interfaces (ports)** - in domain/application layer1413. **Create DTOs** - request/response records per use case1424. **Create MapStruct mappers** - pure DTO/command/response conversion1435. **Implement application services/use cases** - orchestrate domain operations and transactions1446. **Implement infrastructure adapters** - jOOQ repositories, Redis adapters, external clients1457. **Add API layer last** - controllers are thin adapters, DTOs only146147### When Writing Tests1481491. **Unit tests** - domain logic, no Micronaut context1502. **Integration tests** - use `@MicronautTest` + Testcontainers or Micronaut Test Resources1513. **Focused tests** - test jOOQ repository/client behavior with minimal required context152153---154155## Quick Reference156157| Scenario | Action |158|----------|--------|159| Need a DTO | Use Java record |160| Need domain entity | Write explicit domain class with invariants and behavior |161| Need builder | Write static inner Builder class |162| Need JSON mapping | Use Jackson annotations on records |163| Need validation | Use Jakarta Bean Validation (`@NotNull`, etc.) via Micronaut Validation |164| Need database | Use jOOQ through a Micronaut-managed `DSLContext` |165| Need caching | Check Micronaut Cache support before adding new lib |166| Need HTTP client | Prefer Micronaut HTTP client before adding external client libraries |167| Need DTO to command | Use MapStruct mapper; resolve relationships in service |168| Need domain to DTO | Use MapStruct mapper or explicit response assembler |169| Need jOOQ record to domain | Use an explicit persistence mapper in infrastructure |170| Need partial update | Use `@MappingTarget` with `IGNORE` null strategy |171| Controller needs domain object or jOOQ record | No - return DTO from service, never expose internals |172| Service modifies data | Add `@Transactional` on public use-case/service method |173| Read-only query | Use read-only transactional semantics for query use cases |174| Concurrent modifications | Use explicit `version` column predicates in jOOQ updates |175| External call in transaction | No - use outbox/event-driven patterns |176| Need Redis access | Use Micronaut Redis integration behind infrastructure port |177178---179180## Reference Files181182- [DDD Patterns](docs/ddd-patterns.md) - Aggregates, entities, value objects, domain events183- [Data Access](docs/data-access.md) - jOOQ repositories, generated schema types, explicit SQL184- [Transactions](docs/transactions.md) - transaction boundaries, propagation, locking185- [Testing](docs/testing.md) - Testcontainers setup and integration test patterns186- [Mapping](docs/mapping.md) - MapStruct patterns, layer boundaries, DTO design187- [Redis Access](docs/redis-access.md) - Micronaut Redis integration and adapter patterns188- Micronaut guide - https://docs.micronaut.io/4.10.21/guide/189- Micronaut SQL/jOOQ guide - https://micronaut-projects.github.io/micronaut-sql/latest/guide/190- Micronaut Redis guide - https://micronaut-projects.github.io/micronaut-redis/6.9.0/guide/