| name | principle-ddd |
| description | Domain-Driven Design (DDD) — bounded contexts, aggregates, entities, value objects, ubiquitous language, domain events, context map, anti-corruption layer, and repository pattern. Auto-load when modeling a complex domain, splitting services, deciding service boundaries, microservice splits, designing aggregates, or aligning code with business language. |
Domain-Driven Design
DDD is a toolkit for complex domains. For CRUD, it is overkill.
Strategic design
Ubiquitous language
Every important concept in code uses the same word the domain experts use. If marketing says "subscription" and code says "userPlan", you have a translation tax forever. Rename.
Bounded contexts
A bounded context is a boundary inside which a term has exactly one meaning. "Order" in Checkout is not the same entity as "Order" in Fulfillment — it just shares a name.
- One team, one context is the ideal.
- Cross-context communication goes through explicit contracts (events, APIs).
- Shared databases across contexts are an anti-pattern.
Context map
Document how contexts relate: Partnership, Customer/Supplier, Conformist, Anticorruption Layer, Published Language, Shared Kernel, Separate Ways. Pick the relationship intentionally.
Tactical design
Entity
Identity persists through change. User{id, name} — renaming doesn't change the user.
Entities own their behaviour — tell, don't ask. Logic that enforces an entity's invariants belongs on the entity, not in a service that reads its fields and mutates them from outside. An entity that is pure data with all logic elsewhere is an anemic domain model — an anti-pattern. (See principle-solid § "If You Catch Yourself Thinking…" for the reflection-table form.)
Value object
Identity-less, immutable, compared by value. Money{amount, currency}, EmailAddress, DateRange. Prefer aggressively — eliminates primitive obsession bugs.
Aggregate
A cluster of entities and value objects treated as one consistency boundary. The aggregate root is the only entry point; outside code cannot hold references to internal entities.
- Keep aggregates small — large ones cause contention.
- One transaction, one aggregate. Cross-aggregate coordination happens via domain events.
Repository
Collection-like interface for loading and persisting aggregates. Port in domain layer; implementation in infrastructure.
Domain event
Something meaningful that happened. OrderPlaced, PaymentFailed. Emit from aggregates; handle in application services or other contexts.
Domain service
Stateless domain logic that does not belong on a single entity (e.g., a transfer between two accounts).
When DDD is overkill
- Simple CRUD apps.
- Technical utilities (a scheduler, a logger).
- Prototypes where the domain is not yet understood — premature DDD freezes the wrong model.
Signals you need DDD
- Multiple teams colliding in one codebase.
- Business rules hidden inside controllers or stored procedures.
- Experts and developers using different words for the same thing.
- Transactions spanning unrelated data.
Signs you specifically need Full DDD
- Multiple entities must change atomically (→ aggregates + unit of work).
- Business events trigger cascading actions across the domain (→ domain events).
- Different teams own different parts of the domain (→ bounded contexts + context map).
Pattern Selection Quick Reference
| Complexity | Domain patterns | Repository pattern | Example |
|---|
| No business rules | Plain DTOs | Direct data access | Settings CRUD, preferences |
| Simple rules | Entities with methods, value objects | Interface in domain layer | Price alerts with threshold logic |
| Complex invariants | Aggregates, domain events, specifications | Interface + unit of work | Portfolio rebalancing with multi-asset constraints |
Light DDD is the floor when business rules exist: entity behavior + repository interfaces + value objects for constrained types are non-negotiable even for "simple" cases.
See examples/ for a worked Light DDD Order-aggregate implementation (Money value object, aggregate root enforcing one invariant, repository port) in C#, Go, Java, Kotlin, Python, Ruby, Rust, Swift, and TypeScript (read on demand — not auto-loaded).