June 21, 2026
Event Sourcing for a General Ledger: What It Actually Buys You
Event sourcing gets pitched as an architecture pattern — store the sequence of things that happened, derive current state by replaying them, keep a full history for free. All true, and also, for a general ledger specifically, beside the point that actually matters: it’s the mechanism that makes a compliance claim structurally true instead of merely asserted.
The Shape of the Model
A LedgerAccount aggregate doesn’t store a mutable balance field that gets incremented and
decremented. It stores a sequence of events — AccountOpened, JournalEntryPosted,
JournalEntryReversed — and the balance is a projection: a read model computed by folding
those events, rebuildable from scratch at any time. The event store itself is append-only.
There is no UPDATE or DELETE path for a posted event, by construction, not by convention.
Commands are the only way to produce new events, and a command handler validates before it
emits anything — PostJournalEntry checks that debits equal credits before the event exists at
all. An unbalanced entry never becomes a persisted fact; it’s rejected at the boundary.
Why This Is a Compliance Property, Not Just a Data-Modeling Choice
Three things fall out of this shape without any extra compliance-specific code:
-
The audit trail is the primary data, not a side effect of it. There’s no separate “audit log” table that could fall out of sync with the real data — the event stream is the real data. Auditors don’t get a description of what happened; they get what happened.
-
Corrections are visible, not silent. Fixing a mistake means posting a new
JournalEntryReversedevent, timestamped and attributed like every other event. There’s no code path that erases the fact an error occurred — the history of “we were wrong and then corrected it” is itself permanent. -
Retention is automatic, not policy-dependent. SOX 802’s document-retention concern assumes someone might delete or alter a record. In an append-only store, “someone deleted a record” isn’t a policy violation to catch after the fact — there’s no operation that does it.
What Event Sourcing Doesn’t Buy You
To be precise about scope: event sourcing governs the core ledger domain specifically — the part where a full history of what happened is the product itself, not an add-on. Auth and identity in this system are deliberately plain CRUD, not event-sourced, because a session either is or isn’t valid right now; there’s no compliance value in replaying login history the way there is in replaying financial history. Applying one pattern uniformly everywhere would be a bigger architecture than the problem calls for. See the linked ADRs on the developers page for exactly where that boundary sits.