Quiz: Ledger & Data Model
Covers the ledger-first model, derived balances, event sourcing, and snapshots. Reference: ADR: Ledger-First Data Model.
Question 1: Why not store the balance
Mid
balance field on the account?Show answer
A stored balance is a second source of truth that has to be kept in sync with the transaction history. The day a write half-fails, a race interleaves two updates, or a migration hiccups, the stored balance and the sum of transactions disagree, and now you have money that doesn't reconcile. For a fintech that means frozen withdrawals and regulatory questions.
Storing only the append-only ledger and deriving the balance (sum(credits) − sum(debits)) means there's exactly one source of truth. The balance can never disagree with history because it is history, summed. You trade a little compute for the guarantee that your numbers always reconcile.
Question 2: Event sourcing on the frontend
Senior
Show answer
Event sourcing stores the events (the ledger entries) as the source of truth and derives every view by replaying them. Here, balance, balance-history, spending-by-category, and the portfolio breakdown are all separate projections of the same entries, each one a different reduction over the ledger.
That makes features additive: a new analytics view is a new query against the existing source of truth, not a new column you have to populate and keep in sync. You never migrate the core data to add a chart: you write a new projection. The hard part (correct, reconcilable data) is solved once.
Question 3: Snapshots at scale
Staff
Show answer
Snapshots. A snapshot is a saved computeBalance() result at a point in time. Instead of scanning all n entries, you load the latest snapshot and scan only the k entries created after it: computeBalanceFromSnapshot(snapshot, recentEntries).
A nightly snapshot bounds k to ~one day of activity: tens of entries, not thousands. Crucially, the snapshot is a read optimisation, not a new source of truth: if it's ever wrong, you throw it away and recompute from the full ledger. So you get O(k) reads without reintroducing the two-sources-of-truth problem that storing a mutable balance would. The ledger stays untouched; computeBalance is just called with a shorter slice.