Event Sourcing & CQRS

📘 Chapter 14: Data Pipelines & Stream Processing ⏱️ 9 min read 🏗️ Lesson 059

Traditional databases store current state — when you update a row, the previous value is gone. Event sourcing flips this: store every change as an immutable event, and derive current state by replaying events. Combined with CQRS, this creates powerful architectures for audit-heavy, complex domains.

Traditional vs Event-Sourced

Traditional State vs Event Sourcing Traditional: Mutable State UPDATE orders SET status='shipped' WHERE id=42 Previous state is LOST forever orders table id=42, status="shipped" ← only latest Event Sourcing: Append-Only Log OrderCreated {id:42, items:[...], total:99} PaymentReceived {id:42, amount:99} OrderShipped {id:42, tracking:"XY123"} DeliveryConfirmed {id:42, signed:"J.Doe"} Every state change preserved as a fact replay Current State (derived) status="delivered" ❌ Can't answer: "When was it shipped?" ✓ Full history: who, what, when, why
Figure 1: Traditional databases overwrite state; event sourcing keeps every change as an immutable event.

Benefits of Event Sourcing

  • Complete audit trail: Every change is recorded — who did what, when
  • Temporal queries: "What was the account balance at 3pm yesterday?"
  • Replay for debugging: Reproduce any bug by replaying events up to that point
  • Event-driven integration: Other services subscribe to events naturally
  • Undo/redo: Apply compensating events instead of destructive rollbacks

Challenges

  • Event schema evolution: What happens when event format changes? Need versioning strategy
  • Eventual consistency: Read models may lag behind write model
  • Storage growth: Events accumulate forever — need snapshotting for performance
  • Complexity: Simple CRUD becomes much more involved
  • Querying: Can't easily query current state without projections

CQRS: Command Query Responsibility Segregation

CQRS separates writes (commands) from reads (queries) into different models, optimized for their distinct access patterns.

CQRS Architecture Commands Create, Update Write Model Domain logic Validation Event Store Append-only Source of truth publish Projections Transform events Read Store Denormalized Queries Fast reads Write path optimized for consistency | Read path optimized for speed
Figure 2: CQRS separates writes (commands → events) from reads (projections → optimized views).

When to Use ES + CQRS

Good Fit Overkill
Finance — full transaction audit trail Simple CRUD blog or CMS
Healthcare — regulatory compliance Low-traffic internal tools
E-commerce — complex order workflows Prototypes and MVPs
Gaming — replay systems Read-heavy, rarely-changing data
Collaborative editing — conflict resolution Systems where history doesn't matter

Real-World Examples

Banking: Complete Transaction History

A bank's ledger is inherently event-sourced. Every transaction is an immutable fact:

  • Deposited $500Withdrew $200Transferred $100 to savings
  • Balance is derived by replaying all events: $500 - $200 - $100 = $200
  • Regulators can audit every cent that ever moved — nothing is overwritten
  • If a bug miscalculated interest, replay events with the fix to correct all balances

Banks never "UPDATE balance = 200" — they append a new event and derive state. This is event sourcing by regulation.

Gaming: Player Action Replays

Multiplayer games like StarCraft and Fortnite store every player action as an event:

  • Move, attack, build, resource-gather — all timestamped events
  • Replays work by replaying the event stream through the game engine
  • Anti-cheat systems replay suspicious matches to verify legitimacy
  • Balance patches can be tested by replaying old matches with new rules

Interactive: Traditional vs Event-Sourced

Order Lifecycle Comparison

Watch how a traditional DB and an event-sourced system handle the same order flow.

Traditional DB

Event Store