
Overview
The bank’s platform had grown into a tightly coupled system supporting core banking, BNPL and customer-facing services.
As usage increased, shared infrastructure became a constraint. Different workloads competed for database resources, synchronous dependencies connected services too closely, and individual services could not scale independently.
The project focused on modernizing the platform around independent services, workload-specific scaling and stronger failure isolation.
The Challenge
Different banking workloads were competing for the same infrastructure.
Core banking, BNPL and customer-facing APIs had very different traffic patterns, but they relied on many of the same underlying resources.
During periods of increased activity, read-heavy workloads placed additional pressure on the primary database. At the same time, synchronous service dependencies meant that a slow or unavailable service could affect other parts of the platform.
The bank needed to address a fundamental infrastructure problem: How do you scale one banking workload without scaling everything around it? And just as importantly: How do you prevent a problem in one service from becoming a problem for the wider platform?
Understanding the Existing Platform
We first identified where the architecture was creating unnecessary dependency.
The existing infrastructure was evaluated across database usage, service communication, traffic patterns and workload behaviour.
The focus was on understanding:
Database contention: Which workloads were competing for the primary database.
Synchronous dependencies: Where services were relying too heavily on the availability of other services.
Workload differences: How core banking, BNPL and customer-facing services behaved under different traffic conditions.
Future demand: How expected growth would affect the existing architecture over the following 18 months.
Separating the Workloads
Core banking and BNPL didn’t need to scale in the same way.
The platform was reorganized around its major business domains.
Core Banking: Core banking functionality was separated into independently deployable services with its own resource requirements.
BNPL: BNPL received a dedicated service boundary so its workload could be managed independently.
Customer-Facing APIs: Customer-facing services could scale according to their own traffic rather than depending on the capacity of backend workloads.
Reducing Database Pressure
The primary database was doing too much.
PostgreSQL was handling transactional operations alongside significant read-heavy workloads.
Instead of continuing to increase capacity on the primary database, the architecture separated these workloads.
Read Replicas: Read-heavy operations could be served through replicas rather than competing with transactional workloads on the primary database.
Redis: Frequently accessed data could be served through caching, reducing repeated requests to the database.
Moving Beyond Synchronous Dependencies
Services shouldn’t need to wait for each other unnecessarily.
Synchronous communication created dependencies between services where the performance of one could directly affect another.
Kafka was introduced to support asynchronous communication between services.
This allowed services to process events independently and reduced the coupling between workloads.
The change was less about introducing another technology and more about removing unnecessary waiting between services.
Scaling Independently
The unit of scaling changed from the platform to the service.
The infrastructure was migrated to Kubernetes on AWS EKS.
Services could now scale according to their own workload characteristics.
A spike in BNPL activity could be handled without scaling unrelated banking services.
Customer-facing APIs could respond to their own demand.
Core banking services could maintain their own capacity requirements.
Each workload could scale where the demand actually existed.
Containing Failures
A degraded dependency shouldn’t take other services down with it.
Independent services also required independent failure boundaries.
Circuit breakers and bulkheads were introduced to prevent failures from cascading across the platform.
If a downstream dependency became slow or unavailable, the affected service could contain the failure rather than allowing it to spread across unrelated services.
This reduced the potential blast radius of individual service failures.
Infrastructure as Code
Independent services needed a consistent infrastructure foundation.
With more services being deployed independently, infrastructure provisioning also needed to become repeatable.
Terraform modules were introduced for common infrastructure requirements including scaling, networking, monitoring and service configuration.
This created reusable infrastructure patterns that could be applied across services.
Observability
Independent services needed independent visibility.
Grafana and Prometheus were introduced to provide service-level monitoring across the platform.
The engineering team could monitor latency, error rates, resource utilization and service performance.
This made it easier to identify where a problem was occurring and distinguish between service-level issues and shared infrastructure constraints.
Key Features
Domain-Aligned Services: Core banking, BNPL and customer-facing APIs were separated into independently deployable services.
Event-Driven Communication: Kafka reduced critical synchronous dependencies between services.
Read Replicas & Caching: PostgreSQL read replicas and Redis reduced unnecessary pressure on the primary database.
Independent Scaling: Services could scale according to their own workload requirements.
Failure Isolation: Circuit breakers and bulkheads helped contain failures between services.
Infrastructure as Code: Terraform modules created consistent and repeatable infrastructure patterns.
Service-Level Observability: Grafana and Prometheus provided visibility into individual service performance.
Impact
The platform could respond to demand without treating every workload as the same problem.
The modernization addressed the underlying dependencies that were limiting the platform’s ability to scale.
Reduced database contention: Read-heavy workloads were separated from transactional workloads.
Independent service scaling: Individual services could respond to their own traffic requirements.
Improved failure isolation: Problems could be contained closer to the affected service.
More independent deployments: Services could evolve without treating the entire platform as a single deployment.
Repeatable infrastructure: Shared Terraform modules provided consistent infrastructure patterns.
Better operational visibility: Service-level monitoring made performance issues easier to identify.
Client Feedback
“The new architecture gives our teams greater flexibility to scale individual services, while improving resilience across the platform.”
VP of Engineering, European Bank
The Result
Banking infrastructure built around independent workloads.
The platform moved away from a tightly coupled architecture where shared resources and synchronous dependencies could affect unrelated services.
Core banking, BNPL and customer-facing services could operate with greater independence.
Database workloads could be separated.
Services could scale according to demand.
Failures could be contained.
Infrastructure could be provisioned consistently.
And engineering teams gained clearer visibility into the health of individual services.
Conclusion
Modernizing the platform wasn’t about adding more infrastructure. It was about removing the dependencies that made everything scale together.
The new architecture gave the bank a stronger foundation for handling different workloads independently while improving resilience across the platform.



