Domain-Driven Design Explained: Modeling Software Around Business Domains

Learn domain-driven design (DDD) fundamentals — bounded contexts, aggregates, ubiquitous language, and how DDD shapes microservices boundaries.

domain-driven-designdddarchitecturebounded-contextsystem-design

Domain-Driven Design

Domain-driven design (DDD) is an approach to software development that centers the design and architecture around the core business domain, using a shared language between engineers and domain experts to model complex systems.

What It Really Means

Most software problems are not technology problems — they are understanding problems. The code does not match how the business actually works. Engineers use technical terms ("entities," "controllers," "repositories") while business people use domain terms ("policy," "claim," "underwriting"). DDD bridges this gap.

The central idea is ubiquitous language: a shared vocabulary used by both developers and domain experts in code, documentation, and conversation. If the insurance business says "policy," the code has a Policy class, not an InsuranceProduct or Contract. If the business says "bind a policy," the code has policy.bind(), not policy.activate() or policy.setStatus('active'). The code reads like the domain.

DDD was introduced by Eric Evans in his 2003 book. It provides both strategic patterns (how to decompose large systems into bounded contexts) and tactical patterns (how to model domain logic within a context). The strategic patterns are particularly relevant for microservices architecture, where bounded contexts map naturally to service boundaries.

DDD is not for every project. It is most valuable for complex domains where the business logic is the hard part — insurance, healthcare, finance, logistics. For simple CRUD applications, DDD adds unnecessary ceremony.

How It Works in Practice

Strategic Patterns

Bounded Context: A boundary within which a specific domain model is defined and consistent. In an e-commerce system, "Product" means different things in different contexts:

  • Catalog context: Product has a name, description, images, and categories
  • Inventory context: Product has SKU, quantity, warehouse location
  • Pricing context: Product has base price, discount rules, tax category

Each bounded context has its own model of Product, optimized for its specific purpose. Trying to create one universal Product model that serves all contexts leads to a bloated, confusing entity.

Context Mapping: How bounded contexts interact. Common relationships:

  • Customer/Supplier: One context provides data that another consumes
  • Shared Kernel: Two contexts share a small subset of the domain model
  • Anti-Corruption Layer: A translation layer that protects one context from another's model
  • Published Language: A well-defined, versioned interface between contexts

Tactical Patterns

Entity: An object defined by its identity, not its attributes. Two customers with the same name are different customers because they have different IDs.

Value Object: An object defined by its attributes, not its identity. Two Money(100, "USD") objects are equal regardless of where they came from. Value objects are immutable.

Aggregate: A cluster of entities and value objects treated as a single unit for data consistency. An Order aggregate contains OrderLines, ShippingAddress, and PaymentInfo. All changes go through the aggregate root (Order), which enforces business invariants.

Domain Event: A record of something that happened in the domain. OrderPlaced, PaymentReceived, ShipmentDispatched. Domain events are the natural bridge to event-driven architecture.

Repository: An abstraction over data access. Retrieves and persists aggregates. The domain layer defines the repository interface; the infrastructure layer implements it (see hexagonal architecture).

Real-World Example: Insurance Domain

Implementation

python
python
python

Trade-offs

When to Use DDD

  • Complex business domains where the logic is the hard part (insurance, finance, logistics)
  • Large teams that need a shared understanding of the domain
  • Systems where bounded contexts map to team boundaries and microservices
  • Long-lived products where domain understanding deepens over time

When NOT to Use DDD

  • Simple CRUD applications with little business logic
  • Prototypes and MVPs where speed is more important than domain modeling
  • Technical infrastructure projects (message brokers, caches) with no business domain
  • Small teams where everyone already understands the domain intuitively

Advantages

  • Code reflects business reality — easier for new team members to understand
  • Bounded contexts provide natural microservice boundaries
  • Business rule changes map clearly to code changes
  • Ubiquitous language reduces miscommunication between engineers and stakeholders

Disadvantages

  • Significant upfront investment in domain understanding
  • Requires access to domain experts — engineers cannot do DDD alone
  • Over-engineering risk for simple domains
  • Tactical patterns (aggregates, value objects) add complexity

Common Misconceptions

  • "DDD is about the tactical patterns" — Most DDD value comes from the strategic patterns: bounded contexts, ubiquitous language, and context mapping. The tactical patterns (aggregates, entities, value objects) are implementation details. Teams that focus only on tactical patterns miss the point.

  • "Every microservice is a bounded context" — Not necessarily. A bounded context may contain multiple services, or a service may implement part of a bounded context. The mapping is informed by domain analysis, not by arbitrary decomposition.

  • "DDD means no database thinking" — DDD does not ignore persistence. Aggregates define transaction boundaries — each aggregate is loaded and saved as a unit. This has direct implications for database schema design.

  • "You need to read the Evans book before starting" — You can start with the core principles (ubiquitous language, bounded contexts) and add tactical patterns incrementally. Full DDD adoption is a spectrum, not a binary.

  • "DDD is only for object-oriented languages" — DDD principles apply to functional programming too. Aggregates become data structures with pure functions, and domain events remain central. The modeling approach transcends language paradigms.

How This Appears in Interviews

DDD concepts frequently appear in system design interviews:

  • "How would you decompose this system into services?" — Use bounded context analysis. Identify distinct subdomains, their languages, and their relationships. See our system design interview guide.
  • "What are the boundaries of this aggregate?" — Discuss consistency boundaries, transaction scope, and what data must change together atomically.
  • "How do services share data without tight coupling?" — Describe domain events, anti-corruption layers, and context mapping patterns.
  • Practice with our DDD interview questions.

Related Concepts

GO DEEPER

Learn from senior engineers in our 12-week cohort

Our Advanced System Design cohort covers this and 11 other deep-dive topics with live sessions, assignments, and expert feedback.