Software Architecture

What Is Domain-Driven Design? Modeling Real-World Logic

What Is Domain-Driven Design? Modeling Real-World Logic

Most banking, logistics and healthcare systems get hard because of the business rules, not because of the database. Domain-driven design (DDD) is the approach built around that fact. Code gets organized around the business domain, and developers and domain experts work out one shared language and one model together.

Martin Fowler’s 2020 bliki entry picks strategic design as an especially important part of Eric Evans’s 2003 book. It splits a large domain into a network of bounded contexts, and Fowler says he hadn’t seen anyone handle that problem convincingly before.

Tactical patterns come second. Entities, value objects, aggregates, domain services and the rest work inside each context, once its boundaries exist.

What is domain-driven design?

YouTube player

Code gets structured around the business domain, with developers and domain experts building one shared language and one model. That model, not the database schema or the framework, drives each design decision.

The domain is just the business area the software serves, whether that’s banking, logistics or healthcare.

Large systems end up with more than one model. Each lives inside a bounded context, a boundary where its terms stay consistent.

Where domain-driven design comes from

Pearson’s catalog lists Eric Evans’s Domain-Driven Design: Tackling Complexity in the Heart of Software as published by Addison-Wesley on 20 August 2003, under a 2004 copyright.

That copyright year probably explains why some explainers give 2004 as the origin.

Later books turned the ideas into working practice. Vaughn Vernon’s Implementing Domain-Driven Design, published 6 February 2013, connects strategic patterns to tactical code (Pearson). Microsoft Learn recommends Vlad Khononov’s Learning Domain-Driven Design as the modern reference beside Evans.

Martin Fowler’s short bliki entries define single patterns, such as aggregate and bounded context.

What drives the global software industry?

Uncover software development statistics: industry growth, methodology trends, developer demographics, and the data behind modern software creation.

Discover Software Insights →

What domain-driven design is not

People often reduce it to a pattern catalog. CodeOpinion argues that using entities, aggregates and repositories without caring about the domain is only pattern use. Coding is the last step, not the first.

The stack doesn’t matter either. According to the cqrs.com primer, any language works, with or without specific libraries.

And DDD doesn’t replace a software architecture. Layered, hexagonal and microservice designs can all host a domain model, and the approach does not pick between them.

How strategic design splits a domain into bounded contexts

YouTube player

Strategic design starts with a map of the business domain. That gets sorted into subdomains, and bounded contexts are drawn so each model stays consistent inside its own boundary. Tactical design takes over after that, one context at a time.

Microsoft Learn’s Azure Architecture Center (February 2026 revision) puts the order as analyzing the domain, defining bounded contexts, applying tactical patterns inside each one, and only then identifying services.

Subdomains: core, supporting and generic

YouTube player

Microsoft Learn sorts subdomains as core, supporting or generic, and the sorting decides where design effort goes.

The core subdomain is the competitive advantage, so it gets detailed modeling and substantial team investment. Shipping and drone management play this role in Microsoft’s drone delivery scenario. Supporting subdomains keep the business running without setting it apart. Invoicing needs custom code but no deep investment. Generic ones are problems the industry already solved, which makes user accounts and the call center candidates for off-the-shelf products.

Put the modeling effort into the core and buy the generic parts.

Bounded contexts and ubiquitous language

YouTube player

Martin Fowler recalls an electricity utility where the word “meter” meant three different things: the grid connection to a location, the connection to a customer, and the physical device that gets replaced when faulty (bliki, January 2014).

People talk their way past that kind of ambiguity in conversation. Code can’t.

A bounded context draws a line around one model so each word keeps a single meaning inside it. Fowler adds that human culture is usually the dominant factor in where the line falls, because a different language calls for a different model.

Ubiquitous language is the vocabulary developers and domain experts build together inside a context. The same terms show up in conversation, in documentation and throughout the codebase.

