In event sourcing the append-only log of events is the source of truth, and current state is a projection built by replaying those events. It gives a complete audit trail and lets you derive new views from history, at the cost of more complexity in reads and versioning.
Before you start
You should understand events, state and CQRS. This article covers the model and its trade-offs.
Step-by-step walkthrough
Step 1: Append events, never update
Every change is a new event appended to a stream, such as OrderPlaced or ItemShipped. Nothing is overwritten, so the history is intact and the events are facts about what happened. The log is the durable record.
Step 2: Derive state by replay
Current state is a fold over the events: start empty and apply each event in order. This makes any past state reconstructable and lets you add a new read model later by replaying from the beginning, which is impossible with mutable rows.
Step 3: Version events and snapshot long streams
Events are a public contract, so evolve them additively and version the schema. Replaying a very long stream on every read is slow, so store periodic snapshots of the state and replay only the events after the snapshot. This bounds read cost.
Worked scenario
State is computed by applying events in order.
events: OrderPlaced(total=50), ItemShipped(item=A), ItemShipped(item=B)
state: apply in order -> { total: 50, shipped: [A, B] }
replay from snapshot@OrderPlaced -> only ship events re-appliedWalk through the example
The state is a fold, so it can be rebuilt at any time and audited line by line. A snapshot at OrderPlaced means a read applies only the later events, keeping replay short. New projections can be added by replaying the full stream once.
Common mistake
Treating events as CRUD records and mutating them, which destroys the audit trail and the ability to replay. Another is ignoring schema versioning, so an old event cannot be read by new code.
Verify the behavior
Rebuild state from events and compare it with a snapshot plus incremental replay to confirm they match. Add a new read model and replay history to populate it. Read an old event with current code and confirm backward compatibility.
Interview exercise
Why are event-sourced systems well suited to adding new read models?
Answer and reasoning
Because the full history of changes is stored, a new projection can be built by replaying every event rather than needing a migration or backfill from current state, which may lack the detail. The events carry the facts needed to compute any derived view, so the read side can evolve independently as long as the events are preserved.
Continue learning
Compare read models in Read models and the outbox in Outbox pattern. Read the microservices.io event sourcing pattern and try the Microservices interview questions.