A neobank stores every account change as an immutable fact rather than a mutable balance. Which needs justify this, how are balances materialised for fast reads, and what happens when a projection is found to be wrong?
Show the full answer Hide the answer
What justifies it
Three needs, and the first two are usually the deciding ones in a regulated financial context:
- Auditability that is structural rather than added. A regulator asks how a balance came to be what it is on a given date. With immutable facts, the answer is a query rather than an archaeology project, because the history was never overwritten.
- Reconstruction at any point in time. Disputes, chargebacks and investigations are all "what was true on date D", which a mutable-balance model cannot answer without a separately-maintained audit log that is itself a second source of truth capable of disagreeing with the first.
- Debugging. When a balance is wrong, the sequence of facts that produced it is available, and the bug is visible rather than inferred.
The domain fits naturally: a ledger is already an append-only structure. Double-entry bookkeeping has been immutable-fact-based for centuries, and a mutable balance column is the unnatural representation.
Materialising balances
The balance is a fold over the facts, and computing it from the beginning of history on every read is untenable. The standard approach:
- Snapshots: a periodic materialised balance with a marker for the last fact included. A read loads the snapshot and applies only the facts since. Snapshot cadence is a tuning knob between read cost and storage.
- A continuously-maintained projection updated as facts are appended, serving reads directly.
- Crucially, the projection is a cache, not the truth. It can always be discarded and rebuilt. That property is what makes the whole design safe, and it must be true operationally, not just in principle — there must be a tested, timed rebuild path.
When a projection is found to be wrong
This is the question that separates people who have run event-sourced systems from people who have read about them, and the answer is the design's principal advantage:
- The facts are not wrong. Only the derived view is. Nothing needs to be corrected in the source of truth.
- Fix the projection code.
- Rebuild the projection from the facts — into a new projection alongside the existing one.
- Compare old and new, quantify the divergence, and identify which customers were affected and by how much, which is a query rather than an estimate.
- Swap the read path to the new projection.
Compare with a mutable-balance system: the wrong value is the truth, the correct value is unknown, and recovery means restoring a backup and replaying whatever can be found. The difference in blast radius is the entire argument for the pattern.
The costs, stated honestly
Schema evolution of facts is permanent — a fact written five years ago must still be readable, so every version of every event type lives forever in the deserialisation path. Storage grows monotonically, needing a tiering strategy. Eventual consistency between the fact log and projections must be surfaced in the UI. Erasure requirements collide with immutability, which is the genuinely hard one: the usual resolution is crypto-shredding — personal data encrypted per subject, with erasure implemented by destroying the key, leaving the facts structurally intact but unreadable.
And the discipline required is real: the moment someone "fixes" a balance by writing a corrective fact without a corresponding real-world event, the log stops being a record of what happened and the property that justified the whole design is gone.