Comparison · Meaning vs direction

Domain-Driven Design vs Clean Architecture: What Is the Boundary Drawn Around?

DDD draws boundaries around meaning — bounded contexts, ubiquitous language, aggregates. Clean Architecture draws them around dependency direction. How they compose, what each one does that the other cannot, and when DDD is genuinely overkill.

By Michał Jaskólski Updated 9 min read
  1. 01 Domain-Driven Design domain-driven-design Modeling complex business domains in software
  2. 02 Clean Architecture clean-architecture Building maintainable, testable software architectures
  3. 03 Software Design software-design-philosophy Reducing complexity through thoughtful software design
  4. 04 Data-Intensive Apps ddia-systems Designing reliable, scalable data systems
  5. 05 The 37signals Way 37signals-way Lean, opinionated product development from Getting Real to Shape Up

Both are boundary disciplines, and the conflation is expensive because the question each answers is different. Clean Architecture asks which way dependencies may cross a boundary. Domain-Driven Design asks where the boundary belongs and what the words inside it mean. You can satisfy one perfectly and have nothing to show for the other.

The tell is a team that says “bounded context” when it means “layer” — a mistake that yields one shared, overgrown model with a tidy stack of folders around it.

We publish both as free agent skills and they are among the most-installed pair in the library, so this is a composition question, not a contest. If the format is new to you, start with what an AI agent skill is.

What does Clean Architecture draw its boundary around?

Dependency direction, and almost nothing else. Clean Architecture turns on the Dependency Rule: source code dependencies may point only inward, toward higher-level policy, and an inner circle must not name anything declared in an outer one — no class, no function, no data format the outer layer owns. Entities innermost, use cases around them, adapters outside, frameworks at the edge. When control flows outward at runtime, you invert it with an interface declared on the inside.

That is a rule about arrows, and it is deliberately content-free: it has no opinion on whether the central object is an Order or a TransactionRecord, or whether billing and support share one customer model. Point every arrow correctly at a single overgrown model and you have a textbook-compliant architecture wrapped around the exact structure DDD exists to prevent.

What does DDD draw its boundary around?

Meaning. Domain-Driven Design starts from the claim that the hard part is understanding the business, and that the code should say what the business says. Three ideas carry the weight.

Ubiquitous language. One vocabulary, used identically in conversation, in the model and in the code. When the words match, a whole class of translation bugs stops existing, and a domain expert can read a method name and tell you it is wrong.

Bounded contexts. A boundary inside which one word has exactly one meaning. Customer in billing carries a tax ID and a payment method; Customer in support carries a ticket history and a satisfaction score. Fusing them into one omniscient class is the failure the pattern exists to prevent. Where contexts meet you translate, often through an anti-corruption layer.

Aggregates. A cluster governed by a root that enforces the invariants, making it a transactional consistency boundary as well as a modelling one. Evans defined the root and the rule that outside objects reference only it; the familiar heuristics about small aggregates and referencing others by identity are later tightenings, most associated with Vaughn Vernon rather than the 2003 book.

Notice what DDD does not decide: whether the domain layer may import the ORM. Evans isolates the domain; the mechanism that enforces that isolation is Clean Architecture’s contribution.

DDD vs Clean Architecture: the boundary table

Domain-Driven DesignClean Architecture
The question it answersWhere does the boundary belong, and what do the words mean inside it?Which way may dependencies cross it?
Boundary shapeVertical — a bounded context is a full slice of the systemHorizontal — layers within one deployable
Silent onWhich way dependencies pointWhat anything means or where to cut
Between systemsContext mapping: anti-corruption layer, conformist, shared kernelAn adapter, with no view on the relationship
Who must be in the roomDomain experts — it is a conversation before it is codeDevelopers — a decision you can make alone
Verified byA domain expert reading the code and disagreeingAn import graph
Cheapest useful subsetThe ubiquitous languageThe direction rule at one volatile boundary
Cost when overappliedAggregates and mappers around a domain with no invariantsCircles, DTOs and interfaces around a CRUD app

Clean Architecture tells you which way the arrows point. Domain-Driven Design tells you what the boxes are called and why they exist.

How do they compose in one codebase?

Cleanly, because DDD’s domain layer is what Clean Architecture leaves empty at the centre. Martin’s inner circle is a slot: it holds enterprise-wide business rules and says nothing about their shape, so a transaction script sitting there satisfies the Dependency Rule perfectly. DDD is the richest way to fill it — entities, value objects and aggregates that own their invariants.

The repository is where the two lock together. Evans’s Repository already declares itself in domain language and hides the persistence technology; Martin’s rule puts the interface inside the boundary and its Postgres implementation outside. Same object, two justifications. The anti-corruption layer works the same way: Clean Architecture calls it an adapter and stops, while DDD tells you what the translation is for.

