Event Sourcing in Practice
Storing state as an immutable sequence of events and deriving current state by replay — powerful where history is the domain, expensive everywhere else.
Definition
Instead of storing current state and overwriting it, store the events that produced it.
AccountOpened, FundsDeposited, FundsWithdrawn. Current state is a fold over the events;
snapshots make that fold cheap.
What it genuinely solves
- Auditability. The history is the data, not a parallel log that can drift from it.
- Temporal queries. "What was the balance on the 14th?" is a natural question rather than an archaeology exercise.
- Retroactive correction. A bug in a derived calculation can be fixed and history reprocessed, because the inputs were preserved.
- New read models from old data. A view invented today can be built over years of history.
Where it is the honest model
Ledgers, trading books, regulated approval chains, insurance policy lifecycles, anything where "what happened and in what order" is the actual subject matter. In a financial ledger, the events are not an implementation technique for storing a balance — the balance is a derived convenience and the entries are the truth. Modelling it the other way round is what forces awkward reconciliation later.
Payment systems converge on this shape for the same reason: money movement must be explicable after the fact, and an append-only record of what happened answers questions that a mutable "current status" column cannot.
The costs people underestimate
Event schema evolution is forever. Once events are the source of truth, every historical version must remain readable. Upcasting old events to new shapes is permanent, growing code. This is the single largest long-term cost and it is invisible in year one.
Deletion is hard. An immutable log meets a right-to-erasure request awkwardly. The usual answer is crypto-shredding — encrypt personal data per subject and destroy the key — which must be designed in from the start, not retrofitted.
Querying requires projections. There is no ad-hoc query over current state; every question needs a read model, and every read model needs to be built and maintained.
Getting the events wrong is expensive. Events model business facts. If the initial modelling is poor — events that are too fine, too coarse, or that encode implementation detail — you live with them, because they are history.
Failure scenarios
- Adopted for CRUD, where an audit table would have given 90% of the value for 5% of the cost.
- Events named as commands (
UpdateUserrather thanUserEmailChanged), so the log records intentions rather than facts and cannot be replayed meaningfully. - No snapshots, so rebuilding an aggregate with 200,000 events takes minutes.
- Conflating the event store with the integration stream, publishing internal events externally and freezing internal modelling into a public contract.
Trade-offs
Bought: complete history, temporal queries, retroactive correction, new views over old data. Sold: query simplicity, deletion, schema freedom, and a significantly steeper onboarding curve for everyone who touches it.
Apply it to the bounded context where history is the domain. Do not apply it to the whole system.
Interview question
"Which parts of an e-commerce system would you event-source, which would you leave as CRUD, and how do you handle a data deletion request against the event-sourced part?"