Event Sourcing
Store every state change as an immutable event: event stores, projections, snapshots, replay, and the relationship between event sourcing and CQRS.
The Core Idea
Traditional systems store only the current state of an entity — update a row in the database and the previous state is gone. Event Sourcing inverts this: instead of storing current state, you store an ordered log of every event that ever happened. Current state is derived by replaying events from the beginning.
Think of your bank account. The balance is not stored directly — it is calculated by replaying all deposits, withdrawals, and transfers. Git is another example: the current state of your codebase is derived from the history of commits. Event sourcing brings this same auditability and time-travel capability to your application data.
Key Components
| Component | Role | Example |
|---|---|---|
| Event | Immutable fact describing something that happened | `OrderPlaced`, `PaymentFailed` |
| Event Store | Append-only log of events per aggregate | EventStoreDB, Kafka, DynamoDB streams |
| Aggregate | Domain entity that produces and handles events | `Order`, `Account`, `Inventory` |
| Projection | Derived read model built by replaying events | SQL view, Redis hash, Elasticsearch doc |
| Snapshot | Checkpoint of aggregate state to speed up replay | Stored every N events |
| Command Handler | Validates input, loads aggregate, applies events | Domain service layer |
The Event Sourcing Flow
Snapshots
If an aggregate has thousands of events, replaying from the beginning on every command becomes slow. Snapshots solve this: periodically serialize the aggregate state and store it alongside a version number. On the next load, fetch the latest snapshot and only replay events after that snapshot's version.
// Pseudo-code: loading an aggregate with snapshot optimization
async function loadOrder(orderId: string): Promise<Order> {
// 1. Try to load the latest snapshot
const snapshot = await eventStore.getSnapshot(orderId);
let order: Order;
let fromVersion: number;
if (snapshot) {
order = Order.fromSnapshot(snapshot.state);
fromVersion = snapshot.version + 1;
} else {
order = new Order();
fromVersion = 0;
}
// 2. Replay only events AFTER the snapshot
const events = await eventStore.getEvents(orderId, fromVersion);
for (const event of events) {
order.apply(event);
}
return order;
}Event Sourcing + CQRS
Event sourcing pairs naturally with CQRS. The write side uses the event store as its source of truth. Every appended event flows into projectors that maintain denormalized read models in whatever shape queries need. Different projectors can build completely different views of the same events — an order might power an `order_summary` table in Postgres, an Elasticsearch document for search, and a Redis sorted set for a delivery dashboard.
Event Versioning Is Critical
Events are immutable and may be replayed years later. When your domain evolves, you cannot change old events. Instead, version them: `OrderPlaced_v1`, `OrderPlaced_v2`. Upcasters transform old event versions to new formats during replay. Plan your event schema evolution strategy early — it is much harder to retrofit.
Benefits and Costs
| Benefit | Cost |
|---|---|
| Complete audit log — who changed what and when | Event store can grow very large without compaction |
| Time-travel debugging — replay to any point in time | Eventually consistent read models |
| Temporal queries — 'what was the state on date X?' | Steep learning curve for developers |
| Decoupled projections — add new read models without touching write logic | Event versioning and upcasting complexity |
| Natural integration events for downstream consumers | Replaying millions of events to recover state is slow without snapshots |
Real-World Examples
LinkedIn uses event sourcing for its activity feed — every action (like, share, comment) is an event appended to a stream. Walmart uses it for inventory management, enabling them to replay state for audit and dispute resolution. Axon Framework (Java) and EventStoreDB are popular production implementations. Apache Kafka is frequently used as an event store, though it lacks built-in aggregate-scoped streams and optimistic concurrency.
Interview Tip
When an interviewer asks about event sourcing, lead with the business benefits — audit trail, time travel, decoupled projections — before diving into implementation. Then discuss the trade-offs: eventual consistency, event versioning, and snapshot strategies. Mention that you would pair it with CQRS and use something like Kafka or EventStoreDB as the backing store. A common follow-up is 'how do you handle schema changes?' — answer with versioned events and upcasters.