Each context can have its own language, so “account” can mean different things in different contexts (Microsoft Learn).

Context maps and the anti-corruption layer

YouTube player

A context map documents how bounded contexts relate and where they integrate. Microsoft Learn draws its relationship patterns from Evans and later practitioners.

In Customer-Supplier, one upstream context serves another and the two teams negotiate the contract between them. Open Host Service with Published Language has the upstream context expose a defined API in a shared format. With an Anti-corruption Layer, the downstream team builds a translation layer to protect its own model. Separate Ways means no integration at all, and each context evolves alone.

The anti-corruption layer matters most around legacy systems. A convoluted schema or outdated API from an external system leaks into the new design unless a boundary blocks it.

Tactical building blocks inside a bounded context

YouTube player

Inside a bounded context, tactical design works with entities, value objects, aggregates, domain services, domain events, repositories and factories.

According to the Pearson contents listing, Part II of Evans’s book is titled The Building Blocks of a Model-Driven Design. Each block is a narrow software design pattern with one job.

Entity vs value object

AttributeEntityValue object
IdentityUnique, persists over timeNone, defined by its values
ChangeState changes, identity staysImmutable, replaced by a new instance
EqualitySame identifier, same conceptSame values, interchangeable
ExampleCustomer, bank accountDate, currency amount, color

Microsoft Learn’s guidance (February 2026 revision) is to default to value objects and promote a concept to an entity only when its identity must be tracked over time.

An address is usually a value object. It becomes an entity when the domain must track a specific address record, for audit purposes, say.

Entities should carry behavior too. If an entity holds only data and its rules sit in service classes, you have an anemic domain model, which Microsoft Learn calls an antipattern.

Aggregates and invariants

Martin Fowler’s April 2013 bliki entry defines an aggregate as a cluster of domain objects treated as one unit, such as an order together with its line items.

Outside code references only the aggregate root, and the root protects the integrity of the whole cluster. Transactions should not cross aggregate boundaries.

Microsoft Learn’s design rules for aggregates start with keeping them small, so include only data that must stay consistent within one transaction. Other aggregates get referenced by identity. A Delivery stores a DroneId and a PackageId, not the objects themselves. Across aggregates, eventual consistency applies, with domain events carrying changes from one aggregate to the next.

Size is where this usually goes wrong. Merge Delivery, Package, Drone and Account into one aggregate and unrelated updates compete for the same locks.

Services, events, repositories and factories

Some logic fits no single entity, and that goes in a domain service, which is stateless. The Scheduler in Microsoft’s drone scenario qualifies, because drone availability, delivery windows and route optimization span several entities.

Application services are different. They orchestrate use cases and hold no business rules.

A domain event records a domain-significant change. “A delivery was canceled” qualifies, while “a record was inserted” does not.

Aggregates raise events after a state change. Internal events stay inside the context, and integration events reach other contexts through a message broker.

Repositories load and save whole aggregates, since Fowler’s entry treats the aggregate as the unit of storage transfer. That keeps storage details out of the domain model.

Factories encapsulate the creation logic for complex objects, particularly aggregates, so a new aggregate starts in a valid state.

How do teams discover the domain model?

YouTube player

Teams run collaborative workshops where domain experts and developers map the business process before any code exists. EventStorming and Domain Storytelling are the two best-documented formats.

Microsoft Learn adds that no formal method is required, because a sketch on a whiteboard works too. The goal is the same either way, shared understanding of the domain before any technology choices.

MethodFormatMain output
EventStormingDomain events on sticky notes along a timelineShared knowledge, emerging bounded contexts
Domain StorytellingExperts tell stories, recorded in pictographsDomain stories that feed the ubiquitous language
Whiteboard sketchInformal map of business functionsSubdomains and their dependencies

EventStorming

Sources disagree on when the method started.

Wikipedia’s reference list dates Alberto Brandolini’s “Introducing Event Storming” post to 18 November 2013, and eventstorming.com also says he came up with the idea in 2013, while Lucid’s explainer says he created the method in 2012. The earlier date may reflect workshop use before the first write-up.

