Menu
Module 1 / 8 Beginner 12 min read

Why Domain-Driven Design

The hardest part of software isn't the technology — it's mastering the complexity of the domain. DDD is a discipline for doing that on purpose.

In this module

Meet the central claim of DDD, the running example this course uses throughout, and the two halves — strategic and tactical — that every later module builds on.

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 id field 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 OrderService might 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.

Check yourself. Answer each one before you open it — the point is to catch the places where the module read as obvious but didn’t stick.

A support rep tells a customer their order is cancelled, and a courier delivers it anyway. What broke?

Not a missing if. The word cancel meant “void this order” in the support conversation and “flip a status flag” in the code, and nothing in the model held those two meanings together. That drift is the failure DDD is a response to — and the reason the very next module is about the Ubiquitous Language.

You’ve written a beautifully factored Order class — but it lives in the Billing context. Is that a tactical problem or a strategic one?

Strategic. Tactical design tells you what Order is; strategic design tells you where it belongs and why. A well-built class inside the wrong boundary is still a mess, which is why you need both halves rather than picking the one you find more interesting.

A team that has never heard of Eric Evans wrote a class with an id field and id-based equals(). Is it an Entity?

Yes. Recognising DDD that nobody built on purpose is the normal case, not a consolation prize — you classify by what the code does, not by what its authors intended.

Next: Module 2 — The Ubiquitous Language, where you’ll build your first glossary from the bookstore’s Sales context.