Architecting a multi-tenant fintech-as-a-service platform
TL;DR
I architected, designed, and built a multi-tenant fintech-as-a-service platform that lets an institution stand up its own fintech offering. Two decisions carried the design: a custom annotation processor that auto-configures each tenant's domain, database, and baseline requirements instead of hand-wiring every onboarding; and a centralized data access proxy for query execution — chosen over a per-tenant routing library specifically to avoid a connection explosion and keep infrastructure cost low as tenants grow.
The problem
A fintech-as-a-service platform is multi-tenant by definition: many institutions run on shared infrastructure, each expecting its own isolated data, domain, and configuration. Two hard problems show up immediately. First, onboarding — every new institution needs a domain, a database, and a set of baseline requirements provisioned, and doing that by hand per tenant does not scale and invites drift. Second, data access at scale — the obvious approach, a routing library that resolves and connects to the right tenant datastore per request, quietly multiplies database connections across every service and every tenant. Connections are a finite, expensive resource; that path leads straight into pool exhaustion and rising cost.
Constraints
The platform had to add tenants without a proportional increase in operational toil or database connections, isolate each tenant's data cleanly, and stay affordable as the tenant count climbed — because in a FaaS model, per-tenant infrastructure cost is a direct drag on the business model. It also had to be consistent: every tenant provisioned the same way, so onboarding the tenth or the hundredth institution is the same reliable path, not a bespoke effort each time.
The approach
1. Auto-configuration via a custom annotation processor. Rather than manually wiring each institution's setup, I built an annotation processor that drives configuration programmatically. A new tenant's domain, database setup, and other baseline requirements are auto-configured, so onboarding becomes a repeatable, generated path instead of a hand-assembled one. This collapses the per-tenant setup work and eliminates the configuration drift that creeps in when every onboarding is done by hand.
2. A centralized data access proxy instead of a routing library. This is the decision I'd point to first. A per-tenant routing library would have each service opening its own connections to each tenant's database — connection count scaling with (services × tenants), which is exactly the wrong growth curve. Instead, query execution is routed through a centralized data access proxy (DAPS): a bounded, shared pool of connections serves every tenant, so connection count stays low and predictable while tenant data remains isolated. The result is materially fewer database connections and lower cost than the routing-library alternative — the difference between infrastructure that scales sub-linearly with tenants and one that scales linearly (or worse).
I wrote up the proxy design in depth separately: Building DAPS: A Centralized Data Access Proxy for Scalable Multi-Tenant Microservices.
3. Java/Spring Boot foundation with event-driven services. The platform is built on Java and Spring Boot, with PostgreSQL for tenant data, Redis for caching and coordination, Kafka for asynchronous communication between services, and AWS for infrastructure. The annotation processor operates at compile time, which keeps the auto-configuration deterministic and inspectable rather than resolved by fragile runtime reflection.
Why this is the right shape
The instinct in multi-tenancy is to reach for a routing library because it's the shortest path to "connect to the right database." But it optimizes the request and ignores the fleet: connections are the resource that runs out first. Centralizing query execution behind a proxy trades a little indirection for a bounded connection footprint — which is what actually determines whether a FaaS platform's unit economics work as it grows. Pairing that with compile-time auto-configuration means the two things that usually get more expensive per tenant — data-access cost and onboarding effort — both stay roughly flat.
Outcome
Institutions can be onboarded and set up to run their own fintech on shared infrastructure, with each tenant's domain, database, and baseline configuration provisioned automatically rather than by hand. Routing data access through the proxy keeps the database connection footprint low and infrastructure cost contained as the number of tenants grows — the behavior a fintech-as-a-service model depends on.
Skills this demonstrates
End-to-end system architecture and design ownership, multi-tenant data modeling and isolation, compile-time metaprogramming (annotation processors), database connection and cost engineering, event-driven microservices with Kafka, and the judgment to pick an unglamorous but correct infrastructure decision over the convenient default.