Java/Spring 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/# JpaInvoiceRepository, 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 Spring Boot starters already provide it
- Check if Java standard library covers the use case
Analyze classpath first:
./mvnw dependency:tree
# or
./gradlew dependencies
4. Spring Data JPA
Use Spring Data JPA with Hibernate for persistence. Key practices:
- Entities: Explicit getters,
@Version for optimistic locking, protected no-arg constructor
- Fetching: Avoid N+1 with
JOIN FETCH, @EntityGraph, or DTO projections
- Transactions:
@Transactional on service methods, readOnly = true for queries
See docs/data-access.md for entity mapping and repository patterns.
See docs/transactions.md for transaction management rules.
5. Testcontainers for Integration Testing
Use Testcontainers for any test requiring external dependencies (databases, message brokers, etc.).
See docs/testing.md for setup and patterns.
6. MapStruct for Entity/DTO Mapping
Use MapStruct for all structural conversions between layers. Enforce strict boundaries:
- Controllers: Accept/return DTOs only, never touch entities
- Services: Invoke mappers, resolve relationships by ID, call domain behavior
- Mappers: Pure structural conversion only, no repository access, no business logic
- Domain: Independent of DTOs and web layer
See docs/mapping.md for patterns and examples.
Workflow
Before Writing Code
Analyze existing dependencies:
./mvnw dependency:tree | grep -E "(spring-data|lombok|mapstruct|test)"
Verify Spring Data JPA: Ensure spring-boot-starter-data-jpa is present.
Check for MapStruct: If not present and DTOs needed, add mapstruct + mapstruct-processor.
Check for Lombok: If lombok in dependencies, discuss removal strategy with user before proceeding.
When Implementing Features
- Start with domain model — entities and value objects
- Define repository interfaces — in domain layer
- Create DTOs — request/response records per use case
- Create MapStruct mappers — pure structural conversion
- Implement application services — orchestrate domain operations, invoke mappers
- Add API layer last — controllers are thin adapters, DTOs only
When Writing Tests
- Unit tests — domain logic, no Spring context
- Integration tests — use
@SpringBootTest + Testcontainers
- Slice tests —
@DataJpaTest or @WebMvcTest for focused testing
Quick Reference
| Scenario |
Action |
| Need a DTO |
Use Java record |
| Need entity |
Write class with explicit getters, equals/hashCode |
| Need builder |
Write static inner Builder class |
| Need JSON mapping |
Use Jackson annotations on records |
| Need validation |
Use Jakarta Bean Validation (@NotNull, etc.) |
| Need database |
Use Spring Data JPA repository |
| Need caching |
Check if spring-boot-starter-cache already present |
| Need HTTP client |
Check for WebClient or RestClient before adding new lib |
| Need DTO→Entity |
Use MapStruct mapper, resolve relationships in service |
| Need Entity→DTO |
Use MapStruct mapper, ensure graph is fetched first |
| Need partial update |
Use @MappingTarget with IGNORE null strategy |
| Controller needs entity |
NO — return DTO from service, never expose entities |
| Service modifies data |
Add @Transactional on public method |
| Read-only query |
Add @Transactional(readOnly = true) |
| Concurrent modifications |
Use @Version for optimistic locking |
| External call in transaction |
NO — use transactional outbox pattern |
Reference Files
- DDD Patterns — Aggregates, entities, value objects, domain events
- Data Access — Spring Data JPA entities, repositories, fetching strategies
- Transactions — JPA transaction management, propagation, locking
- Testing — Testcontainers setup and integration test patterns
- Mapping — MapStruct patterns, layer boundaries, DTO design
1---2name: java-spring-ddd3description: Use when working on Java Spring Boot or Spring Framework applications, Spring MVC, Spring WebFlux, Spring Data JPA, DDD module boundaries, Testcontainers integration tests, MapStruct DTO mapping, transaction management, caching, HTTP clients, or Lombok-free backend code.4---56# Java/Spring Development Standards78## Core Principles910### 1. Domain-Driven Design (DDD)11Structure code **around business capabilities**, not technical layers. See [docs/ddd-patterns.md](docs/ddd-patterns.md) for patterns and examples.1213**Avoid: technical-layer slicing**14```15com.acme.app.controller16com.acme.app.service17com.acme.app.repository18```1920**Prefer: business modules (bounded contexts)**21```22com.acme.app/23├── billing/24│ ├── domain/ # Invoice, Payment, Money, BillingPolicy25│ ├── application/ # IssueInvoiceUseCase, RecordPaymentUseCase26│ ├── infrastructure/# JpaInvoiceRepository, PaymentGatewayClient27│ └── api/ # BillingController, request/response DTOs28├── catalog/29│ ├── domain/30│ ├── application/31│ ├── infrastructure/32│ └── api/33└── identity/34 └── ...35```3637**Key rules:**38- Top-level packages are **business modules** (bounded contexts)39- Technical layers (`domain/application/infrastructure/api`) are **inside** each module40- A change like "adjust invoice rules" should touch **one module** (`billing/`), not hop across layers41- Cross-module calls go through **explicit APIs** (use cases, ports, domain events)4243### 2. No Lombok44Write explicit Java code. Do not use Lombok annotations (`@Data`, `@Getter`, `@Builder`, etc.).4546**Why:** Explicit code is debuggable, IDE-friendly, and avoids compile-time magic issues.4748**Instead of Lombok:**49- Use Java records for immutable data carriers50- Generate getters/setters via IDE51- Write explicit builders when needed52- Use `Objects.equals()` and `Objects.hash()` for equals/hashCode5354### 3. Conservative Dependency Management55Before adding a new library:56571. Check if functionality exists in current dependencies582. Check if Spring Boot starters already provide it593. Check if Java standard library covers the use case6061**Analyze classpath first:**62```bash63./mvnw dependency:tree64# or65./gradlew dependencies66```6768### 4. Spring Data JPA69Use Spring Data JPA with Hibernate for persistence. Key practices:7071- **Entities:** Explicit getters, `@Version` for optimistic locking, protected no-arg constructor72- **Fetching:** Avoid N+1 with `JOIN FETCH`, `@EntityGraph`, or DTO projections73- **Transactions:** `@Transactional` on service methods, `readOnly = true` for queries7475See [docs/data-access.md](docs/data-access.md) for entity mapping and repository patterns.76See [docs/transactions.md](docs/transactions.md) for transaction management rules.7778### 5. Testcontainers for Integration Testing79Use Testcontainers for any test requiring external dependencies (databases, message brokers, etc.).8081See [docs/testing.md](docs/testing.md) for setup and patterns.8283### 6. MapStruct for Entity/DTO Mapping84Use MapStruct for all structural conversions between layers. Enforce strict boundaries:8586- **Controllers**: Accept/return DTOs only, never touch entities87- **Services**: Invoke mappers, resolve relationships by ID, call domain behavior88- **Mappers**: Pure structural conversion only, no repository access, no business logic89- **Domain**: Independent of DTOs and web layer9091See [docs/mapping.md](docs/mapping.md) for patterns and examples.9293---9495## Workflow9697### Before Writing Code98991. **Analyze existing dependencies:**100 ```bash101 ./mvnw dependency:tree | grep -E "(spring-data|lombok|mapstruct|test)"102 ```1031042. **Verify Spring Data JPA:** Ensure `spring-boot-starter-data-jpa` is present.1051063. **Check for MapStruct:** If not present and DTOs needed, add `mapstruct` + `mapstruct-processor`.1071084. **Check for Lombok:** If `lombok` in dependencies, discuss removal strategy with user before proceeding.109110### When Implementing Features1111121. **Start with domain model** — entities and value objects1132. **Define repository interfaces** — in domain layer1143. **Create DTOs** — request/response records per use case1154. **Create MapStruct mappers** — pure structural conversion1165. **Implement application services** — orchestrate domain operations, invoke mappers1176. **Add API layer last** — controllers are thin adapters, DTOs only118119### When Writing Tests1201211. **Unit tests** — domain logic, no Spring context1222. **Integration tests** — use `@SpringBootTest` + Testcontainers1233. **Slice tests** — `@DataJpaTest` or `@WebMvcTest` for focused testing124125---126127## Quick Reference128129| Scenario | Action |130|----------|--------|131| Need a DTO | Use Java record |132| Need entity | Write class with explicit getters, equals/hashCode |133| Need builder | Write static inner Builder class |134| Need JSON mapping | Use Jackson annotations on records |135| Need validation | Use Jakarta Bean Validation (`@NotNull`, etc.) |136| Need database | Use Spring Data JPA repository |137| Need caching | Check if `spring-boot-starter-cache` already present |138| Need HTTP client | Check for WebClient or RestClient before adding new lib |139| Need DTO→Entity | Use MapStruct mapper, resolve relationships in service |140| Need Entity→DTO | Use MapStruct mapper, ensure graph is fetched first |141| Need partial update | Use `@MappingTarget` with `IGNORE` null strategy |142| Controller needs entity | NO — return DTO from service, never expose entities |143| Service modifies data | Add `@Transactional` on public method |144| Read-only query | Add `@Transactional(readOnly = true)` |145| Concurrent modifications | Use `@Version` for optimistic locking |146| External call in transaction | NO — use transactional outbox pattern |147148---149150## Reference Files151152- [DDD Patterns](docs/ddd-patterns.md) — Aggregates, entities, value objects, domain events153- [Data Access](docs/data-access.md) — Spring Data JPA entities, repositories, fetching strategies154- [Transactions](docs/transactions.md) — JPA transaction management, propagation, locking155- [Testing](docs/testing.md) — Testcontainers setup and integration test patterns156- [Mapping](docs/mapping.md) — MapStruct patterns, layer boundaries, DTO design