Skip to content

telegraphchi/idempotent-ledger-java

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

idempotent-ledger (Java)

An append-only, double-entry ledger with idempotent writes in Java 21 — the core of how a payment system records money correctly. No runtime dependencies; JUnit 5 for tests.

The same pattern I keep in Go (go version), implemented in idiomatic modern Java (records, sealed-style reason enums, switch expressions). Clean-room and original.

The three properties that keep money safe

  • Append-only — entries are never mutated; every balance reconstructs from the log, so history is fully auditable.
  • Double-entry — each transfer writes a matching debit and credit, so all balances always sum to zero. audit() proves it.
  • Idempotent — each write carries a key. Replaying it (retried request, redelivered message) applies the money once; reusing a key for a different transfer throws IDEMPOTENCY_CONFLICT.

Money is stored as long minor units. Never double for money. The class is thread-safe.

Quickstart

var ledger = new Ledger();

ledger.deposit("seed-1", "alice", 1_000, "funding");        // External -> alice
ledger.transfer("invoice-77", "alice", "bob", 250, "rent"); // alice -> bob

// A retried request with the same key applies once:
var retry = ledger.transfer("invoice-77", "alice", "bob", 250, "rent");
retry.replayed();        // true — no double charge

ledger.balance("bob");   // 250
ledger.audit();          // throws IllegalStateException if ever inconsistent

Idempotency contract

Call Result
New key applied; replayed() == false
Same key, same params cached result; replayed() == true; applied once
Same key, different params LedgerException(IDEMPOTENCY_CONFLICT); nothing applied

Build & test

mvn test

Nine JUnit 5 tests cover deposits/transfers, conservation, idempotent replay, idempotency conflict, insufficient funds, validation, append-only monotonicity, and two concurrency tests — 500 distinct transfers across a thread pool, and 100 threads racing a single idempotency key (which must apply exactly once).

Requires JDK 21+ and Maven.

Making it durable

Replace the in-memory List/Map with a write-ahead log or an append to a Kafka topic and project balances from it. The idempotency and double-entry logic — the part that's easy to get wrong — stays identical.

License

MIT

About

Append-only, double-entry ledger with idempotent writes in Java 21. JUnit 5, thread-safe, audit-verified.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages