Domain-driven design is not, first, a diagram or a set of vocabulary words to learn. It is a set of concrete obligations a team takes on: talk to domain experts on an ongoing basis, name things in code exactly as the business names them, draw an explicit boundary wherever that naming stops holding, and protect the rules that must always be true behind a single guarded entry point. Skip any one of those four and a team ends up with the jargon, aggregates and bounded contexts in the wiki, without the benefit the jargon was supposed to produce.
The core idea
The vocabulary that usually gets taught first, bounded context, ubiquitous language, aggregate, describes artifacts that fall out of doing the four things above, not a starting point in itself. A team can draw a perfect context map and still not be practicing DDD if nobody is talking to domain experts anymore. The discipline is behavioral as much as technical, and the tactical code patterns only formalize a model that conversation should have already produced.
The four concrete asks
| Ask |
What it looks like day to day |
What happens if you skip it |
| Talk to domain experts continuously |
Regular modeling sessions, not a single kickoff interview |
The model calcifies around an early, incomplete understanding |
| Name things exactly as the business does |
Code review rejects a rename "for clarity" that drifts from the agreed term |
Developers and domain experts quietly start meaning different things by the same word |
| Draw a boundary where meaning changes |
A documented map showing where a word means something different across modules |
One bloated shared model tries to satisfy every department and satisfies none well |
| Guard invariants behind one entry point |
Every rule for a valid change lives inside one aggregate, not duplicated across callers |
The same rule gets implemented three times, slightly differently, and drifts |
A concrete example
Take the word "return." To a warehouse, a return is a physical item back on a shelf, with a condition check and a restock decision. To finance, a return is a reversed charge, with its own approval and ledger entry. To support, a return is a closed ticket, with its own status lifecycle. DDD does not try to unify these into one shared Return class; it draws three bounded contexts on purpose, gives each its own model, and defines an explicit translation at the seams where they need to talk to each other. Forcing one shared class across all three is the more common failure, not the fix.
Common mistakes
- Skipping straight to aggregates and value objects. Tactical patterns formalize a model that domain conversations should have produced first; without that, a team gets ceremony wrapped around a guess.
- Letting a rename "for developer clarity" drift from the agreed term. The moment code and business conversation use different words for the same thing, the ubiquitous language is already broken.
- Drawing boundaries around team org charts instead of meaning. A context boundary should track where a word's actual definition changes, not who reports to whom.
- Scattering an invariant across every caller "because it's a simple check." The first duplicate is fine; the third one drifts, and now parts of the system disagree about what a valid state looks like.
FAQ
Do I need a formal workshop with domain experts to start?
Not a formal one. Start by writing down the words your domain experts already use and checking whether the code uses the same ones.
Is DDD only useful for large teams?
No, though the coordination benefit of bounded contexts becomes more visible once several teams touch overlapping concepts.
What is the minimum viable version of DDD?
Ubiquitous language and one honest bounded context map, done before any tactical pattern is introduced.
How do I know a boundary belongs in a different bounded context?
The clearest signal is the same word needing a different definition, a different set of valid states, or different rules depending on which part of the system uses it.
Where to go next
See ports and adapters in practice for the structural boundary that typically protects a domain model like this, clean architecture vs hexagonal for how that boundary gets named and organized, and what a merge conflict is for what it looks like in code when two teams disagree about what a word means without an agreed boundary.