System Design
Event Sourcing and CQRS
Event Sourcing: the basics
**Event Sourcing means storing the changes, not the state** Traditional approach: store the current state of an object. Event Sourcing: store the sequence of events that produced that state. Analogy: - A regular database is a photo (current snapshot) - An Event Store is a video (the full history)
**Core principles of Event Sourcing:** 1. **Events are immutable** - never modified, never deleted 2. **Events are the source of truth** - the database is secondary 3. **State is derived** - current state = replay of all events 4. **Append-only** - the store only accepts new events
**Why Event Sourcing pays off:** | Benefit
| **Full audit** | Every change is recorded forever | | **Time travel** | Reconstruct state at any past moment | | **Debug** | Replay events to reproduce bugs | | **Analytics** | Full history available for ML/BI | | **Event-driven** | Natural fit with microservices |
What did you learn about the basics of Event Sourcing?
Event Store
**Event Store - a specialised event storage** Requirements: - Append-only writes - Ordered reads per aggregate - Optimistic concurrency control - High write throughput Options: EventStoreDB, Kafka, PostgreSQL, DynamoDB
**Optimistic concurrency - the standard way to handle conflicts**
On conflict: reload the aggregate, retry the command.
**Snapshotting - load-time optimisation** Problem: replaying an aggregate with 10,000 events is slow. Fix: periodically save a snapshot of the state.
What did you learn about the Event Store?
Projections
**Projections - read models built from events** The Event Sourcing problem: to answer 'all orders for this customer' you have to: - Load ALL events - Filter by customer - That is slow! Fix: Projections - precomputed views over the events.
**Projection Handler:**
**Checkpoint - tracking position**
What did you learn about Projections?
CQRS Pattern
**CQRS - Command Query Responsibility Segregation** Separate models: - **Command Model** (write) - handles commands, emits events - **Query Model** (read) - optimised for queries CQRS is often paired with Event Sourcing, but the two are independent.
**CQRS without Event Sourcing:**
Options: - PostgreSQL read replicas - Materialized views - Elasticsearch for search - Redis for caching
**Eventual consistency in CQRS** Problem: there is a delay between the write and the read model update.
Fixes: 1. **Read-your-writes** - after a write, read from the write model 2. **Polling** - the UI refreshes on a timer 3. **WebSocket** - push when the projection updates 4. **Version in the response** - the UI waits for the version it needs
What did you learn about the CQRS Pattern?
Trade-offs and when to use it
**When to reach for Event Sourcing:** Good fit: - Financial systems (audit, compliance) - Systems with complex domain logic - Need for a complete audit trail - Temporal queries ('state as of date X') - Event-driven architectures Bad fit: - Simple CRUD applications - Hot rows with constant updates (event flood) - Heavy ad-hoc queries against the current state - A team that has not used the pattern before
**Approach comparison:** | Aspect | CRUD
| **Complexity** | Simple | High | | **Audit** | Built separately | Built in | | **Scaling** | Medium | Excellent | | **Consistency** | Strong | Eventual | | **Time travel** | Impossible | Built in | | **Debugging** | Straightforward | Event replay | | **Schema changes** | Migration | Event versioning |
What did you learn about the trade-offs and when to use Event Sourcing?