| name | java-domain-driven-design |
|---|---|
| description | Design and implement Java systems with Domain-Driven Design, including bounded contexts, aggregates, entities, value objects, repositories, domain services, application services, domain events, anti-corruption layers, shared kernel, published contracts, query-side models, sagas, and modular package or multi-module structure. Use this skill whenever the user asks for Java backend architecture or coding that should reflect DDD, clean architecture, rich domain models, modular monolith design, Spring Boot service design, aggregate modeling, CQRS-style read models, cross-context integration, or domain-focused refactoring, even if the user does not explicitly say "DDD". |
Design code around domain language and business invariants first, then fit frameworks and persistence around that model. Default to Java 21, Spring Boot 3, Maven, and JPA when the user does not specify a stack, but keep the domain layer independent from Spring and persistence concerns.
Follow this sequence unless the user asks for only one step:
- Identify the core domain language.
- Separate bounded contexts before writing classes.
- Model aggregates and invariants before repositories or controllers.
- Implement application services as orchestration only.
- Adapt infrastructure to the domain instead of leaking infrastructure into the domain.
- Return a concrete code structure, not only theory.
Before coding, extract:
- Primary business capability
- Key actors and use cases
- Ubiquitous language terms
- Invariants that must always hold
- Commands that change state
- Queries that read state
- External systems or integration points
If the request is underspecified, state the assumptions explicitly and continue. Do not block on perfect requirements.
Use bounded contexts when the domain has distinct language or rules. Do not force one giant model.
For each context, name:
- Responsibility
- Upstream and downstream dependencies
- Aggregate roots
- Domain events it publishes or consumes
- Public application service entry points
If the user asks for a small feature, still say which bounded context owns it.
When one bounded context or external system exposes a model that would pollute local domain language, introduce an Anti-Corruption Layer instead of reusing upstream terms directly.
Read references/tactical-patterns.md when choosing between entity, value object, aggregate, domain service, repository, factory, and domain event.
Apply these defaults:
- Prefer value objects for concepts defined by attributes and validation.
- Put business invariants inside the aggregate root.
- Allow external state changes only through aggregate methods with intention-revealing names.
- Use repositories only for aggregate roots.
- Use domain services only when behavior does not belong naturally to one aggregate or value object.
- Use application services to coordinate transactions, authorization, integrations, and cross-aggregate workflows.
- Raise domain events for meaningful business facts, not CRUD notifications.
Read references/java-ddd-layout.md when creating packages or modules. Read references/shared-kernel.md when deciding whether a concept belongs in a shared base module. Read references/anti-corruption-layer.md when integrating with another bounded context, legacy system, or third-party service. Read references/assemblers.md when deciding where type conversion belongs. Read references/build-structures.md when choosing Maven or Gradle multi-module layout. Read references/contracts-and-published-language.md when defining shared schemas, integration events, or public APIs across contexts. Read references/query-side.md when handling read models, reporting, or search-heavy queries. Read references/consistency-and-sagas.md when designing transactions, eventual consistency, or long-running workflows. Read references/testing-strategy.md when deciding how to test domain, application, integration, ACL, and assembler code. Read references/error-handling.md when defining exception boundaries and failure models. Read references/output-template.md when producing architecture or implementation output for the user.
Enforce these dependency rules:
domaindepends on nothing fromapplication,infrastructure, or Spring.applicationdepends ondomainonly.infrastructuredepends onapplicationanddomain.interfacesorwebdepends onapplication, never directly on repositories or entities for write workflows.- Persistence annotations in domain classes are acceptable only if the team already uses that style and the user prefers it. Otherwise keep ORM mapping in infrastructure adapters.
For medium or large systems, prefer a multi-module build with one module for shared-kernel, one module per bounded context, and one bootstrap module that wires the runtime application.
If the solution uses a shared-kernel module, keep it thin and stable. Only place abstractions there when they are framework-independent and truly shared by multiple bounded contexts.
If the solution uses an Anti-Corruption Layer:
- Place translation logic at the boundary, usually in
infrastructureor a dedicated integration package. - Translate upstream DTOs, events, and error semantics into local commands, value objects, and domain concepts.
- Do not leak foreign names, enums, status codes, or persistence models into the local domain.
- Keep ACL orchestration separate from the core aggregate logic.
- Prefer local snapshots or local value objects when another bounded context exposes data that is needed but not owned locally.
If the solution uses assemblers:
- Use them for pure type conversion at boundaries.
- Keep them close to the boundary they serve, such as
interfaces,application,persistence, oracl. - Do not put business decisions, invariants, transaction control, or workflow orchestration in assemblers.
- Allow ACL code to use dedicated translators or assemblers, but keep the boundary semantics owned by the ACL.
If the solution shares contracts across contexts:
- Keep published schemas, integration events, or API contracts separate from the domain model.
- Do not treat published language contracts as equivalent to local aggregates or entities.
- Avoid putting context-specific integration DTOs into
shared-kernel.
If the solution includes read-heavy features:
- Separate write-side aggregate modeling from query-side projections or read models.
- Do not force dashboards, search responses, or reports through aggregate repositories when a read model is clearer.
- Keep query models optimized for read use cases, not for enforcing write invariants.
If the solution spans multiple aggregates or contexts in one business process:
- Keep a single transaction inside a single aggregate boundary whenever possible.
- Use domain events, process managers, or sagas for long-running or cross-context workflows.
- Do not solve cross-context consistency with direct infrastructure coupling.
When generating code, prefer:
- Immutable value objects
- Rich domain methods over anemic setter-based models
- Constructor or factory validation for invariants
- Explicit repository interfaces in the domain or application boundary
- Separate command and query DTOs at the interface layer
- Transaction boundaries in application services
- Domain-specific exceptions or result types instead of generic
RuntimeException
Use record for small immutable value objects when it keeps the model clearer. Use classes when richer behavior or custom constructors are needed.
Prefer clear failure boundaries:
- domain failures for invariant violations
- application failures for use-case orchestration and policy failures
- integration failures for remote or infrastructure problems
- interface-layer mapping from internal failures to API responses
If Spring Boot is used:
- Keep
@RestController,@Service,@Repository, JPA entities, and messaging adapters outside the core domain model. - Map HTTP DTOs to commands at the boundary.
- Inject repository ports into application services, not controllers.
- Avoid putting business rules in controllers, mappers, or JPA callbacks.
- Keep transactional orchestration in the application layer.
When asked to design or code, provide the most useful artifact for the request:
- For architecture requests: bounded context map, package structure, aggregate definitions, and dependency rules
- For feature requests: command flow, domain model changes, application service, repository port, and adapter sketch
- For coding requests: compilable Java classes with concise comments only where necessary
- For refactoring requests: target design, migration steps, and the first safe code slice to implement
Prefer concrete class and package names over abstract advice.
For substantial design or coding responses, default to this structure:
- Assumptions
- Bounded contexts
- Aggregates and invariants
- Module or package layout
- Commands, queries, events, and contracts
- Key classes or code
- Consistency notes, ACL points, and extension points
Before finishing, verify:
- The ubiquitous language is reflected in class, method, and package names.
- Each aggregate has a clear consistency boundary.
- Business invariants are enforced in domain code.
- Controllers and adapters contain no core business decisions.
- Repositories are defined around aggregate access, not arbitrary tables.
- Cross-context interactions are explicit.
- ACL boundaries are explicit anywhere foreign models would otherwise leak into the domain.
- Published contracts are separated from local domain models.
- Query-side models are separated from write-side aggregates where appropriate.
- Transaction boundaries and eventual consistency strategy are explicit.
- Failure types are mapped at the correct layer.
- The response includes enough code or structure that Claude/Codex can continue implementation immediately.
- Use references/tactical-patterns.md for modeling decisions.
- Use references/java-ddd-layout.md for package layout, module dependencies, and Spring adapter placement.
- Use references/ddd-checklist.md as a final review pass before delivering architecture or code.
- Use references/shared-kernel.md before introducing a shared base module or common domain abstractions.
- Use references/anti-corruption-layer.md when integrating across bounded contexts or legacy systems.
- Use references/assemblers.md when defining DTO, command, persistence, or ACL translation components.
- Use references/build-structures.md when proposing Maven or Gradle project structure.
- Use references/contracts-and-published-language.md when designing integration schemas or shared contracts.
- Use references/query-side.md when designing read models and query flows.
- Use references/consistency-and-sagas.md when designing transactions, eventual consistency, or process managers.
- Use references/testing-strategy.md when deciding what to test at each layer.
- Use references/error-handling.md when shaping domain, application, integration, and HTTP/API failures.
- Use references/output-template.md to keep design and code responses consistent and implementation-ready.