Total wealth
Main · EUR
Products
Card
nothing on your card yet
Transactions
The living map · click anything
every box is a real running process · raw Java 21, zero frameworks · PostgreSQL ×5 · Kafka · Redis · Prometheus ·
38 executable proofs in the repo
a link that exists. Messages and money travel it, and the dashes travel with them.
The ecosystem, live
🐘 PostgreSQL ×4
…
commits, last minute
📨 Kafka
…
events published, last minute
☸ Kubernetes
app ×3 replicas (the ONLY thing that scales)
pg-eu → eu-west-1 · pg-uk → eu-west-2
residency = a scheduling constraint
pg-eu → eu-west-1 · pg-uk → eu-west-2
residency = a scheduling constraint
manifests in k8s/ · the cluster shape of this exact system ·
live /metrics ▸
Trace
Live activity · every commit, publish and delivery
watching…
The Guide
Click anything on the map · this panel answers for it: three depths, live numbers, and its actual database rows.
Inside
The stack · every technology, and its job in this bank
Not a checklist. Every one of these is really running
and provably used · the same stack a modern neobank is built on. Click through the X-ray and Console to watch each do its job.
☕Java 21
The entire bank, raw: JDBC and a JDK HttpServer, a virtual thread per request. No Spring, no framework.
🐘PostgreSQL 16
The double-entry ledger, and a separate database per service. ACID transactions where money moves, with row locks and CHECK constraints.
📨Apache Kafka
The event backbone: the transactional outbox, cross-region payment sagas, and the notifications stream. At-least-once, consumers idempotent.
⚡Redis
A read-through cache for live prices and market history. Two levels (in-process + shared), and it fails open: the bank is correct without it.
☸Kubernetes
Orchestration on k3s. The stateless app scales by replicas; data residency is a nodeSelector the scheduler cannot violate.
🔥Prometheus
Scrapes the hand-written /metrics endpoint on every app pod and stores the time series. The source behind the live dashboard below.
📊Grafana
Dashboards over Prometheus, provisioned as code, running on the cluster. The same series this page renders live.
🦋Flyway
Versioned SQL migrations: the schema is code, applied per database, baselined over live data. Browse the history in SQL Studio.
🧪Spock + JUnit
Two test voices: JUnit for the concurrency lessons, Spock specifications for the units. 46 executable proofs run on every build.
🐳Docker + Compose
One image, packaged once, run anywhere: on a laptop, in Compose (the live public site), or on the Kubernetes cluster.
🔒Caddy
The edge: automatic TLS from Let's Encrypt, HTTP redirected to HTTPS, reverse-proxied to the app by service name.
🤖OpenAI
Rita, the support agent: grounded in your live balances, proposes actions with the same powers as the buttons, human-in-the-loop on each.
Live operations dashboard
The app exposes /metrics
(Prometheus text, hand-written). Prometheus scrapes it from every pod and stores the series;
Grafana graphs it on the Kubernetes cluster. This panel reads the same /metrics live in your browser, every 3s.
connecting…
Request rate HTTP
·req/s
Latency p99 histogram
·ms
Redis hit rate cache
·%
Money in flight saga
·EUR
HTTP requests by route req/s
Ledger events by kind events/s
Outbox backlog by region rows pending
Connection pool busy by region connections
Runs on Kubernetes · the manifests in this repo actually deploy
The public demo runs on Docker Compose on one box.
The same image also runs on a real 3-node Kubernetes cluster (k3s):
./k8s/run-local.sh builds it,
stands the cluster up, and applies the manifests. Prometheus then scrapes every app pod and Grafana graphs it ·
the dashboard above is those exact metrics, live in this page.kubectl get pods -o wide
🌍 Data residency, enforced by the scheduler. pg-eu carries a
nodeSelector: region=eu-west-1, pg-uk region=eu-west-2, so UK data can only ever
schedule onto UK nodes. The 3 stateless app replicas spread across all nodes; the databases are pinned. Grafana
and Prometheus run in the cluster too · kubectl port-forward svc/grafana 3000:3000 to open them.The ledger it produced · every group sums to zero
The same design at 100,000,000 customers · what scales is COUNTS, not concepts
The demo above runs every mechanism once;
production multiplies them. Every concept below already lives somewhere in this demo · expand one (or click
a box in the diagram) and it says exactly where, and what changes at 100M.
The deep-dive map · 16 questions, and where this bank answers them
The 16 questions a payments system has to survive, worked the way you'd actually reason about one:
clarify (requirements, throughput, consistency needs) → high-level (the map above)
→ deep dive (any question here · the full answer opens in place, at three depths)
→ trade-offs (each answer's Deep dive tab) → what it costs.
Strong consistency where money moves, eventual where echoes ripple · say it early.
1
PostgreSQL · where money commits
Every payment is one ACID transaction · and the event announcing it is written INTO that same
commit (the outbox table). Pick any database, run the exact query, read the exact rows. The two
⚖ audits are the bank auditing itself: zero rows = the books balance.
loading catalog…
·
⬇ a relay (a virtual thread per region) polls the outbox and ships each event ·
sent with acks=all, marked published only after the broker confirms: at-least-once, never lost ⬇
2
Kafka · where events travel
The broker itself, not a mirror: real partition·offset numbers, the exact bytes. The two
consumer groups below read independently · shard-applier moves cross-region money,
notifications stores echoes · and lag (end − committed) is the number every Kafka operator
watches. lag 0 = fully caught up.
connecting to the broker…