The Open Group’s O-AA standard describes the format. Participants place domain events on sticky notes along a timeline, with the aim of maximizing what every participant learns.

Domain events go on orange sticky notes and actors on small yellow ones (DDD Crew glossary). The output exposes emerging bounded contexts and seeds the ubiquitous language. Brandolini also teaches an EventStorming masterclass at Domain-Driven Design Europe, listed in its 2024, 2025 and 2026 programs.

Domain Storytelling

Stefan Hofer and Henning Schwentner came up with Domain Storytelling. Domain experts tell stories about their tasks while a moderator records them in a pictographic language of actors, work objects, activities and sequence numbers.

The book, Domain Storytelling, was published by Addison-Wesley on 7 September 2021 in Vaughn Vernon’s Signature Series (Pearson).

Experts see right away whether their story was understood correctly, which is the method’s main draw according to the DDD Academy course page.

When the right people are missing

A workshop with only developers produces developer assumptions.

Wikipedia’s entry on the method says it needs people who know the questions to ask (typically developers) and people who know the answers (domain experts, product owners). Airbrake’s overview lists the need for outside domain experts as a cost of the approach.

Domain-driven design alongside microservices, hexagonal architecture and CQRS

DDD settles where the boundaries and models go. How the resulting pieces get deployed, isolated and queried is a separate call, and microservices, hexagonal architecture and CQRS are options for it, not requirements.

ApproachWhat it decidesRelationship to DDD
MicroservicesDeployment unitsA bounded context is a service candidate
Hexagonal and clean architectureDirection of dependenciesDomain model sits in the core
CQRSSeparate models for writes and readsApplied inside one bounded context
Event sourcingState stored as a sequence of eventsFits domain events, never mandatory

Microservices

YouTube player

Microsoft Learn’s guidance on identifying microservice boundaries starts from the domain model, not from infrastructure.

In general, a service should not span more than one bounded context, and aggregates are often good candidates for each microservice because their boundaries come from business requirements. They also tend to have high functional cohesion and a boundary of persistence, and they stay loosely coupled to other aggregates.

On sizing, a service should be no smaller than an aggregate and no larger than a bounded context (Microsoft Learn, tactical DDD guidance, February 2026 revision).

Evans adds a caution from his 2017 DDD Europe talk, as reported by InfoQ. A microservice can be a good bounded context, but a service is not automatically one.

Chatty calls between two services signal that their functions belong together, according to the same Microsoft page. When in doubt, start coarse-grained, because splitting one service is easier than moving functionality across several.

Team structure breaks the mapping too. If one team owns several unrelated bounded contexts, or one context needs coordination across many teams, Microsoft Learn says to revisit the boundaries or the team structure.

Hexagonal and clean architecture

Alistair Cockburn described hexagonal architecture in 2005, and a figure caption in his own book on the pattern says Ports and Adapters specifies only two layers, inside and outside.

That leaves the inside of a hexagonal architecture undefined, and domain-driven design fills it with entities, aggregates and domain services.

Robert C. Martin’s post of 13 August 2012 groups hexagonal, onion and similar styles under one idea, layers with business rules at the center. His Dependency Rule says source code dependencies point only inward.

Evans’s own layering follows the same shape. Fowler’s 2003 anemic-model entry quotes him keeping the application layer thin, coordinating tasks only, with business rules held in the domain layer.

None of these styles tells you how to split a large system into contexts. That stays with strategic design.

CQRS and event sourcing

Martin Fowler’s July 2011 bliki entry on CQRS opens with a warning: for most systems, the pattern adds risky complexity.

CQRS, Greg Young’s Command Query Responsibility Segregation, uses separate models for updates (commands) and display (queries). It pays off in a few complex domains, and where reads and writes need to scale apart. It belongs to a specific bounded context, not the whole system.

