What is Event Sourcing?
Learn what event sourcing is, how state is derived from an immutable event log, snapshots, and its relation to CQRS.
Expected Interview Answer
Event sourcing is a pattern where every change to application state is stored as an immutable sequence of events rather than overwriting a single row, and the current state is derived by replaying those events in order.
Instead of a table holding only the latest value of an entity, an event-sourced system appends facts like "OrderPlaced", "OrderShipped", and "OrderCancelled" to an append-only log, and the current state of an order is computed by folding all its events in sequence. This gives a complete audit trail for free, since nothing is ever deleted or overwritten, and it allows rebuilding any past state or new read projections simply by replaying the event stream. Because replaying a very long stream on every read is expensive, systems typically use snapshots to periodically checkpoint state and only replay events since the last snapshot. Event sourcing pairs naturally with CQRS, where the event stream is the write model’s source of truth and separate read projections are built from it, but the two remain distinct concepts.
- Provides a complete, immutable audit trail of every state change automatically
- Enables rebuilding any past state or brand-new read projections by replaying events
- Naturally supports temporal queries like “what did this look like last Tuesday”
- Decouples the source of truth (events) from however many derived views are needed
AI Mentor Explanation
Event sourcing is like a ball-by-ball commentary log of a cricket match instead of just a scoreboard showing the current total. Every delivery is recorded as an immutable event — "Ball 34.2: four runs", "Ball 34.3: wicket" — and the current score is simply the result of replaying every ball from the start. If you want to know the score after over 20, you replay events up to that point rather than storing a separate snapshot for every possible moment. That append-only, replayable log of facts is exactly what event sourcing does for application state.
Step-by-Step Explanation
Step 1
Command produces an event
A validated action (e.g., "ShipOrder") is translated into an immutable event describing what happened, not just the resulting state.
Step 2
Event is appended to the log
The event is written to an append-only event store; existing events are never edited or deleted.
Step 3
State is derived by replay
Current state for an entity is computed by folding (reducing) all its events in chronological order.
Step 4
Snapshots optimize replay
Periodic snapshots checkpoint the folded state so replay only needs events since the last snapshot, keeping reads fast.
What Interviewer Expects
- Explains that events are immutable facts, and state is derived by replaying them
- Mentions snapshots as the standard optimization for long event streams
- Notes the audit-trail and temporal-query benefits as first-class outcomes, not afterthoughts
- Distinguishes event sourcing from simply logging changes for debugging (events ARE the source of truth here)
Common Mistakes
- Treating event sourcing as identical to CQRS (they are complementary but separate patterns)
- Forgetting to mention snapshots, leading to concerns about unbounded replay cost
- Assuming events can be edited or deleted, breaking the immutability guarantee
- Underestimating the complexity of event schema evolution (versioning old events) over time
Best Answer (HR Friendly)
“Event sourcing means storing every change to data as a permanent, ordered event, like “order placed” or “item shipped”, instead of just keeping the latest value. The current state is figured out by replaying all those events in order, which gives you a full history for free and lets you rebuild past states whenever you need to.”
Code Example
async function shipOrder(orderId) {
await eventStore.append(orderId, {
type: "OrderShipped",
at: new Date().toISOString(),
})
}
function reduceOrderState(events) {
let state = { status: "created", items: [] }
for (const event of events) {
switch (event.type) {
case "OrderPlaced":
state = { ...state, status: "placed", items: event.items }
break
case "OrderShipped":
state = { ...state, status: "shipped" }
break
case "OrderCancelled":
state = { ...state, status: "cancelled" }
break
}
}
return state
}
async function getOrderState(orderId) {
const snapshot = await snapshotStore.getLatest(orderId)
const eventsSince = await eventStore.readFrom(orderId, snapshot?.version ?? 0)
return reduceOrderState([snapshot?.state, ...eventsSince].filter(Boolean))
}Follow-up Questions
- Why are snapshots necessary in an event-sourced system, and how often should they be taken?
- How do you handle changing the shape of an old event type without breaking replay of historical events?
- How does event sourcing relate to and typically pair with CQRS?
- What happens to storage growth over time in an event-sourced system, and how is it managed?
MCQ Practice
1. In event sourcing, how is the current state of an entity determined?
Event sourcing derives current state by replaying every recorded event for that entity in order.
2. What is the purpose of a snapshot in an event-sourced system?
Snapshots periodically save the folded state so only events since the snapshot need to be replayed, keeping reads fast.
3. Which of these is a key characteristic of events in event sourcing?
Events in event sourcing are immutable, persisted facts that are appended to an event log and never altered.
Flash Cards
What is event sourcing? — Storing every state change as an immutable event and deriving current state by replaying those events.
Why use snapshots? — To checkpoint derived state so replay does not have to start from the first event every time.
Key benefit of event sourcing? — A complete, built-in audit trail and the ability to rebuild any past state or new projections.
Event sourcing vs CQRS? — Related but distinct: event sourcing is about how state is stored; CQRS is about separating read and write models.