---
title: "Real-life scenario: LedgerStream"
chapter: "18"
---

# Real-life scenario: LedgerStream

LedgerStream reconciles millions of daily transactions from banks, payment
providers, and SAP. It must identify mismatches, preserve an audit trail, and
finish the daily batch within 45 minutes.

## Requirements

- Inputs arrive as large UTF-8 CSV/JSON files and partner HTTP APIs.
- Money must be exact across currencies and rounding rules.
- A duplicate file or event must not duplicate ledger effects.
- Partner APIs may be slow for minutes.
- A corrupt record must not stop all valid records.
- The process must resume safely after a crash.
- Operators need progress, mismatch counts, traces, and a reproducible report.

## Domain model

Use records for validated immutable data carriers:

```java
record Money(BigDecimal amount, Currency currency) {
  Money {
    Objects.requireNonNull(amount);
    Objects.requireNonNull(currency);
    amount = amount.setScale(currency.getDefaultFractionDigits(),
                             RoundingMode.HALF_EVEN);
  }
}
```

Use a sealed result hierarchy: `Matched`, `Mismatch`, and `Rejected`. An
exhaustive switch forces handling when a new result type appears.

## Processing design

Read files with NIO using bounded buffers and explicit UTF-8. Validate each row
into a domain command. Compute a stable idempotency key from partner, file, and
transaction identity. Persist checkpoints after bounded chunks.

Use virtual threads for independent blocking partner HTTP calls. Do not create
an unbounded request storm: a semaphore caps each partner's concurrency and the
connection pool remains the real scarce resource. Each call has a timeout,
bounded retry with jitter for safe transient failures, and a circuit breaker in
the surrounding application layer.

CPU-heavy matching uses a fixed-size platform-thread executor based on measured
cores. `ConcurrentHashMap` stores shared indexes only where memory permits;
larger joins belong in a database or external sort. Never use parallel streams
for blocking partner calls.

## Correctness

Use `BigDecimal`, explicit currency/rounding, `Instant` for event time, and the
partner's `ZoneId` for business-day boundaries. Treat duplicate input as a
successful replay of the previous outcome. Isolate malformed rows into a
quarantine report with safe error detail.

## Modules

Create modules or strongly enforced packages: `ledger.domain`,
`ledger.ingest`, `ledger.match`, `ledger.partner`, `ledger.report`, and
`ledger.runtime`. Domain code does not depend on file, HTTP, or database
adapters.

## Operations

Build with a pinned JDK 25 toolchain and reproducible JAR. Run as non-root.
Record artifact checksum, source revision, input manifest, configuration, and
output checksum. Use JFR during performance tests. Publish throughput, queue
depth, rejected rows, partner latency, retry count, GC, and completion ETA.

## Failure drill

Terminate the process halfway through. On restart, LedgerStream verifies the
input manifest, resumes from a committed checkpoint, replays idempotently, and
produces the same final checksums. Simulate one slow partner and prove the
concurrency cap protects all others.

## Decision record

**Chosen:** immutable records, sealed results, NIO, `BigDecimal`, virtual
threads for blocking I/O, bounded CPU pool, checkpoints, idempotency, and JFR.

**Rejected:** `double` for money, one thread per record without limits,
parallel streams for network calls, untrusted Java deserialization, and a
single giant mutable map without capacity proof.
