Back to Payments & Banking
payments
Ledger Design
Double-entry, immutable journal, and balance materialization
Ledger Design — Overview
Double-entry, immutable journal, and balance materialization
Financial ledgers use double-entry bookkeeping: every debit has matching credit. Immutable append-only journal with materialized balances. Event sourcing fits naturally — account state is fold of all events.
// Double-entry ledger entry
public record LedgerEntry(
String journalId,
String debitAccount, // e.g. MERCHANT_RECEIVABLE
String creditAccount, // e.g. CUSTOMER_LIABILITY
Money amount,
String reference,
Instant timestamp
) {}
// Append-only — never update, only compensating entries
ledger.append(new LedgerEntry(
"J-001", "CASH", "REVENUE", Money.of(100, "USD"),
"PAY-123", Instant.now()
));
// Materialized balance from stream
Balance balance = journal.stream()
.filter(e -> e.affects(accountId))
.reduce(Balance.ZERO, Balance::apply);Tip: Use idempotent journal IDs — retries must not create duplicate entries.