A banking platform, end to end, that you can operate
The five projects above, joined into one system and put on a cluster: a gateway, an auth service, accounts, payments, transactions and notifications, with Kafka between them, a database each, a pipeline that deploys them, and the dashboards and traces to run it at 3am.
The business problem
Every project on this site so far was one service with one hard problem. This one is the platform: six services that only make sense together, and the question is no longer 'does the transfer work' but 'can a new engineer deploy a change to the payment service on Friday afternoon and know within five minutes whether it broke anything'.
Build it in the order the milestones give, and stop after any milestone with a system that works. The last two are the ones that make it a platform rather than six repositories.
What you will have at the end
- Service boundaries that own their data, with events between them and no shared database
- A gateway that authenticates at the edge and fails fast when a service is slow
- A payment that survives a duplicate request, a crashed service and a replayed event
- Every service in a container, on a cluster, deployed by a pipeline, with a rollback you have used
- One trace id from the gateway to the notification, and a dashboard that shows the money moving
Milestones
Each one ends in something you can observe. Without that a milestone is a heading, and you have no way to know you finished.
The boundaries, on paper first
Auth, Customer, Account, Payment, Transaction, Notification. For each: what data it owns, which events it publishes, which it consumes, and which synchronous calls it makes. Draw it. Then check: does any arrow go into another service's database? Remove it.
done whenA one-page diagram where every service owns exactly one database and every cross-service read is either an API call or a projection of an event.
Auth and the gateway
The auth-service project, unchanged, issuing JWTs. Spring Cloud Gateway validates them at the edge, forwards the subject as a header, and routes by path with per-route timeouts and a circuit breaker on each — the gateway project's milestones, applied to five routes.
done whenAn expired token is a 401 from the gateway before any service is reached, and slowing Account to three seconds does not slow Customer.
Accounts and transactions with the ledger
The JDBC ledger's schema and rules, now behind Account, with Transaction as a read model built from AccountDebited/AccountCredited events through the outbox. Money is integers in minor units, append-only, balance from the ledger.
done whenA transfer shows in the Transaction service's history within a second of committing in Account, through Kafka, and replaying the topic from the beginning rebuilds the same history.
Payments as a saga
Payment orchestrates: reserve in Account, call the (stubbed) external provider, confirm or release. Idempotency-Key on the request; persisted saga state; a compensation path you test by making the provider fail after the reserve.
done whenKilling the Payment service between the reserve and the confirm, then restarting it, ends with the payment either confirmed or released — never stuck, never doubled — and a test proves both branches.
Notifications that survive a replay
The event-driven-notifications project as the sixth service: consumes PaymentConfirmed and sends (a logged) email exactly once per payment, however many times Kafka delivers the event.
done whenResetting the consumer group's offset to the beginning sends zero duplicate emails.
Containers, cluster, pipeline
A multi-stage Dockerfile per service with a layered jar and a non-root user; a Kubernetes manifest per service with requests, limits, three probes and a PodDisruptionBudget; a workflow that builds, tests, pushes an image tagged with the commit, and rolls the Deployment. Expand-contract for every migration from here on.
done whenA one-line change to Payment reaches the cluster from a merged pull request with no manual step, and kubectl rollout undo brings the previous version back in under a minute.
Operate it
Structured JSON logs with the trace id, shipped to one place; Micrometer metrics scraped by Prometheus; the golden signals per service on one Grafana board; OpenTelemetry traces from the gateway through Kafka to Notification; one alarm per service on error rate and p99, each with a runbook link. Then break something — fill Account's connection pool — and use only the dashboards to find it.
done whenFor any payment id, you can find its trace, every service it touched, the Kafka hop, and the log lines from each — and the pool exhaustion shows up on the dashboard before a user would have noticed.
Data
Six databases, not one. The Transaction service's history is a projection and can be dropped and rebuilt from the topic; the Account ledger is the system of record and can not. Say which is which in every schema's README.