CQRS & Event Sourcing: When Complexity Pays Off
Command Query Responsibility Segregation (CQRS) and Event Sourcing are often mentioned together but are independent patterns. Understanding when each adds value -- and when the overhead is not justified -- is the key architectural decision.
Traditional CRUD vs CQRS
| Dimension | Traditional CRUD | CQRS |
|---|---|---|
| Data model | Single model for reads and writes | Separate read and write models |
| Scalability | Scale everything together | Scale reads and writes independently |
| Consistency | Strong (same DB) | Eventually consistent (projection lag) |
| Complexity | Low | High (two models, sync mechanism) |
| Query flexibility | Limited by write schema | Read models optimized per use case |
| Audit trail | Requires additional logging | Native with event sourcing |
| Team structure | Single team per service | Read/write teams can work independently |
| Data storage cost | Lower | Higher (events + projections) |
Event Sourcing Architecture
Commands
│
┌──────▼──────┐
│ Command │
│ Handler │
└──────┬──────┘
│ validate + produce events
┌──────▼──────┐
│ Event │
│ Store │ (append-only log)
└──────┬──────┘
│ publish
┌────────────┼────────────┐
│ │ │
┌──────▼──┐ ┌─────▼────┐ ┌───▼────────┐
│ Read │ │ Read │ │ External │
│ Model A │ │ Model B │ │ Projector │
│ (list) │ │ (search) │ │ (analytics)│
└────┬────┘ └────┬─────┘ └────┬───────┘
│ │ │
┌────▼────┐ ┌────▼─────┐ ┌────▼───────┐
│ Query │ │ Query │ │ Query │
│ API A │ │ API B │ │ API C │
└─────────┘ └──────────┘ └────────────┘
When-to-Use Decision Matrix
| Criterion | Score 0 (skip) | Score 1 (consider) | Score 2 (strong fit) |
|---|---|---|---|
| Audit requirements | No audit needed | Audit log required | Full temporal queries |
| Read/write ratio | Balanced | 10:1 reads | 100:1+ reads |
| Domain complexity | Simple CRUD | Moderate workflows | Complex state machines |
| Team size | < 3 developers | 3-8 developers | 8+ developers |
| Query diversity | 1-2 query patterns | 3-5 query patterns | 6+ query patterns |
| Regulatory | None | Data lineage needed | Full replayability required |
Interpretation: 0-3 total = stick with CRUD, 4-7 = consider CQRS without event sourcing, 8-12 = CQRS + event sourcing justified.
Complexity vs Benefit Trade-off
| Pattern | Implementation Cost | Operational Cost | Debugging | Best For |
|---|---|---|---|---|
| Simple CRUD | Low | Low | Easy | Content management, settings |
| CQRS (no ES) | Medium | Medium | Moderate | High-read APIs, dashboards |
| Event Sourcing (no CQRS) | Medium-High | Medium | Hard | Audit-heavy domains |
| CQRS + ES | High | High | Very Hard | Finance, regulated, temporal |
Key Infrastructure Decisions
Event Store options: EventStoreDB (purpose-built), Apache Kafka (if already in stack), PostgreSQL with append-only tables (simple start), AWS DynamoDB Streams.
Projection rebuild strategy: You will need to rebuild projections. Design for it from day one. Track projection version, support parallel rebuilds, and test rebuild time under production data volumes.
Snapshotting: For aggregates with long event histories (1000+ events), periodic snapshots prevent slow aggregate loading. Balance snapshot frequency against storage cost.