Domain-driven design essentials
A shared language, bounded contexts, entities, value objects, aggregates and domain events: modelling complex business software.
On the roadmap:Software Design · step 3: Architecture styles
Lesson 7 of 7 · 12 min read · quiz · exercise
What DDD is for
Domain-driven design (DDD), introduced by Eric Evans, is an approach for software where the business domain is complex. Its core claim: the hardest part is not technology but understanding and modelling the business, so code should reflect the domain, in the domain's own language, developed with domain experts.
DDD has a strategic side (how to divide a big system) and a tactical side (how to model inside a part).
Strategic design
Ubiquitous language
Developers and domain experts agree on one shared vocabulary, and use it everywhere: conversations, documents and code. If the business says "policy lapse", the code has Policy.lapse(), not status = 3. Misunderstandings shrink because there is no translation layer in people's heads.
Bounded contexts
One model cannot serve a whole large organisation: words mean different things in different places. A bounded context is a boundary inside which a model and its language are consistent.
Figure SDA-SDE-07-1 · Bounded contexts · © forcsguy.com
Each context has its own model, and often its own team, code and database. Contexts relate through explicit patterns: shared kernel, customer-supplier, conformist, and the anti-corruption layer, a translator that protects your model from a messy external one. Contexts are natural candidates for microservices boundaries.
Subdomains
Break the business into subdomains and treat them differently:
- Core: what makes the business different. Invest your best people and custom design.
- Supporting: needed but not distinctive. Build simply.
- Generic: solved problems (authentication, payments). Buy or reuse.
Tactical design: building blocks
Entities
Objects defined by identity, which persists as attributes change: a Customer, an Order. Two customers with the same name are still different.
Value objects
Objects defined only by their values, immutable, and interchangeable: Money(10, "EUR"), Address, DateRange. They carry their own rules (money cannot add different currencies) and remove "primitive obsession".
class Money {
private constructor(readonly cents: number, readonly currency: string) {}
static of(cents: number, currency: string) { if (cents < 0) throw new Error("negative"); return new Money(cents, currency); }
add(o: Money) { if (o.currency !== this.currency) throw new Error("currency mismatch"); return new Money(this.cents + o.cents, this.currency); }
}
Aggregates
A cluster of entities and value objects treated as one unit for consistency, with a single aggregate root through which everything is accessed. The root enforces invariants ("an order total equals the sum of its lines").
class Order { // aggregate root
private lines: OrderLine[] = [];
addLine(product: ProductId, qty: number, price: Money) {
if (this.status !== "draft") throw new Error("Order is locked"); // invariant
this.lines.push(new OrderLine(product, qty, price));
}
}
Rules of thumb: keep aggregates small, change one aggregate per transaction, and refer to other aggregates by id, not by object reference.
Domain events
Facts that happened in the domain, named in the past tense: OrderPlaced, PaymentReceived. They let other parts react without tight coupling and carry changes between aggregates and contexts, usually with the outbox pattern for reliable delivery.
Repositories and services
- A repository loads and saves aggregates, hiding persistence (a port in hexagonal architecture).
- A domain service holds business logic that does not belong to one entity (for example a pricing calculation across several aggregates).
- An application service (use case) orchestrates: load, call domain methods, save, publish events.
Factories
Create complex aggregates in a valid state.
How DDD fits with the rest
- The domain model sits at the centre of clean or hexagonal architecture, free of framework code.
- Event-driven designs and CQRS often pair with DDD.
- Bounded contexts guide microservice boundaries.
When to use it
DDD pays off where the domain is complicated and changes: insurance, logistics, finance, healthcare. For simple CRUD applications it is overkill; a plain data model is fine.
Starting small
- Talk to domain experts and write down their words as a glossary.
- Draw the contexts and how they exchange information (a context map).
- Model one core subdomain deeply, with aggregates and invariants as tests.
- Use event storming (a workshop mapping events on sticky notes) to discover the domain together.
- Keep refining the model as understanding improves.
Real-world reading
Related reading
- Domain-Driven Design ReferenceEric Evans · Domain LanguageBooks
- Moving Core Business Search to Elasticsearch at YelpYelp engineers · YelpCase Studies
- Tech Dummies: System Design Case StudiesNarendra L · Tech DummiesYouTube
- Fast Order Search using Data Pipeline and Elasticsearch at YelpYelp engineers · YelpCase Studies
Show 6 moreShow fewer
- Event SourcingMartin FowlerBlogs
- The Dataflow Model (paper)Akidau et al. · GooglePapers
- Service Architecture (3 parts: Domain Gateways, Value-Added Services, BFF) at SoundCloudSoundCloud engineers · SoundCloudCase Studies
- Pattern: Transactional outboxChris Richardson · microservices.ioDocumentation
- Tech Stack at ShopifyShopify engineers · ShopifyCase Studies
- Introduction to gRPCgRPC · CNCF / gRPCDocumentation
Check your understanding
Question 1 of 3
0 correct so far
What is a bounded context?
Go deeper
- Domain-Driven Design (the 'blue book') and Domain-Driven Design Distilled by Eric Evans; Vaughn Vernon: the original and a short introduction; both worth buying
Practise it: URL shortener
Everything below is already set up for this lesson: change the design, run the traffic, and see whether you meet the goal.
- 1The task
- 2Build and test
Read-heavy, a few links are very popular.
Add or remove parts, move traffic up and down, and break something to see what gives way first.
Try it yourself
In an online shop, sales calls a person a 'customer' with marketing preferences, while shipping calls them a 'recipient' with an address and delivery notes, and billing a 'payer' with payment terms. How does DDD approach this?
Click what a strong design here would need:
More in Design practice
© 2026 forcsguy.com. Original lesson text and diagrams. Books and sites linked above belong to their authors.