Domain-driven design (DDD)
Domain-driven design models software around business domains using ubiquitous language, bounded contexts and aggregates. Learn its core concepts.
Overview
Domain-driven design, usually shortened to DDD, is an approach to building software for complex business areas. Its central idea is that the structure and language of the code should match the way experts in the business think and talk about their work. Developers and domain experts build a shared model together, name things the same way, and draw clear boundaries around areas where the model holds. The code then expresses that model directly.
An everyday analogy is the word "bank" on a river walk and at a high-street branch. Within each setting everyone knows what it means, but if a single dictionary tried to cover both without context, conversations would go wrong. DDD accepts that a large organisation has many such settings. "Customer" means one thing to sales, another to billing and another to support. Rather than forcing one giant definition, DDD gives each area its own model and its own vocabulary, and defines how the areas translate between each other.
DDD matters well beyond classic business software. It is the most common method for deciding where to split microservices, and its ideas about boundaries, events and consistency underpin patterns such as CQRS, event sourcing and the saga pattern. For data and AI platforms it also helps decide who owns which data, a question at the heart of data mesh.
What it is
DDD has two levels: strategic design, about the big picture and boundaries, and tactical design, about building blocks inside one boundary.
| Concept | Level | What it is |
|---|---|---|
| Domain | Strategic | The business area the software serves, such as insurance claims or logistics |
| Subdomain | Strategic | A part of the domain: core (competitive advantage), supporting or generic |
| Ubiquitous language | Strategic | The shared vocabulary used by experts and in the code, within one context |
| Bounded context | Strategic | A boundary within which one model and language apply consistently |
| Context map | Strategic | A description of how bounded contexts relate and integrate |
| Entity | Tactical | An object defined by identity that persists over time, such as an order |
| Value object | Tactical | An object defined only by its values, immutable, such as money or an address |
| Aggregate | Tactical | A cluster of entities and values treated as one unit for changes, with one root |
| Domain event | Tactical | A record that something meaningful happened, such as PaymentReceived |
| Repository | Tactical | An abstraction for loading and saving aggregates |
| Domain service | Tactical | Domain logic that does not belong to one entity |
Strategic design usually brings the biggest benefit. Tactical patterns help when the business rules inside a context are genuinely complex.
Where it came from
Eric Evans introduced domain-driven design in his 2003 book "Domain-Driven Design: Tackling Complexity in the Heart of Software", often called the "blue book". It drew on object-oriented design and on Evans's experience modelling complex domains with experts.
Vaughn Vernon's "Implementing Domain-Driven Design" (2013), the "red book", gave practical guidance on applying the patterns, and his shorter "Domain-Driven Design Distilled" followed in 2016. Alberto Brandolini developed Event Storming, a workshop technique for discovering domain events and boundaries with sticky notes, which became a popular way to start DDD work. Interest grew sharply with microservices from the mid-2010s, because bounded contexts offered a principled way to find service boundaries.
How it works
1. Talk with domain experts
Developers work directly with the people who know the business, in workshops and ongoing conversation. The goal is to understand processes, rules and exceptions in the experts' own words.
2. Distil the subdomains
The team separates the core subdomain, where the business differentiates itself and deserves the best modelling, from supporting and generic subdomains, which can often use simpler designs or bought products.
3. Draw bounded contexts
Each context gets its own model and language. In a shipping company, "Booking", "Routing" and "Billing" may each be a context. The word "shipment" can mean slightly different things in each, and that is allowed.
4. Map relationships
A context map shows how contexts integrate. Patterns include a shared kernel, customer-supplier relationships, a conformist downstream, and an anti-corruption layer that translates a foreign model so it does not leak in.
5. Model inside each context
Within one context, the team uses entities, value objects and aggregates. Each aggregate protects its own rules: all changes go through the aggregate root, and one transaction changes one aggregate.
6. Communicate through events
When an aggregate changes, it can publish domain events. Other aggregates or contexts react to them, which keeps boundaries loose and supports eventual consistency.
7. Refine continuously
The model is never finished. When experts use a new term or the code becomes awkward, the team updates the model and the code together.
Key concepts and terms
| Term | Meaning |
|---|---|
| Ubiquitous language | Shared vocabulary used consistently in talk, documents and code within a context. |
| Bounded context | The boundary where a particular model is valid. |
| Core domain | The part of the business that provides competitive advantage. |
| Context map | A diagram and description of relationships between contexts. |
| Anti-corruption layer | A translation layer that protects a model from another system's concepts. |
| Aggregate | A consistency boundary of related objects changed as a unit. |
| Aggregate root | The single entry point entity of an aggregate. |
| Entity | An object with a lasting identity. |
| Value object | An immutable object compared by its values. |
| Domain event | A fact about something that happened in the domain. |
| Invariant | A rule that must always hold, such as "order total equals sum of lines". |
| Event Storming | A collaborative workshop for discovering events and boundaries. |
A worked example
This scenario is illustrative. A regional insurer is rebuilding its claims system. The old system has one Customer table with 140 columns used by sales, underwriting, claims and finance. Every change breaks something.
The team runs a two-day Event Storming workshop with claims handlers, underwriters and finance staff. Orange sticky notes list events such as "Claim Submitted", "Claim Assessed", "Payment Approved" and "Policy Renewed". Clusters emerge, and the group agrees on four bounded contexts: Policy, Claims, Payments and Customer Contact.
In the Claims context, the experts never say "customer"; they say "claimant", and a claimant may not even be the policyholder. The team adopts that word. A Claim aggregate holds claim lines and an assessment, enforcing invariants such as "approved amount cannot exceed the policy limit of 50,000". Money is a value object with amount and currency. When a claim is approved, the aggregate publishes ClaimApproved. The Payments context subscribes and creates a payment instruction in its own model. An anti-corruption layer translates data from the legacy policy system into the Claims model so the old 140-column structure does not leak in.
Claims becomes the first service rebuilt, because it is the core domain where faster, fairer handling matters most. Customer Contact is handled by an off-the-shelf CRM, since it is generic.
Strengths and limitations
Strengths:
- Aligns code with how the business works, so changes are easier to reason about.
- Shared language reduces misunderstandings between experts and developers.
- Bounded contexts give principled boundaries for teams, services and data ownership.
- Focuses design effort on the core domain where it pays off.
- Domain events support loosely coupled, event-driven systems.
Limitations:
- Needs real access to domain experts, which many organisations struggle to provide.
- Tactical patterns add overhead for simple CRUD applications.
- Terminology is abstract and often misapplied.
- Getting boundaries wrong early can be costly, so iteration is needed.
- It is a way of thinking, not a framework, so results depend heavily on the team.
How it compares with alternatives
| Approach | Focus | When it fits better |
|---|---|---|
| Data-centric design | Start from database tables and build screens on top | Simple record-keeping applications |
| Transaction script | Procedures that handle each request step by step | Small domains with little shared logic |
| Hexagonal architecture | Isolating domain logic from infrastructure | Complements DDD inside a bounded context |
| Microservices | Deployment and team structure | Often guided by DDD contexts, but not a modelling method |
| Data mesh | Domain-oriented ownership of analytical data | Applies DDD-style ownership to data platforms |
Practical guidance and best practices
- Start with strategic design: find subdomains and bounded contexts before writing aggregates.
- Put developers in regular contact with domain experts, not only through requirement documents.
- Use the experts' words in class names, method names and APIs, and change code when language changes.
- Keep aggregates small; reference other aggregates by identity rather than holding them.
- Change one aggregate per transaction and use domain events for the rest.
- Protect the core domain from legacy and third-party models with anti-corruption layers.
- Spend modelling effort on the core; use simple designs or products for generic subdomains.
- Revisit the context map as the organisation and product change.
Common mistakes
- Applying every tactical pattern to a simple CRUD application.
- Designing huge aggregates that lock too much data and cause contention.
- Treating bounded contexts as technical layers rather than business boundaries.
- Inventing a developer vocabulary that domain experts do not recognise.
- Creating one enterprise-wide canonical model that every context must share.
- Doing a single modelling workshop and never revisiting the model.
Frequently asked questions
Is DDD only for microservices?
No. DDD predates microservices by about a decade and works well in a modular monolith. Bounded contexts can be modules in one codebase; microservices are just one way to deploy them.
What is the difference between an entity and a value object?
An entity has an identity that persists while its attributes change, such as a customer account. A value object is defined only by its values and is usually immutable, such as an amount of money; two with the same values are interchangeable.
How big should an aggregate be?
As small as possible while still protecting its invariants. If a rule spans many objects, consider whether it really must be enforced immediately or can be handled with events and eventual consistency.
Do I need event sourcing to do DDD?
No. Event sourcing is an optional persistence technique that fits well with domain events, but most DDD systems store current state in ordinary databases.
How do I find bounded contexts?
Look for places where the same word means different things, where different experts own different rules, and where processes hand over. Event Storming workshops are a practical way to surface these.
Is DDD worth it for a small project?
Usually only the lighter parts: a shared language and clear module boundaries. Full tactical modelling pays off when business rules are complex and change often.
Glossary
- Domain: the subject area a system serves.
- Subdomain: a distinct part of the domain.
- Bounded context: the scope in which a model applies.
- Ubiquitous language: the shared vocabulary inside a context.
- Aggregate: a unit of consistency for changes.
- Entity: an object with identity.
- Value object: an immutable object defined by its attributes.
- Domain event: a record of something significant that happened.
- Repository: an abstraction for storing and retrieving aggregates.
- Anti-corruption layer: a boundary translator between models.
Summary
Domain-driven design, introduced by Eric Evans in 2003, builds software around a model shared with domain experts. Strategic design divides a large domain into bounded contexts, each with its own ubiquitous language, and maps how they relate. Tactical design uses entities, value objects, aggregates and domain events to express rules inside a context. DDD is most valuable for complex, changing domains and for finding service and data boundaries; it is overkill for simple CRUD work. Start with language and boundaries, keep aggregates small, and refine the model continuously.
Related
It belongs to Architecture in my stack, under MLOps and AI infrastructure. Others in the same group: