Domain-Driven Design (DDD)

Domain-Driven Design is an approach that puts the business domain at the center of software design and builds a shared language between engineers and domain experts. Instead of letting database tables or framework conventions shape the model, DDD shapes the code around how the business actually thinks and talks.

DDD is most valuable when the domain is complex and the cost of misunderstanding is high — billing rules, logistics, healthcare. It's overkill for simple CRUD. The biggest risk is "DDD theater": adopting the patterns (aggregates, value objects) without the domain conversations that give them meaning.

TL;DR

Quick Example

A value object that enforces its own validity, plus a domain event:

Strategic DDD

Ubiquitous language

Build a shared vocabulary — "Project," "Member," "Workspace," "Permission" — and use the same terms everywhere: conversations, docs, code, and APIs. When the model and the conversation use different words, bugs and misunderstandings breed in the gap.

Bounded contexts

A bounded context is a boundary within which a model is internally consistent. "Billing" means invoices, subscriptions, and payments; "Identity" means users, sessions, and MFA. Within a context, every name has a precise meaning; across contexts, the same word ("user") can legitimately mean different things. Bounded contexts reduce confusion and define clear ownership boundaries.

Tactical DDD (Building Blocks)

DDD and Microservices

Bounded contexts often map cleanly onto microservice boundaries — each service owns one context and its data. But they don't have to: you can apply DDD inside a monolith (a "modular monolith") and capture most of the benefit — clear contexts, a shared language, well-defined aggregates — without distributed-systems overhead.

Best Practices

Common Mistakes

Treating the database schema as the domain model

Giant aggregates

FAQ

When should I use DDD — and when is it overkill?

Use DDD when the domain is complex, evolving, and central to the business — where getting the rules subtly wrong is costly (billing, scheduling, compliance). It's overkill for simple CRUD apps, prototypes, or technically-driven systems with little business logic. The strategic parts (ubiquitous language, bounded contexts) are valuable far more often than the full tactical pattern set.

What's the difference between an entity and a value object?

An entity has identity that persists independent of its attributes — two users named "Alice" are different users; a user stays the same entity even as their email changes. A value object has no identity and is defined entirely by its values — two Money(5, "USD") instances are interchangeable, and value objects are immutable. Modeling the right concepts as value objects removes a lot of accidental complexity and bugs.

What is an aggregate and why keep it small?

An aggregate is a cluster of related objects treated as a single unit for data changes, with a root that guards its invariants — you modify the cluster only through that root. Keeping aggregates small limits the transaction/consistency boundary, reduces lock contention, and keeps invariants tractable. Large aggregates create performance problems and tangled rules; coordinate between small aggregates via domain events instead.

Can I do DDD without microservices?

Absolutely. DDD is about modeling, not deployment topology. A modular monolith with clear bounded contexts, a ubiquitous language, and well-designed aggregates captures most of DDD's value with far less operational complexity than microservices. Bounded contexts can later become service boundaries if you split the monolith, but that's an option, not a requirement. See Microservices.

Related Topics

References