TSTier SlateAll templates

Payments & ledgers

Double-entry ledger - the balance is not a column

Teaches why money is stored as immutable movements and read as a derived sum, and what it costs to never update a total.

  • System design
  • core
  • 8 step walkthrough
  • 5 tables

Share

  • Facebook
  • X

What this board gets wrong on purpose

The tension

The board spends every paragraph arguing that a balance must never be stored, and then stores one. The snapshot exists because a merchant settlement account takes 200,000 entries a day and no screen is going to SUM 365 million rows - but a snapshot is a cached sum, and a cached sum is a balance column with better manners. It can be written by a projector that read a partially-committed batch; it can be built from a replica that was behind; and when it is wrong, the read path returns a number that is confidently, silently, off. The trial-balance job is the only thing that would notice, it runs hourly, and it proves a GLOBAL invariant - debits equal credits across the whole ledger - which is exactly the check that a per-account snapshot error passes. So the system can be internally consistent and still show one customer the wrong balance for an hour, and the design has no per-account proof that runs often enough to catch it.

1 · Requirements

A ledger is the system of record for money that has moved. It is not a balance service with history bolted on; it is a history from which balances happen to be derivable. Every requirement below is downstream of that one inversion.

Ask these before designing

  • Is this the system of record, or a reporting copy? If a regulator or an auditor will read it, entries are immutable and retention is a decade. If it is a convenience projection of somebody else’s ledger, almost nothing here is needed.
  • Can an account go negative? A customer wallet must not. A revenue account must. A clearing account is expected to be negative for milliseconds. "No negative balances" is not a ledger rule, it is a per-account-type rule, and getting that wrong blocks legitimate postings at 3 a.m.
  • One currency or many? Multi-currency does not mean a currency column. It means an account has exactly one currency, a transaction balances per currency, and any cross-currency movement is two transactions joined by an FX position account that somebody owns.
  • How late can a posting be? If a card scheme can send a transaction dated four days ago, the ledger needs both an effective date and a recorded date, and every report has to say which one it used.
  • Who is allowed to post? If the answer is "any service", the invariant is enforced in fifteen codebases. This board has exactly one writer for that reason.

Functional

  • Post a transaction of two or more entries whose amounts sum to zero, atomically.
  • Reject any transaction that does not balance, before anything is written.
  • Read an account balance, and read it as of any past instant.
  • List an account’s entries in stable order, paginated.
  • Reverse a transaction by posting its mirror image, linked to the original.
  • Prove, on demand, that the whole ledger balances.

Non-functional

  • Entries are immutable. No UPDATE, no DELETE, no exceptions, enforced by revoked grants rather than by code review. The ledger role has INSERT and SELECT on entries and nothing else.
  • Durability beats latency, every time. Synchronous commit before acknowledgement. A ledger that loses an acknowledged write is not a ledger.
  • p99 under 150 ms to post a transaction, under 40 ms to read a balance.
  • 99.99% on reads, 99.95% on writes. Reads serve screens; writes can be queued and retried by the caller with an idempotency key.
  • The global invariant - sum of all entries = 0 - is checked hourly and alerts loudly. It is the one assertion that cannot be argued with.

Explicitly out of scope

Chart-of-accounts design, tax treatment, period close and journal approval workflow, FX rate sourcing, the payment rails themselves. The ledger records what happened; deciding what should happen is somebody else’s job.

The rest of this board

Board preview

Available on Tier Slate

This page publishes the question. The answer — 5 more written pages, an 8-step narrated walkthrough and a 5-table schema — is the board itself, and it opens in Tierslate.

  • 5written pages
  • 8walkthrough steps
  • 5tables27 columns

5 pages behind this one

  • Capacity estimation
  • Storage estimation
  • Availability
  • How it works
  • Deployment plan
Open this board in TierslateBrowse every board

More boards

Systems where losing a write is losing money.

  • Fraud detection - a decision that must fit in 120 milliseconds
  • Payment gateway - what a timeout means when the card may already be charged
  • Matching engine - price-time priority, and why the clock cannot decide who was first
  • Wallet transfers - the state machine, the hold that expires, and the reversal that cannot
All 50 templatesOpen Tierslate
Tierslate

tierslate.com

HomeTemplatesPrivacyTerms