The problem DDD is a response to
Most non-trivial software fails for a boring reason: nobody can hold the business rules and the code in their head at the same time anymore. A developer reads OrderService.process() and has no idea whether “process” means “validate,” “charge,” “ship,” or all three. A product manager describes a rule in a meeting — “a split shipment only happens if no single warehouse can fill the order” — and six months later that rule is scattered across four classes, half-implemented, and nobody is sure if it’s still true.
Domain-Driven Design, introduced by Eric Evans in 2003 and substantially extended by Vaughn Vernon in 2013, is a response to exactly this failure mode. Its central claim is blunt: the hard part of building serious software is not the technology, it’s mastering the complexity of the domain — the actual subject matter the software serves (logistics, banking, claims processing, in this course’s case, a bookstore’s order fulfillment). Frameworks, databases, and languages are solved problems you can look up. The business’s own rules — what counts as a valid order, when an invoice is owed, how a shipment gets allocated — are not solved anywhere. You have to model them, and that model has to survive contact with real code.
Note. DDD is not a framework, a library, or an architecture diagram you copy. It’s a set of habits for naming things and drawing boundaries so the software keeps meaning the same thing to everyone who touches it.
The defining idea: one vocabulary, two places
Here’s the idea that everything else in this course hangs off: the model is not a diagram filed away after a design meeting. It’s a vocabulary that lives simultaneously in conversation and in code. When a developer says Order.cancel() and a customer support rep says “cancel the order,” they mean the same operation, with the same rules, because the code is the rule. This shared, rigorous vocabulary is called the Ubiquitous Language, and it’s the subject of the very next module — it’s load-bearing enough to be the name of this course.
If the word in the meeting and the word in the method name have drifted apart, the model has already broken — you just haven't noticed yet.
This sounds abstract until you watch it fail. Imagine a support rep asks an engineer to “cancel” an order that’s already been picked up by a courier. The engineer’s code happily flips a status flag, the rep tells the customer it’s cancelled, and a courier turns up at their door with a parcel nobody can intercept. The bug isn’t a missing if statement — it’s that “cancel” in conversation and cancel() in code stopped meaning the same thing somewhere along the way, and nothing in the model enforced their agreement.
The running example for this course
Every module from here on uses the same domain so the concepts accumulate instead of resetting: a small online bookstore’s order fulfillment system, with three areas of responsibility that will become formal Bounded Contexts in Module 4 — Sales (placing and tracking orders), Billing (invoicing), and Shipping (getting books to customers). Module 3 will explain why Sales is the core domain here — the part with the differentiating logic — while Billing and Shipping are supporting and partly generic. You’ll meet the Order aggregate, Money and OrderLine value objects, and an OrderPlaced domain event repeatedly; by Module 8 you’ll be able to read the whole model end to end.
Two halves: strategic and tactical
DDD splits cleanly into two concerns, and conflating them is the single most common way people get lost in it:
- Strategic design is about the large-scale shape of the problem: how the whole domain divides into areas of meaning, where the boundaries between those areas lie, and how the areas relate to each other. This is organising the domain. Modules 3–5 cover this: Subdomains, Bounded Contexts, and Context Mapping.
- Tactical design is about the building blocks inside one of those boundaries — the actual classes that express the model: Entities, Value Objects, Aggregates, and the rest. This is expressing the domain in code. Modules 6–8 cover this.
Strategic structure tells you where things live and why. Tactical structure tells you what the things are. You need both — a beautifully factored Order class inside the wrong boundary is still a mess, and a perfect set of boundaries with anemic, getter-setter classes inside them hasn’t actually modeled anything.
How to read the rest of this course
A few habits will save you time in every module that follows:
- Most real codebases are not “pure” DDD, and that’s fine. You’ll constantly be mapping a plain class with an
idfield and behavior onto “Entity” even though nobody on the original team had read Evans. Recognizing DDD that was never intentionally built is a normal, central skill — not a failure to find the “real” pattern. - Classify by responsibility, not by name. A class called
OrderServicemight be a thin, correct orchestrator — or it might be hiding the one real piece of business logic in the whole codebase. Suffixes and annotations are hints; what the code actually does is the verdict. - Boundaries are judgment calls, not facts. Later modules will give you heuristics for finding Bounded Contexts and Aggregate boundaries, but none of them are mechanical tests. State your reasoning, and expect to revise it as you learn more about the domain — that’s the discipline working, not a sign you got it wrong the first time.
Tip. Keep the bookstore example in mind as a sandbox. Whenever a module’s definition feels abstract, ask “what would this be for
Order?” before moving on.
Next: Module 2 — The Ubiquitous Language, where you’ll build your first glossary from the bookstore’s Sales context.