~/blog/who-should-consider-using-ddd-ddd-series
Who should consider using DDD? (DDD series part 3)
Part 3 of the DDD series: which organisations get value from it, from regulated domains to complex products, and which get overhead instead.
series · Domain-Driven Design (DDD) Series3 / 11
- 01What Domain-Driven Design Is (DDD series part 1)
- 02Benefits of using DDD (DDD series part 2)
- 03Who should consider using DDD? (DDD series part 3)
- 04Domain Model (DDD series part 4)
- 05Bounded Context (DDD series part 5)
- 06Value object (DDD series part 6)
- 07Entity (DDD series part 7)
- 08Services (DDD series part 8)
- 09Mastering Transactions: The Power of Aggregates in DDD
- 10Domain Events in DDD (DDD series part 10)
- 11Alternatives to Domain-Driven Design, and When to Use Them
Part 2 of this series argued that DDD's payoff is a shared vocabulary between developers and the people who actually understand the business. A shared vocabulary is only worth building if someone on the other side of it has something non-obvious to say. This part is about recognising that someone, and about the organisational conditions that make the investment pay back rather than sit on the books as unused ceremony.
What follows is not a maturity model and not a checklist to tick before a kickoff meeting. It covers the three questions worth asking before adopting DDD: whether a domain expert actually exists and will correct you, whether the domain has rules worth protecting rather than fields worth validating, and whether the system will live long enough to collect on the investment. It also covers the two situations people cite as reasons for DDD that are not reasons at all, and where to read next if the answer comes back no.
The expert who corrects you#
DDD runs on a ubiquitous language: one vocabulary, shared by developers and the people who understand the business, used consistently in conversation, in code, and in the model. That only works if a real domain expert exists and is willing to tell you when you got a word wrong.
"Domain expert" does not mean a stakeholder who signs off on a Jira ticket. It means someone who can say "that's not what a chargeback is, a chargeback is initiated by the card network, not the merchant" and be right. Insurance underwriters, compliance officers, clinical staff, and senior support engineers who have fielded five years of edge cases are domain experts. A product manager repeating what sales told them usually is not, and that distinction decides whether the model you build reflects the business or reflects a guess dressed up as one.
Without that person, the model becomes the developers' best understanding of a domain none of them live in, defended in code review by people who all made the same guess. That is not worse than a plain CRUD application. It is worse than an honest one, because it looks designed.
A domain with real invariants#
The second question is whether the domain has rules that span more than one field. A domain model earns its keep by protecting invariants: a booking cannot exceed an order's remaining credit, a policy cannot lapse while a claim is open, a transfer cannot settle twice. If every rule in the system is "this field is required" or "this value must be positive," there is no invariant to protect, and the aggregate you build around it exists to hold a setter.
// BAD: the "aggregate" is a bag of fields with a save() method. No rule crosses
// two of them, so there is nothing a domain model protects that a form
// validator did not already protect for free.
final class Booking
{
public function __construct(
public string $id,
public string $customerName,
public \DateTimeImmutable $date,
) {}
}Compare that to a rule that actually spans state:
// GOOD: the invariant ("a seat cannot be double-booked, capacity cannot go
// negative") lives inside the aggregate boundary and is enforced on every
// write path, not re-checked by whichever controller happens to call it.
final class EventBooking
{
private int $seatsTaken = 0;
public function reserve(int $seats): void
{
if ($this->seatsTaken + $seats > $this->capacity) {
throw new CapacityExceeded($this->id);
}
$this->seatsTaken += $seats;
}
}The difference between the two examples is not style. It is whether a rule can be violated by calling the setters in the wrong order from two different places in the codebase. If it can, you have an invariant. If it cannot, because there was never a rule to break, DDD has nothing to add here.
Enough runway to collect on the investment#
A domain model is an investment that pays back over years of change, not over the sprint that ships
it. Internal tools with a known lifespan, one-off migration scripts, and prototypes built to answer
a single question will be retired before the investment matures. Spending three weeks modelling
Policy as an aggregate for a tool that gets replaced in four months is not caution, it is waste with
good intentions.
Two reasons people cite that are not reasons#
"Organizations that use microservices architecture" shows up in almost every list like this one, including an earlier draft of this post. It does not belong on the list. DDD is a way to model a domain. Microservices are a way to deploy software. A monolith can be rigorously domain-driven, and a fleet of forty services can be forty transaction scripts talking over HTTP. The two get confused because bounded contexts make good service boundaries, which is a fact about DDD helping you deploy, not about your deployment topology telling you whether to model.
"Team inexperience" is the second one, usually offered as the reason not to adopt DDD rather than a reason to. I think it is the weakest reason either way. Teams learn a ubiquitous language the same way they learn a codebase: by using it. A domain with nothing worth modelling does not become interesting because the team got better at modelling.
A decision you can act on#
| Signal | Favors DDD | Favors something simpler |
|---|---|---|
| Domain expert | Exists, corrects you in review | Nobody can tell you when the model is wrong |
| Invariants | Rules span multiple fields or entities | Every rule is a single-field validation |
| Lifespan | Multi-year system under active change | Internal tool, migration script, prototype |
| Team shape | Several teams need the same words to mean the same thing | One team, one codebase, low churn |
| Deployment | Irrelevant to this decision | Irrelevant to this decision |
Three rows should point the same direction before this is worth the ceremony it adds. One row pointing toward DDD and the rest pointing away is not a tie, it is an answer.
Where to go if the answer is no#
If your domain turns out to be simpler than the list above requires, that is not a dead end for this series. Part 11 covers the actual alternatives by name, transaction script, active record, table module, and tactical-only DDD, and what each one is good at. The honest version of "DDD is not for everyone" names what you reach for instead, rather than leaving you with a CRUD app and a vague sense you did something wrong.
Previous parts#
Benefits of using DDD (DDD series part 2)
Unlocking the Power of Domain Driven Design in Software Development (DDD series part 1)
$ related posts
· 19 min
What This Architecture Cost, Measured Rather Than ArguedNEW
What hexagonal architecture costs in Symfony 8: ports, layers and buses counted from the repository, and the thresholds below which it is not worth it. Part 6.
▸ DDD, CQRS and Hexagonal Architecture in Symfony 8 · 6#ddd#hexagonal-architecture#symfony#php#software-architecture#cqrs#refactoring19 min
· 17 min
Event Sourcing Behind the Port You Already Have
Event sourcing in Symfony 8: an event-sourced repository swapped in by one DI alias, proven by the same contract test, and what the alias did not cover. Part 5.
▸ DDD, CQRS and Hexagonal Architecture in Symfony 8 · 5#ddd#hexagonal-architecture#symfony#php#event-sourcing#postgresql#cqrs17 min
· 19 min
Domain Events Are Not Integration Events
Domain events vs integration events in Symfony 8: recorded by the aggregate, released after commit, versioned into a contract, consumed by a worker. Part 4.
▸ DDD, CQRS and Hexagonal Architecture in Symfony 8 · 4#ddd#hexagonal-architecture#symfony#php#messenger#rabbitmq#domain-events19 min