The failure worth naming is confusing the two boundary shapes. A bounded context is vertical — its own model, its own language, its own full stack, potentially its own database. A layer is horizontal and lives inside a context. One Customer model with a controller, a service and a repository around it is layers, not contexts, and a fifth folder will not produce any.

Prompt

Use the domain-driven-design skill to map the bounded contexts in my system — find the words that mean different things to different teams, draw the context map with the relationship type for each pair, and mark where an anti-corruption layer is needed so another team's model does not leak into mine

Domain-Driven Design

Is hexagonal architecture the same as Clean Architecture — and is either one DDD?

Related, not identical, and no.

Ports and Adapters (Alistair Cockburn, 2005), Onion Architecture (Jeffrey Palermo, 2008) and Clean Architecture (Robert C. Martin, 2012 as a post, 2017 as a book) are a family; Martin presents his as a synthesis and names the others as inputs. They share one instinct: the application core defines the interfaces, infrastructure implements them, and the source dependency points inward.

They differ in what else they prescribe. Hexagonal is deliberately symmetric — driving ports on one side, driven ports on the other — and says nothing about layering inside the hexagon. Onion adds concentric rings with a domain model at the centre. Clean Architecture adds the entity-versus-use-case split and a rule about what may cross a boundary.

None of them is DDD. The association is historical — Vaughn Vernon’s Implementing Domain-Driven Design uses ports and adapters as its default architecture — but that is a pairing convention, not an identity. You can build a hexagon around a domain model nobody in the business would recognise.

When is DDD overkill?

More often than its reputation suggests. The tactical half — aggregate roots, repositories, factories, value objects, an application layer, mapping between domain and persistence models — multiplies files and indirection, fights most ORMs, and needs a team fluent in the vocabulary. Against a domain with real invariants that is an excellent trade; against a form saved to a table it is ceremony, and an aggregate that enforces nothing is a class with a list inside it.

The guard is DDD’s own strategic layer, which is the half people skip. Separate the core domain — the part that is actually your advantage — from the generic subdomains anyone could buy, model the core deeply, and buy authentication, email and payments rather than hand-crafting a commodity. If nothing qualifies as core, that is your answer about the tactical patterns.

Strategic DDD is a different bargain: naming things the way the business names them costs nothing, and noticing that Customer means two things is worth doing on day one, when the split is a rename rather than a migration. It is the aggregate machinery that has to justify itself — The 37signals Way is the counterweight when it cannot.

Prompt

Use the clean-architecture skill to take the domain model I already have and place it under the Dependency Rule — say which types are entities and which are use cases, where each repository interface should be declared versus implemented, and exactly what is allowed to cross each boundary

Clean Architecture

Which one do you need first?

Stop at the first line that matches.

  1. Two teams use the same word for different things and the translation keeps producing bugs. DDD, strategic half.
  2. You cannot run a business rule in a test without the database and the web framework. Clean Architecture. Your model is not the problem yet.
  3. One class has forty fields because three departments each needed six. DDD — a missing context boundary, not a missing layer.
  4. You are about to split into services. DDD first to find the seams, Clean Architecture second for the inside of each one. Splitting before you know the seams is the expensive order.
  5. The domain is genuinely CRUD and every word means one thing. Keep the direction rule and the ubiquitous language; skip the tactical patterns. If the modules are multiplying anyway, that is a complexity question for Software Design.

Frequently asked questions

Is a bounded context the same as a layer?

No, and it is the most consequential confusion of the two. A bounded context is a vertical slice — its own model, its own language, its own stack of layers, potentially its own database. A layer is horizontal and sits inside a context. Layers separate technical responsibilities within one model; contexts separate models from each other.

Do I need DDD to use Clean Architecture?

No. Clean Architecture works over any core, including procedural transaction scripts — the Dependency Rule is satisfied by placement alone. What you lose is a principled answer to where the boundaries go, which is why systems with a clean layer diagram and one enormous shared model are so common.

Are aggregates, domain events and CQRS all part of DDD?

Aggregates are, from the 2003 book. Domain events entered the pattern language later, and CQRS and event sourcing grew up alongside DDD rather than inside it — commonly paired, not required. Treat the tactical catalogue as a menu you order from once strategic design says the domain is worth it.

Which should I learn first?

Clean Architecture, for the faster payback: one rule, verifiable in an import graph, useful on the smallest project. DDD earns its keep when people already argue about what things mean — and if that is happening now, invert the order, because no amount of correct dependency direction settles an argument about what Customer is.

Where to go next

If your question is craft rather than boundaries, the neighbouring comparison is Clean Code vs Clean Architecture. Applied from scratch, in sequence: how to design the best possible architecture for a new app.

Both skills are free and MIT-licensed, alongside the rest of the Architecture & Systems skills:

npx skills add wondelai/skills --all --global
Work with us

We build the skills you already use. Now we’ll build yours.

Custom skills · Subagents · MCP integrations — shipped to production, not demoed.

Sprints from $3K · shipped to production, or you don’t pay the final milestone.