Store every change as an event in an append-only log, and derive current state by replaying them, instead of only keeping the latest value.
A typical table update replaces the old value with the new one — the account balance goes from $80 to $100, and the fact that it happened through a $20 deposit is gone. Reconstructing "what happened and when" for an audit, a dispute, or a bug investigation is impossible once the history has been overwritten.
Instead of storing current state directly, every change is appended to an event log as an immutable fact (`DepositMade`, `WithdrawalMade`). Current state is derived by replaying all events for an entity in order — often cached as a snapshot so a full replay isn't needed on every read.
`getBalance` never reads a stored balance field — it recomputes it by folding over every event that ever happened to the account.
const eventLog = [];
function append(accountId, event) {
eventLog.push({ accountId, ...event, at: eventLog.length });
}
function getBalance(accountId) {
return eventLog
.filter((e) => e.accountId === accountId)
.reduce((balance, e) => {
if (e.type === "Deposited") return balance + e.amount;
if (e.type === "Withdrawn") return balance - e.amount;
return balance;
}, 0);
}
append("acct-1", { type: "Deposited", amount: 80 });
append("acct-1", { type: "Withdrawn", amount: 20 });
append("acct-1", { type: "Deposited", amount: 40 });
console.log(getBalance("acct-1")); // 100 — derived, not stored directly
console.log(eventLog.filter((e) => e.accountId === "acct-1")); // full history, still intact