Skip to content

CQRS & Event Sourcing

CQRS separates commands (writes) from queries (reads). Event Sourcing stores every state change as an event — the current state is the sum of all past events.


flowchart LR
User["👤 User"] --> Write["✏️ Command Side<br/>(Write Model)"]
User --> Read["📖 Query Side<br/>(Read Model)"]
Write --> WriteDB[("Write DB<br/>(Normalized)")]
WriteDB -->|"Sync / Event"| ReadDB[("Read DB<br/>(Denormalized)")]
Read --> ReadDB
style User fill:#7c3aed,color:#fff
style Write fill:#4f46e5,color:#fff
style Read fill:#6366f1,color:#fff
style WriteDB fill:#059669,color:#fff
style ReadDB fill:#059669,color:#fff

AspectTraditional CRUDCQRS
ModelOne model for read & writeSeparate models
SchemaSame schema for bothDifferent schemas (write: normalized, read: denormalized)
ComplexitySimpleHigher
PerformanceMedium (one size fits none)⚡ Optimized for each operation
Best forSimple appsComplex domains, high-read/write asymmetry

Instead of storing “current balance = $100”, store every event:

Account 123 Events:
1. AccountCreated($0)
2. Deposit($50) → balance = $50
3. Withdraw($20) → balance = $30
4. Deposit($70) → balance = $100 ← current

To get the current state: replay all events in order.

Benefits:

  • ✅ Full audit trail — every change is recorded
  • ✅ Time travel — reconstruct state at any point
  • ✅ Debugging — exactly what happened, in order

Costs:

  • ❌ Event store grows forever (need compaction)
  • ❌ Replaying events can be slow
  • ❌ Schema evolution — old events need to be handled

ScenarioWhy This Combo?
Audit trail required (banking, compliance)Every transaction is an event
Read and write patterns are very differentOptimize each separately
Complex business rulesCommands can validate before writing
Need time travel / debuggingReplay events to understand failures

ScenarioWhy Not?
Simple CRUD appOverkill — adds massive complexity
No audit requirementMore complexity than benefit
Small teamToo much to build and maintain

  • CQRS is powerful but doubles your data storage (write DB + read DB).
  • Event Sourcing gives you a perfect audit trail but the event store grows forever.
  • Don’t start with CQRS/Event Sourcing. Add them when your CRUD patterns genuinely hurt.

  • CQRS = separate “write this” from “read that.” Each can be optimized differently.
  • Event Sourcing = instead of saving the current state, save every change (event) in order.
  • Together they’re powerful for complex systems but overkill for simple CRUD apps.