Enforces DDD tactical patterns including aggregates, value objects, domain events, repositories, and bounded context boundaries for complex domain modeling.
## Domain-Driven Design Rules
### Bounded Contexts
- Each bounded context has its own ubiquitous language — the same word can mean different things in different contexts
- Map context boundaries explicitly: define what each context owns and what it imports
- Contexts communicate via published events or anti-corruption layers — never share domain models directly
- A bounded context maps to one deployable unit (module, service, package) — do not mix contexts in one file
- Define a context map document: customer-supplier, conformist, or partnership relationships
### Entities
- Entities have a unique identity that persists across state changes: `UserId`, `OrderId`
- Define identity as a dedicated value object — never use raw primitives for IDs in domain code
- Entity equality is based on identity, not attribute values
- Entities protect their invariants: all state-changing methods validate business rules before mutating
- Entities must not have public setters — expose intentional methods: `user.activate()`, not `user.status = 'active'`
- Entities are always valid — no partially constructed entities; use factory methods or builders
### Value Objects
- Value objects have no identity — equality is based on all their attributes
- Value objects are immutable — create a new instance instead of mutating: `address.moveTo(newCity)`
- Use value objects for: money, dates, coordinates, email addresses, phone numbers, measurement units
- Value objects encapsulate validation: `Email.parse("invalid")` throws or returns an error — never expose a raw string
- Value objects can have behavior: `Money.add(other: Money): Money`
### Aggregates
- An aggregate is a cluster of entities and value objects with a single aggregate root
- All access to the aggregate internals must go through the aggregate root — never directly mutate child entities
- Each aggregate root has a globally unique identity
- Aggregates enforce consistency boundaries — all invariants within an aggregate are enforced synchronously
- Keep aggregates small — an aggregate should fit in a single transaction
- Reference other aggregates by identity only, not by object reference
- Design aggregates around consistency requirements, not data ownership
### Domain Events
- A domain event is an immutable record of something significant that happened in the domain
- Name events in past tense: `UserRegistered`, `OrderShipped`, `PaymentFailed`
- Events carry all context needed to react: timestamp, aggregate ID, relevant attributes
- Aggregate roots collect domain events during a command; they are dispatched after the transaction commits
- Do not perform side effects inside the aggregate when raising events — let handlers do that
- Use domain events to communicate between bounded contexts asynchronously
### Repositories
- Repositories provide collection-like access to aggregates — they hide persistence details
- Define repository interfaces in the domain layer; implement them in the infrastructure layer
- Repository methods: `findById(id)`, `save(aggregate)`, `findAll()` — no CRUD for individual fields
- Never return partial aggregates from a repository — always return a fully reconstructed aggregate root
- Repositories work with aggregates, not entities or value objects below the root
### Domain Services
- Use a domain service for logic that does not naturally belong to any single entity or aggregate
- Domain services are stateless and operate on domain objects passed as arguments
- Name domain services after domain activities: `TransferFundsService`, `PricingCalculator`
- Do not confuse domain services with application services — domain services have no infrastructure concerns
### Application Services
- Application services orchestrate use cases: load aggregate, call domain logic, save aggregate, publish events
- Application services are the transaction boundary — one use case, one transaction
- Application services translate between DTOs (API layer) and domain objects
- Application services must not contain business logic — delegate to domain objects and domain services
### Ubiquitous Language
- Use domain terminology exactly in code: class names, method names, variable names
- If the domain expert would not recognize the name, rename it
- Build a glossary with the domain expert; terms in the glossary must appear verbatim in code
- Refactoring the language in the domain is worth the churn — consistency pays off
### Anti-patterns
- Anemic domain model: entities with only getters/setters and no behavior — violates encapsulation
- Large aggregates that span multiple consistency boundaries — leads to contention and locks
- Repositories that return DTOs or raw data — the domain layer must work with domain objects
- Leaking infrastructure concerns (database column names, JSON field names) into the domain model