DDDD - Domain Driven Design for Dummies. One extra D, because by the time you finish Eric Evans’ original 560-page book, you’ll feel like a dummy regardless. This is the shorter version, the one I wish I’d read before trying to apply DDD on a real project for the first time.
What DDD actually is
Strip away the jargon and DDD is about one stubborn idea: the structure of your code should reflect the structure of the business it serves. Not the database. Not the framework’s defaults. The business.
Most software fails this. We model User, Order, Product, and call it a domain model. But “user” in marketing means something different than “user” in billing, which is different from “user” in shipping. Pretending they’re the same entity is how you end up with a 47-column users table that nobody dares modify.
DDD’s contribution is a vocabulary for getting this right. Two patterns matter more than all the others combined.
Pattern 1: Ubiquitous Language
The team - engineers, product, domain experts - uses the same words for the same concepts, everywhere. In meetings. In code. In the database. In the API. In Slack.
If marketing calls them “subscribers” and engineering calls them “users” and the support team calls them “accounts”, you have three different things masquerading as one. The conversations get confused. The code gets confused. Bugs live in the gaps.
The fix is not “everyone should call them users now.” The fix is deciding when a subscriber, a user, and an account are actually distinct concepts, and being precise about which one you mean in any given context.
Which leads directly to the second pattern.
Pattern 2: Bounded Contexts
A bounded context is the scope inside which a term has one meaning. Within the billing context, “customer” means a paying entity with a payment method. Within the shipping context, “customer” means a delivery address with a contact. They’re different concepts that happen to share a name. The boundary protects you.
In practice, a bounded context usually maps to:
- A service or a clearly-isolated module.
- A team that owns it.
- A domain expert who can answer “what does this mean?” without hedging.
Microservices done well are usually bounded contexts done well. Microservices done badly are the same monolith chopped into network calls - same data model, same coupling, same problems, plus latency. The boundary is the value, not the deployment artifact.
If you draw a context map of your business and your code looks nothing like it, that’s the work.
Strategic vs. tactical DDD
DDD splits into two halves, and most teams should care almost exclusively about the first one.
- Strategic DDD - ubiquitous language, bounded contexts, context maps, the patterns of integration between contexts. This is where 90% of the value lives. It changes how you organize teams, services, and conversations.
- Tactical DDD - aggregates, entities, value objects, domain events, repositories. The implementation patterns inside a context. Useful, but they’re tools, not goals.
Plenty of teams adopt the tactical patterns (entities and aggregates everywhere) without ever drawing a context map. They get a more complicated codebase and none of the strategic clarity. Don’t do this. Strategic first. Tactical only if you genuinely need it.
The tactical patterns, briefly
Since you’ll see the terms anyway:
- Entity - something with identity that persists. A
Customeris the sameCustomertomorrow even if their name changes. Identity matters. - Value Object - something defined entirely by its attributes, with no identity. Money. A date range. A street address. Two
Money(10, "EUR")are interchangeable; you don’t ask “which one?” - Aggregate - a cluster of entities and value objects treated as a single consistency unit. The aggregate has a root (the only entity you can hold a reference to from outside) and enforces invariants for everything inside it. An
Orderaggregate might containOrderLineentities; outside code talks to theOrder, never directly to a line. - Domain Event - a notable thing happened.
OrderPlaced.PaymentFailed.SubscriptionRenewed. Useful for decoupling: the billing context emits an event; the shipping context reacts, without billing needing to know shipping exists. - Repository - the abstraction that pretends your aggregates live in a collection, hiding the fact that they’re actually persisted somewhere.
customerRepository.findById(id).
Use these where they pay rent. Don’t use them where they don’t.
When DDD is overkill
DDD is investment-heavy. It’s worth it when:
- The domain is genuinely complex - multi-step business processes, lots of edge cases, regulatory requirements, domain experts you need to interview.
- The system is long-lived - the cost of getting the model wrong compounds over years.
- The team is big enough to benefit from explicit boundaries - roughly, more than one team working on the same product.
It’s not worth it when:
- You’re building CRUD. If your app is “list, create, update, delete” against tables, DDD will add ceremony without insight.
- The domain is well-understood and stable. A URL shortener doesn’t need bounded contexts; it needs to shorten URLs.
- You’re a team of two building an MVP. Ship it. Learn what the actual domain looks like. Apply DDD to v3 if the complexity warrants it.
The first time I tried to apply DDD, I was the only engineer on a project that didn’t need it. I produced an aggregate-rich, event-driven codebase that did exactly what a Rails app would have done in 1/10th the code. That was an expensive lesson.
A starting point
If you want to try DDD on a real project, do these three things and stop:
- Run an EventStorming session. Get the domain experts in a room (virtual or otherwise). Map the business as a timeline of events on sticky notes. You’ll discover the actual boundaries, fast. Alberto Brandolini’s book is the source.
- Draw a context map. Boxes for bounded contexts, arrows for how they integrate. Note the relationships - partnership, customer-supplier, anti-corruption layer. This single diagram changes architectural conversations.
- Agree on the ubiquitous language for one context. Write the glossary down. Use the words in code, in the API, in tickets. Notice how the conversations get cleaner.
That’s enough DDD to deliver most of the value on most projects. If you outgrow it, then read the book.
The one-paragraph summary
DDD says: the domain is the source of truth, the team’s shared language is the contract, and explicit boundaries protect you from accidental complexity. The tactical patterns are tools to use sometimes; the strategic patterns are habits to practise always. Get the boundaries right and the rest is easier. Get the boundaries wrong and no architecture pattern will save you.
If you’re trying to figure out where the seams in your system should be - that’s a conversation I enjoy.