Domain-driven design (DDD) is an approach to building software where the structure of the code reflects how the business actually works. Teams define a ubiquitous language, shared terms for domain concepts, and organize the system into bounded contexts that own their models.
Why it matters
When code, conversations, and documentation use different words for the same idea, integration breaks silently. DDD pushes ambiguity to explicit boundaries instead of hiding it in a single shared model that tries to mean everything at once.
How DDD fits together
DDD starts with the business domain, turns conversations into a shared language, then draws boundaries where models need to stay internally consistent. Those contexts integrate through explicit contracts instead of sharing one global model.
flowchart TB Domain["Business domain"] --> Language["Ubiquitous language"] Experts["Domain experts"] --> Language Developers["Developers"] --> Language Language --> ContextMap["Context map"] ContextMap --> SalesContext ContextMap --> BillingContext ContextMap --> ShippingContext subgraph SalesContext["Sales bounded context"] SalesOrder["Order aggregate"] SalesCustomer["Customer = buyer"] SalesOrder --> OrderPlaced["OrderPlaced domain event"] end subgraph BillingContext["Billing bounded context"] BillingCustomer["Customer = payer"] Invoice["Invoice aggregate"] end subgraph ShippingContext["Shipping bounded context"] ShippingCustomer["Customer = recipient"] Shipment["Shipment aggregate"] end OrderPlaced -->|"published language"| BillingAcl["Anti-corruption layer"] BillingAcl --> Invoice OrderPlaced -->|"integration event"| Shipment
Core ideas
- Ubiquitous language, developers and domain experts use the same vocabulary in meetings, docs, and code.
- Bounded context, a boundary where a model is consistent; the same term can mean different things in different contexts.
- Context map, a diagram of how contexts relate (upstream/downstream, shared kernel, anti-corruption layer).
- Aggregates, consistency boundaries around clusters of entities that change together.
- Domain events, record meaningful state changes for integration without tight coupling.
Strategic vs tactical
Strategic design decides where to draw boundaries, how teams align, and how contexts integrate. Tactical patterns (entities, value objects, repositories, domain services) implement behavior inside a context.
Start strategic: a context map and clear ownership beat premature entity modeling across the whole company.
Integration with event-driven systems
Contexts often integrate through domain events and asynchronous messaging. Long-running cross-context workflows may use the saga pattern when a single transaction cannot span services.
Trade-offs
- Upfront modeling conversations take time before features ship.
- Multiple contexts mean more integration work than one shared database.
- DDD pays off when the domain is complex and long-lived; simple CRUD apps may not need the full toolkit.