Event sourcing (Fowler’s December 2005 write-up, which he labels draft material) stores every state change as an event, in the order applied. That gives a complete rebuild, temporal queries and event replay. It gets tricky around external systems, which need gateways that behave differently during replay.

Fowler reports that most CQRS cases he has run into went badly. Start without either pattern and add one only where a single bounded context shows a concrete need.

Is domain-driven design worth the cost?

It pays off when the business rules are the hard part of the system. It doesn’t when the hard part is moving data in and out of a database.

Martin Fowler’s 2020 bliki entry places the approach in complex domains where a lot of messy logic needs organizing.

Benefits

  • Models reflect business intent, which means fewer misunderstandings (Microsoft Learn).
  • Fowler credits DDD for building models in software and evolving them over the product’s life, not drawing them once on paper.
  • Service boundaries come from the domain, not from org charts or technology choices (Microsoft Learn).
  • Evans advises concentrating on the core domain and not recreating existing capabilities in generic and supporting subdomains.

Costs

Fowler’s 2020 entry calls Evans’s original book a hard read with a reputation to match, though he says it repays the effort.

Beyond the book, there’s a large pattern vocabulary to learn before the first model feels natural. Upfront modeling work has a limit too, since Evans warns that polishing a model until it is perfect means no software ships (InfoQ, 2017).

Then come the translation layers around legacy systems. Evans says they belong in budgets and plans because an anti-corruption layer is significant software in its own right (Domain Language white paper, 2013).

A quick test before committing

Ask which part of the system is hard.

If the rules are intricate and messy, model them. If the system mostly stores and retrieves records, a CRUD design is the cheaper answer.

Then ask where the effort goes. Spend it on the core domain and reuse what already exists for the rest.

When domain-driven design does not apply or goes wrong

YouTube player

Simple CRUD systems don’t need it. Elsewhere it tends to go wrong through anemic models nobody chose on purpose, a hunt for the perfect model, or a head-on start inside a legacy system.

Simple CRUD and low-complexity domains

Many systems fit a CRUD mental model, and Fowler’s 2011 CQRS entry says they should be built that way.

His 2005 event sourcing draft makes the same split in code. Transaction Scripts suit simple logic, and a domain model pays off when logic gets more complicated.

The main cost Fowler names in his 2003 anemic-model entry is the object-relational mapping layer. It only repays itself when object-oriented techniques organize complex logic.

The anemic domain model dispute

YouTube player

Sources disagree on whether an anemic domain model is a mistake.

Fowler’s entry of 25 November 2003 calls it an anti-pattern, saying anemic models carry every cost of a domain model and none of the benefits. Logic parked in services is really a procedural design, and he and Evans had noticed the trend growing.

Not everyone agrees. A February 2014 post on a University of Edinburgh informatics course blog argues the anemic model is a SOLID design, and a 2019 codecentric post calls it a valid tool while still treating an anemic start as a smell. DevIQ notes that anemic models are expected in a functional approach, where immutable records carry data and functions carry behavior.

It comes down to whether behavior has to live next to data for the result to count as a domain model. Object-oriented sources say yes, while functional and pragmatic ones say no.

For object-oriented codebases with intricate rules, keep validation and state transitions in the entities. Elsewhere an anemic model is defensible, and moving behavior into entities later is ordinary code refactoring.

Perfectionism and legacy systems

Evans told the 2017 DDD Europe audience that the approach is not for perfectionists. Waiting for a perfect model means nothing ships, and hacking out something poor is the opposite failure.

He also said a leaking or confusing abstraction should be dropped, because plain if-then-else logic beats an illusion of elegance.

Legacy systems add a second trap. Evans’s 2013 white paper opens by saying that attempts to apply DDD tactics inside a legacy system almost always disappoint, since the legacy model is implicit and seldom changeable with reasonable effort.

