Core pattern

Two apps, one shared core, layered

Every service is hexagonal: domain logic in the middle, ports at the edge, adapters translating to the outside world. Zoomed out, the whole platform follows the same shape.

Applications
Own only what's genuinely theirs
Digital Banking & RemittanceTakaful
calls \u2193
Shared adapter platform \u2014 8 ports, both apps, one certification
binds to \u2193
External providers, per market
NADRA \u00b7 Raast \u00b7 SBP/FMU (PK) UAE Pass \u00b7 Aani \u00b7 CBUAE (AE) Absher/Yakeen \u00b7 sarie \u00b7 SAMA (SA)

Trace exactly what depends on what \u2192

Repository dependency graph
Figure \u2014 repository dependency graph (Document 28)
Repository topology

Eight repositories, one direction of dependency

platform-packages sits at the root; everything depends downward onto it. The two apps depend on platform-packages and on platform-adapters (contract-verified via Pact) \u2014 never the reverse.

  • Three placements are counter-intuitive by design: the FX library lives inside banking-app, not the adapters repo; OIDC ships as a client package; Persistence is dual-language.
  • The shared adapters live in one workspace but version and deploy independently, per adapter.
Open Document 28 \u2197
Branching and environment promotion diagram
Figure \u2014 GitHub Flow + tagged releases (Document 27)
Branching & release flow

Trunk-based, promoted by tag \u2014 not by branch

Short-lived feature branches merge to a protected main. Environments are fed by promoting the exact same signed artifact through tags and approvals: develop auto-deploys, staging needs one approval, production needs two \u2014 per region.

  • No long-lived develop branch \u2014 environments are a deployment concern, not a branching one.
  • Hotfixes branch from the production tag and reconcile back to main the same day.
Open Document 27 \u2197
NestJS 10 / TypeScript
Default for every service & adapter
~92% of services
Java 21 / Spring Boot 3.3
The calculation core only
Ledger \u00b7 Pricing \u00b7 FX

A float or rounding error in the ledger or a quote is catastrophic \u2014 exact BigDecimal arithmetic and JVM throughput justify leaving the TypeScript default in exactly these three places.

Technology stack

One language by default, two by exception

Every shared adapter, every domain service and every BFF is NestJS/TypeScript. The exception is drawn as narrowly as possible: Ledger, Pricing and the in-process FX library \u2014 nothing else.

  • Three guardrails compensate for leaving the JVM elsewhere: integer minor-unit money (@platform/money), dependency-cruiser boundary tests, and strict TypeScript config.
  • Every TS \u2194 Java seam is locked by Pact contracts in CI.
Open Document 01 \u2197