Double-Entry Ledger Architecture for Product Engineers
Listen to this article
Preparing audio…
FinTech Engineering
I start a ledger design by asking which financial states must be impossible to represent. Double-entry provides a powerful constraint, but only when accounts, units, time, and corrections are modelled deliberately.

Work one posting before discussing architecture
A customer tops up a wallet with AED 100. In a simplified chart of accounts, the platform gains AED 100 in a safeguarded bank asset and owes AED 100 to the customer:
| Entry | Account | Debit | Credit |
|---|---|---|---|
| 1 | Safeguarded cash — AED | 100.00 | 0.00 |
| 2 | Customer wallet liability — AED | 0.00 | 100.00 |
The journal balances in AED. “Debit” and “credit” are directions within an account classification, not synonyms for money entering or leaving a customer UI. A payout would reduce both the cash asset and wallet liability with the directions appropriate to those account types.
Square’s public description of Books explains why it chose balanced, immutable journal entries as a production consistency primitive. David Ellerman’s mathematical treatment formalizes double-entry using pairs of unsigned quantities. Neither source replaces an organization’s accounting policy; they support the invariant, not a universal chart of accounts.
Model transactions and entries separately
Here is how I would model it. A journal transaction is the atomic business posting, carrying a transaction ID, business reference, posting-rule version, recorded time, effective time, description, and lifecycle links. Its entries carry account, direction, amount, currency or asset unit, and analytical dimensions.
At commit, enforce:
- at least two entries per journal;
- sum of debits equals sum of credits for each currency or asset unit;
- amounts use an explicit fixed precision and cannot be negative;
- accounts accept the stated unit and were open at the effective time;
- a business operation cannot post twice;
- entries cannot be updated or deleted through ordinary application paths.
PostgreSQL constraints provide building blocks such as checks, unique constraints, and foreign keys. Cross-row balance normally also needs a controlled posting transaction or database routine; a row-level check alone cannot prove that the journal balances.
Never balance currencies by accident
USD 100 debit and EUR 100 credit do not form a balanced journal. Balance per unit. A foreign-exchange trade typically needs separate balanced legs and explicit FX position or clearing accounts, plus the agreed rate and valuation evidence. Reporting can translate values into a base currency, but that valuation is a derived view—not permission to mix denominations inside the posting invariant.
Represent money as integer minor units only when the currency and product precision make that safe. Otherwise use a fixed decimal with validated scale. Floating-point arithmetic has no place in the authoritative amount.
Pending is a product decision, not a universal entry type
An authorization hold can be represented with memorandum accounts, encumbrance entries, separate available-balance reservations, or another subledger design. Posting pending and settled positions as full journal entries is one valid choice, not a law of double-entry.
Choose based on which questions the ledger must answer. If available balance is financially material, the reservation needs durable identity, expiry, partial capture, release, and reconciliation. Do not mutate a pending entry into a settled one. Link the settlement transaction to the reservation and post the release or reclassification required by the chosen accounting model.
Correct with reversals, not edits
If AED 40 was credited to the wrong customer, create a reversal transaction linked to the original and then a corrected transaction. Preserve reason, initiator, approval, and timestamps. A reversal should copy the exact original units and accounts in opposite directions; a compensating adjustment that uses different accounts is a different accounting action and should be named accordingly.
This append-only approach keeps statements, period close, and downstream events explainable. Administrative database access still exists, so “immutable” also requires access control, audit logging, backups, and integrity monitoring.
Balances are projections with proofs
The authoritative balance is the sum of entries through a defined cutoff. For performance, maintain balance snapshots or account aggregates in the same transaction as posting, or rebuild them from the journal. Store a sequence/checkpoint and test that projected balances equal recomputed balances.
Concurrency matters when the product forbids a negative available balance. Lock the relevant account, use serializable logic, or maintain a conditional balance row so two withdrawals cannot both pass the same balance check. Double-entry proves the journal balances; it does not by itself prevent overspending.
The controls that make it production-grade
For every posting, an investigator should retrieve the initiating operation, posting rule, journal, entries, reversal chain, external reference, and resulting balance. Monitor rejected unbalanced journals, duplicate business keys, stale reservations, reversal rates, projection drift, and reconciliation breaks.
Property-based tests are especially useful: generate posting inputs and assert per-unit balance, immutability, idempotency, and rebuild equivalence. Then test concrete product scenarios—partial capture, fee, refund, chargeback, and currency conversion—with accounting review.
My design rule is not merely “use debits and credits.” I want a chart of accounts and posting boundary where every movement is balanced, denominated, traceable, corrected by new evidence, and independently recomputable.
Before releasing a product flow, have accounting and engineering review its full posting matrix: initiation, pending state, settlement, fee, partial completion, expiry, refund, dispute, and correction. Include the external evidence and customer statement description for every leg. A technically balanced journal can still be economically wrong when it uses the wrong account, date, party, or valuation rule; balance is a necessary control, not the whole accounting judgment.
Related reading
- Designing an Event-Driven Ledger That Auditors Can Trust
- Idempotency at Scale: More Than a Request Header
What decision would you make?
Add a question, a field note, or a respectful counterpoint. Comments are for signed-in members so the discussion stays useful and professional.