He also advises against introducing DDD through a legacy replacement project. Such efforts are high risk even for a team that has mastered the techniques.

How to start applying domain-driven design

Start with one core subdomain and a small team that has access to a domain expert. Model it inside a single bounded context, then derive services from the model, never the other way around.

  1. Choose one core subdomain with real intricacy, not the whole system.
  2. Gather the people who know the answers and run a discovery workshop.
  3. Draw the bounded context around that subdomain and list its neighbors on a context map.
  4. Model small aggregates inside the context, with rules held in the entities.
  5. Put an anti-corruption layer between the new model and any legacy system it reads from.
  6. Derive services from the model only after that, starting coarse-grained.
  7. Review the boundaries as the model changes.

Evans’s white paper describes the first pilot in similar terms: an important but modest-sized problem with some intricacy, and a small, self-disciplined team that controls its own code.

Microsoft Learn calls the whole effort an iterative process, so treat every boundary as a draft.

Starting inside a legacy system

Evans’s 2013 white paper lists strategies for starting when legacy systems dominate. Its introduction says three, but its contents list four.

StrategyMain traitData
Bubble contextModest commitment to DDDRead from the legacy database through the anti-corruption layer
Autonomous bubbleNeeds an organization committed to significant new developmentOwn data store, synchronized by an anti-corruption layer
Exposing legacy assets as servicesOpen-host service over an anti-corruption layerBacked on demand or synchronized
Expanding a bubbleGrows an existing bubble feature by featureOnly data the new model uses

The bubble context is the lowest-risk start. Work inside one can run for several months or, when the interface to the legacy context is narrow, a few years.

Most bubbles are short-lived, according to the same paper. Holes appear in the anti-corruption layer or interest fades, the bubble bursts, and the functionality lives on as an extension of the legacy system.

The paper also has a rule about data. Bring in only what the new model uses, because passing legacy fields through untranslated bores a tunnel through the boundary.

Domain-Driven Design FAQ

Which books teach domain-driven design?

YouTube player

Start with Eric Evans’s Domain-Driven Design: Tackling Complexity in the Heart of Software. Martin Fowler says it has a reputation as a hard read but repays the effort, and he names Vaughn Vernon’s Implementing Domain-Driven Design as the next step.

Domain Storytelling by Hofer and Schwentner covers discovery workshops.

Does domain-driven design require a specific language or framework?

No. Martin Fowler describes the core notions as conceptual, so they apply with any programming approach.

He singles out strategic design as the part where that is especially true, and notes that Evans’s own background is in the object-oriented community.

Is domain-driven design compatible with agile teams?

Yes. Fowler notes that Evans is a strong proponent of Extreme Programming and sees domain-driven design as a natural part of it, a view most XP practitioners he knows share.

The fit makes sense because the model is built in software and evolves over the product’s life.

Who should be in the room when the domain model is built?

Developers and domain experts together. Evans’s legacy white paper asks for a small, self-disciplined team that controls its own code, working on a modest but intricate business problem.

The experts supply the terms and rules, and the developers turn them into the model.

Keeping a Domain Model Accurate Over Time

Domain-driven design keeps a domain model accurate by treating every model and boundary as a draft that gets reviewed whenever the business changes. The review goes in order. Terms in meetings get compared against names in code, then bounded contexts against team structure, and anti-corruption layers against both.

Language comes first because a term that means different things in conversation and in code is the cheapest drift to spot, and it warns of the others.

Boundaries rank second. In Evans’s 2017 example, a business reorganized around market segments while its credit card and cash account contexts stayed the same, so each new team worked in both contexts and had to coordinate.

The price of this routine is a standing review after every reorganization. Checked against the cited sources on 30 October 2026, the order holds until new guidance from Evans or Microsoft Learn on context boundaries changes it.

Bogdan Sandu

Stay sharp. Ship better code.

Every week: one curated article, one tool worth knowing, one tip you can use tomorrow. No noise, no padding.