Case study · In production

Multi-country insurance platform

Ghana & Nigeria · Java · Spring Boot 3 · PostgreSQL

Client type

Regional insurance startup

Markets

Ghana · Nigeria

Engagement

Long-term product engineering

Status

In production · Live traffic

Java 21 Spring Boot 3.4 JHipster PostgreSQL Liquibase React 18 TypeScript

The problem

The client operated an insurance business in Ghana and needed to expand into Nigeria — a fundamentally different regulatory environment, currency, and integration landscape. Their existing core system had been built for a single market and couldn't cleanly separate tenant-level configuration from shared business logic.

The specific technical problem was that motor and non-motor policy issuance depended on hard-coded assumptions about the Ghanaian regulator's data model, the local KYC providers, and the payment rails. Copying the platform for Nigeria would have doubled the maintenance burden overnight and created two divergent codebases within twelve months.

The ask

Rebuild the platform on a multi-tenant architecture that could serve Ghana and Nigeria from one core — without breaking any of the live Ghanaian traffic, and without a rewrite that would freeze feature delivery for six months.

The approach

We came in as the lead engineering team on the migration and rebuild. The approach had three components:

1. Tenant model refactor, in-place

Rather than a big-bang rewrite, we refactored the existing codebase in-place to introduce a tenant abstraction. Every table got a tenant discriminator column via Liquibase migrations that ran zero-downtime against live Ghanaian data. Every service query got a tenant-scoped filter, enforced at the repository layer rather than sprinkled throughout the business logic.

2. Insurer-integration layer rewrite

The direct-to-insurer integrations (motor pricing, policy issuance, claim settlement) had been coded against specific Ghanaian insurers' APIs. We rewrote this layer as a pluggable adapter pattern — each insurer became a self-contained adapter implementing a common interface, with retry, circuit-breaker, and observability built into the base class. Adding a Nigerian insurer became a matter of writing one new adapter, not modifying the pricing engine.

3. Renewals subsystem — the hardest piece

Renewals were the subsystem where the two markets diverged the most. Ghanaian regulations require specific notice periods and formats; Nigerian regulations require different ones. Rather than fork the code, we rewrote renewals as a state machine driven by tenant-configured rules. The state machine handles both markets' regulations from one codebase, and adding a third market means adding rows to a rules table — not writing code.

What shipped

What made the release own-able

The reason this worked wasn't the specific architectural choices — plenty of firms could have made the same calls. It was that one person owned the release end-to-end: the merge queue, the CI pipeline, the migration rollouts against live data, the coordination with the local Ghanaian insurance regulator's technical team, the go/no-go call on the Nigerian launch date.

Lesson

Multi-market platform migrations don't fail because of tech choices. They fail because nobody is explicitly on the hook for the release. Polymit is on the hook for every release we ship.

Stack details

Have a similar problem?

If your team is planning a multi-market expansion, or trying to introduce multi-tenancy into a single-tenant codebase without a rewrite, we've been there. Talk to us.

Talk to us →