DDD & Microservices
Microservices without DDD are a distributed monolith.
Domain-Driven Design defines proper service boundaries. Event Sourcing and CQRS solve consistency and performance. Result: architecture where teams work independently.
Why DDD before microservices¶
Most failed microservices architectures share a common problem: services are divided incorrectly. Auth service, notification service, user service — these are technical layers, not business domains. Result: distributed monolith where every change requires coordination of 3 teams.
DDD defines service boundaries according to business domains. Bounded context = service. Ubiquitous language = shared vocabulary between business and tech teams. Result: services that change independently because they correspond to independent business areas.
Event Storming — discovery in 2-3 days¶
Before code, you need a map. Event Storming is a workshop where domain experts and developers jointly map business processes:
- Big Picture (half day) — All business events on timeline. Identifying hot spots and problems.
- Process Modeling (1 day) — Detailed flow for key processes. Commands, events, policies, read models.
- Software Design (1 day) — Aggregates, bounded contexts, context mapping. Technical architecture from business model.
Output: system map understood by both CTO and product owner. Bounded contexts directly map to microservices.
Event Sourcing¶
For domains requiring complete history (finance, audit, compliance):
- State = sum of events. No UPDATE, only INSERT. Full reconstruction to any point in time.
- Audit trail for free — Every change is an event with context (who, what, when, why).
- Temporal queries — “What was the account state on 31.12.2024?” Trivial query.
- Event replay — Bug fix? Fix handler, replay events, state gets corrected.
When NOT Event Sourcing: CRUD domains, simple entities without business logic, data with high frequency changes (IoT telemetry). Event Sourcing adds complexity — we use it only where it brings value.
CQRS — separation of reads and writes¶
Command Query Responsibility Segregation:
- Write model — Optimized for business logic and consistency. Aggregates, invariants, validation.
- Read model — Optimized for queries. Denormalized views, materialized projections.
Example: Order system. Write model validates business rules (inventory, credit, limits). Read model provides denormalized view for dashboard (order + customer + products + delivery status in one query).
Anti-patterns¶
- Shared database — Services share database → tight coupling. Each service has its own store.
- Synchronous chains — A calls B, B calls C, C calls D. Cascade failure. Prefer asynchronous events.
- Nano-services — Too small services = more network calls, more failure points, more operational overhead.
- God service — Service that knows everything and does everything. Refactor to bounded contexts.
- Distributed transactions — 2-phase commit over network. Use Saga pattern.
Context Mapping¶
Relationships between bounded contexts:
- Partnership — Two teams collaborate, shared goal
- Customer-Supplier — Upstream supplies, downstream consumes
- Anti-Corruption Layer — Protection from foreign model (typically legacy integration)
- Published Language — Shared language for integration (API contracts)
Context map is a living document — updated with every architectural change.
Časté otázky
DDD makes sense for complex domains with rich business logic (finance, logistics, insurance). For CRUD applications or simple APIs it's overkill. We decide based on complexity assessment.
There's no magic number. We follow bounded contexts from the domain model. Typically 5-15 services for medium complexity systems. Less is more — nano-services bring more problems than they solve.
Asynchronously via Kafka (events) for most cases. Synchronously via gRPC/REST for queries where you need immediate response. Rule: commands asynchronously, queries synchronously.