DevLearn
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.