A regulated fintech must prove every customer-facing balance can be reconstructed from an audit trail. Compare event sourcing, CDC into an immutable log, and conventional tables with a triggered audit log.
Show the full answer Hide the answer
The requirement, stated precisely
For any point in time, produce the balance and the sequence of facts that produced it, with assurance that the record has not been altered. This must hold for years, across schema changes, and survive the departure of everyone who built the system.
Event sourcing
The facts are the source of truth; balances are a fold over them.
Strengths: auditability is structural rather than added — there is nothing to reconcile between the record and the truth because they are the same thing. Point-in-time reconstruction is a query. Debugging is direct: the sequence that produced a wrong value is available.
Costs: event schema evolution is permanent, so every version of every event type lives forever in the deserialisation path. Storage grows monotonically. Projections are eventually consistent and must be surfaced as such. And erasure collides with immutability, usually resolved by crypto-shredding.
The decisive property: when a projection is found to be wrong, the facts are not wrong. Fix the code, rebuild into a new projection, compare, and identify exactly which customers were affected and by how much — as a query rather than an estimate. In a mutable system the wrong value is the truth.
Nubank's use of an immutable fact-based model is the well-known instance, and the domain fits naturally: double-entry bookkeeping has been append-only for centuries, and a mutable balance column is the unnatural representation.
CDC into an immutable log
Conventional mutable tables, with the database's change log captured into immutable storage.
Strengths: the application is ordinary, with no event-sourcing discipline required of developers. The capture sees every write — including migrations, admin tools and other services — which application-level auditing misses. Adoptable incrementally on an existing system.
Costs: the log records row changes rather than business facts, so "the balance changed from X to Y" is captured while why is not — and reconstructing intent from row diffs is possible and unpleasant. The log is also coupled to the internal schema, so a refactor changes the audit record's shape.
Good fit: an existing system that must acquire an audit trail without being rewritten.
Conventional tables plus a triggered audit log
Triggers or application code write an audit row alongside each change.
Strengths: simple, familiar, queryable with the same tools.
Costs: the audit log is a second source of truth that can disagree with the first, and does — a path that forgets to write it, a trigger that is dropped during a migration, a bulk update that bypasses it. A gap is undetectable without an independent check, and the whole assurance rests on completeness that nothing verifies.
Application-written audit logs are the weakest of the three, precisely because they depend on every code path remembering.
Choosing
- New system, correctness-critical, regulated → event sourcing, accepting the discipline.
- Existing system needing an audit trail → CDC into immutable storage, which is far cheaper than a rewrite and captures everything.
- Low-stakes auditing → triggered audit tables, understanding the gap risk.
Regardless of choice: the store must be genuinely immutable — write-once storage with retention lock — or the audit trail is a claim rather than evidence. And the reconstruction must be tested periodically, because an untested reconstruction path is a compliance assertion nobody has verified.