Domain-Driven Design (DDD)
DDD is the language microservices are written in. It answers: how do you draw service boundaries?
Why DDD Matters for Microservices
The hardest part of microservices is not the technology — it's knowing where to cut. DDD provides:
- A vocabulary shared by developers and domain experts
- A principled way to identify service boundaries
- Building blocks for rich domain models
Strategic DDD — The Big Picture
Bounded Context
The single most important concept. A bounded context is an explicit boundary within which a domain model is valid and consistent. The word "Customer" means something different in Sales vs Billing — that's two bounded contexts.
- Each microservice typically maps to one bounded context
- Boundaries enforced via explicit APIs and event contracts
- Teams own bounded contexts; one team per context is the goal
Ubiquitous Language
A shared vocabulary used consistently by developers and domain experts, within a bounded context. No translation needed inside the context; translation happens at boundaries.
Subdomains
| Type | Description | Strategy |
|---|---|---|
| Core | Competitive advantage; what makes you unique | Build it yourself; invest deeply |
| Supporting | Necessary but not differentiating | Build or buy; less investment |
| Generic | Commodity functionality | Buy SaaS or use open source |
graph TD
A[Problem Space] --> B[Core Subdomain - Order Matching]
A --> C[Supporting Subdomain - Inventory]
A --> D[Generic Subdomain - Email / Auth]
B --> E[Order Service - You build]
C --> F[Inventory Service - You build]
D --> G[Identity Provider - SaaS]
Context Mapping — How Bounded Contexts Relate
graph LR
ORD[Order Context] --> SHP[Shipping Context]
PAY[Payment Context] --> ORD
ID[Identity Context] --> ORD
ID --> PAY
ORD -->|ACL| LEG[Legacy ERP]
| Pattern | Relationship | Use When |
|---|---|---|
| Shared Kernel | Two teams share a domain model subset; changes require joint agreement | Transitioning; keep small |
| Customer / Supplier | Upstream shapes what downstream gets; downstream can negotiate | Clear producer-consumer relationship |
| Conformist | Downstream conforms to upstream with no negotiation power | Third-party APIs you can't influence |
| Anti-Corruption Layer (ACL) | Translation layer protecting your model from external/legacy systems | Integrating with legacy; strangling monolith |
| Open Host Service | Upstream publishes a well-defined protocol multiple consumers can use | Shared platform services |
| Published Language | Well-documented shared schema (Avro, JSON Schema, Protobuf) | Event-driven integration contracts |
| Separate Ways | No integration; teams solve problems independently | When integration cost exceeds value |
Tactical DDD — Building Blocks
| Building Block | Description | Example |
|---|---|---|
| Entity | Has a unique identity; mutable over time | Order identified by orderId |
| Value Object | No identity; defined entirely by attributes; immutable | Money(amount, currency), Address |
| Aggregate | Cluster of entities and value objects; one Aggregate Root controls all access | Order root containing OrderLine entities |
| Domain Event | Immutable fact: something happened in the domain | OrderPlaced, PaymentFailed |
| Domain Service | Logic that doesn't belong to any single entity | PricingService, TaxCalculator |
| Repository | Abstraction for persisting and loading aggregates | OrderRepository.save(order) |
| Factory | Encapsulates complex object creation | OrderFactory.createFromCart(cart) |
| Application Service | Orchestrates use cases; thin layer above the domain | OrderApplicationService.placeOrder(cmd) |
Aggregate Design — Rules of Thumb
graph TD
subgraph Order Aggregate
A[Order Root] --> B[OrderLine Entity]
A --> C[ShippingAddress Value Object]
A --> D[OrderStatus Value Object]
end
A -->|id only| E[CustomerId]
A -->|id only| F[ProductId]
- Model aggregates around invariants (business rules that must always hold)
- Keep aggregates small — include only what must change atomically together
- Reference other aggregates by ID only, never by object reference
- One transaction = one aggregate — cross-aggregate changes use domain events
- Cross-aggregate consistency is eventual, coordinated by events
DDD and Microservices — The Alignment
| DDD Concept | Microservices Equivalent |
|---|---|
| Bounded Context | Microservice boundary |
| Ubiquitous Language | Service API vocabulary |
| Domain Event | Integration event published to Kafka |
| Aggregate | Unit of transactional consistency |
| Anti-Corruption Layer | Adapter translating external API |
| Context Map | Service dependency diagram |
The common mistake
Decomposing by technical layer (UserController, UserService, UserRepo as separate services) instead of by business capability. DDD prevents this.
→ Deep Dive: Strategic DDD — Bounded contexts, ubiquitous language, subdomains in depth
→ Deep Dive: Context Mapping — ACL, Shared Kernel, Customer-Supplier relationships
→ Deep Dive: Tactical Building Blocks — Entities, Value Objects, Domain Events, Repositories
→ Deep Dive: Aggregate Design — Invariants, sizing aggregates, cross-aggregate consistency
→ Deep Dive: DDD and Microservices Alignment — Mapping DDD to service boundaries