Background
A regional commercial bank had operated its core banking platform as a monolithic Java application for twelve years. The platform managed current accounts, business lending, fixed deposits, and payment processing through a single codebase and shared relational schema in Oracle. All business logic lived in one deployable unit, meaning changes to any area — however small — required a full regression test cycle and a four-week coordinated release process involving 40+ engineers across product, QA, infrastructure, and compliance teams.
The monolith was not inherently broken: it ran the bank's operations reliably. The problem was what it couldn't do. Test coverage was below 5% — not from neglect but because the shared data model made unit testing practically impossible without standing up significant infrastructure. Every change carried risk that engineers found difficult to reason about, which slowed delivery to a crawl and made the platform increasingly expensive to change.
The Challenge
The bank had two strategic pressures operating simultaneously. First, they had a regulatory reporting requirement arriving in 18 months that required new product data structures the current monolith couldn't accommodate without significant rework of the shared schema. Second, they had a digital banking product that was ready to launch but required API capabilities from core that didn't exist and couldn't be safely added without breaking existing integrations.
The objective was not to rewrite the platform from scratch — the bank had been burned by a failed "big bang" rewrite attempt four years earlier that was abandoned after 18 months. The requirement was a migration path that preserved production continuity while enabling independent delivery of new capabilities.
Our Approach
AlgoDomain's engineering team conducted a four-week technical discovery to map the domain model inside the monolith. We identified six distinct bounded contexts with meaningful domain separation: current accounts, lending, fixed deposits, payments, customer identity, and notifications. Each had its own business logic, its own rate of change, and its own external integration surface.
We adopted a strangler fig migration pattern: rather than decomposing the monolith from the inside, we would incrementally build parallel services and route traffic to them one domain at a time, keeping the monolith operational throughout.
The first step was introducing an API gateway layer — Kong — which became the single entry point for all client traffic. This allowed us to gradually redirect traffic from the monolith to new services as they became production-ready. The monolith itself wasn't touched during this phase; we were adding routing infrastructure around it.
Phase Structure
The programme was structured into six phases, each covering one domain. Within each phase, the work followed a consistent pattern: identify all reads and writes to the domain, design the new service's API contract and data model, implement the service with comprehensive test coverage, run in dual-write mode alongside the monolith for a validation period, then cut traffic over to the new service and decommission the corresponding monolith module.
Apache Kafka was introduced as the event backbone for asynchronous communication between services, replacing synchronous in-process calls that had been responsible for the monolith's tight coupling. This also provided an audit trail and enabled the notification service to consume domain events from multiple sources without tight dependencies.
ArgoCD was introduced for GitOps-based continuous delivery to Kubernetes on AWS EKS. Each new service had its own deployment pipeline from day one — engineers working on current accounts could deploy independently of engineers working on payments.
Data Migration Strategy
Migrating data was the highest-risk element of the programme. The shared Oracle schema had denormalized data across tables with ownership that spanned multiple business domains. We used a dual-write pattern during migration: for each domain being extracted, new writes went to both the monolith's schema and the new service's PostgreSQL database. Once the new service was active, we ran a reconciliation job for a period before formally decommissioning the monolith tables.
Database migrations were handled with Flyway, giving the bank full version control over schema changes. Rollback paths were tested as part of each release process — a requirement the bank's risk team had from the outset.
Testing Infrastructure
The monolith's negligible test coverage meant the bank had no regression safety net. Building new services provided the opportunity to establish proper testing practices from scratch. Each new service was built with a mandatory minimum coverage threshold enforced in CI — unit tests for business logic, integration tests against real database instances, and contract tests using Pact to verify API behaviour with consuming systems.
By the end of the programme, automated test coverage across the new services averaged 87%. The bank's QA team shifted from manual regression testing to test strategy and automation engineering — a significant change in how QA contributed to the delivery process.
Results
The programme completed on schedule across all six phases. The monolith was fully decommissioned at the end of phase six. The outcomes the bank measured over the subsequent 12 months were consistent with the objectives set at the start of the programme:
- Release cycle time dropped from four weeks to daily across all product domains.
- Production availability reached 99.97%, measured over 12 months following programme completion.
- Automated test coverage moved from below 5% to 87% across the new service estate.
- The digital banking product launched six months after the customer identity and current account services went live.
- The regulatory reporting requirement was met through the payments and lending services before the deadline.
What Didn't Go Smoothly
The data reconciliation phase for the lending domain took three months longer than planned — the shared schema had foreign key relationships that weren't visible in the application code, and the reconciliation tooling we initially built didn't capture all edge cases. We ran parallel operations for longer than intended, which added cost and complexity to the decommissioning step. In retrospect, the initial schema analysis in the discovery phase needed more depth for the lending domain specifically.
The notification service was also redesigned partway through the programme when it became clear the initial event model wasn't sufficient for the compliance audit requirements — a change that required coordination across three other service teams. This added approximately six weeks to the overall timeline.