A real-time double-entry ledger core for high-volume payments.
STACK
Designed and built a double-entry ledger core handling millions of postings per day. Every entry is immutable, every balance reconcilable, and every payout auditable to the last cent.
The client’s existing balance system stored a single mutable "balance" column per account. Under concurrent load — two payouts firing at once, a retried webhook, an agent re-attempting a failed call — that column silently lost updates. Support was fielding "where did my money go" tickets weekly, and finance couldn’t reconcile the books without a manual spreadsheet audit at the end of every cycle.
Replaced the mutable balance column with an immutable double-entry ledger: every transaction writes two or more offsetting entries that must net to zero, and every balance is a derived sum over entries rather than a stored number anyone can overwrite. Nothing is ever updated in place — corrections are new offsetting entries, which means the full history of any account is always a queryable audit trail, not a forensic investigation.
A single `ledger_entries` table with no UPDATE or DELETE grants at the database role level — only INSERT. Balances are computed with a SUM() query, never stored and mutated directly.
Every mutating operation carries a client-generated idempotency key tied to the business decision, not the HTTP request — so a retried webhook or a re-planning agent can never double-post.
Because every entry is immutable and every transaction balances to zero by construction, "does this reconcile" became a SQL query finance could run themselves instead of a manual monthly close.
ledger.ts
1async function postTransaction(entries: LedgerEntry[], idempotencyKey: string) {2 const total = entries.reduce((sum, e) => sum + e.amount, 0)3 if (total !== 0) throw new Error('Entries must net to zero')45 const existing = await db.idempotencyKeys.findUnique({ where: { key: idempotencyKey } })6 if (existing) return existing.result78 return db.$transaction(async (tx) => {9 await tx.ledgerEntries.createMany({ data: entries })10 return tx.idempotencyKeys.create({ data: { key: idempotencyKey, result: entries } })11 })12}
Designed the entries schema, idempotency key strategy, and account model before writing any UI.
Built the idempotent posting pipeline and wired it to the payments provider behind a queue.
Load-tested concurrent posting at 5x expected peak volume and shipped the self-serve reconciliation report.
Modeling the ledger as immutable entries eliminated an entire category of race conditions by construction — no amount of application-layer locking would have been as reliable.
Idempotency keys need to be tied to the business decision, not the HTTP call — generating a fresh key per retry defeats the entire point.
Giving finance a self-serve reconciliation query saved more support time than any dashboard feature we shipped.
If this project resonates with what you're building, let's talk. I take on a limited number of projects each quarter.
hello@martinsai.name.ng · Response within 24h