Olatunde Success walks through the NestJS monorepo architecture powering three production fintech products at SAW Technologies: module boundaries, shared domain libraries, per-product deployments, and the rules that keep one codebase from becoming one big ball of mud.
At SAW Technologies, one NestJS monorepo powers Dbrij, Cirva, and Resikonet. Three products, one repository, one CI pipeline, and a small team that has to move fast without breaking money movement. This setup gets questioned in every architecture conversation I have, so here is exactly how it works and why I keep choosing it.
What lives where
apps/ holds one deployable NestJS application per product, plus workers. Each app owns its controllers, its wiring, and nothing else.
libs/domain holds the business logic: wallets, escrow, ledger, KYC, notifications. This is where the real code lives.
libs/infra holds the boring shared pieces: Prisma clients, BullMQ setup, the encryption module, audit logging, idempotency middleware.
Each product composes domain libraries like ingredients. Cirva imports wallets and KYC; Resikonet imports escrow and disputes; Dbrij imports nearly everything.
The rules that keep it sane
A monorepo without boundaries becomes one application wearing three costumes. Our rules are mechanical enough to enforce in review. Apps may import libs; libs may never import apps. Domain libraries may not import each other sideways without an explicit interface. And every money-touching library gets its own test suite that runs even when only another product changed, because shared code means shared blast radius.
Why not three repositories?
Because the most valuable code we own is the code all three products share. The AES-256-GCM field encryption module, the idempotency layer, the audit logger, the SafeHaven and Paystack integrations: these were hardened once and reused everywhere. In separate repositories they would have drifted into three slightly different, slightly wrong versions. In the monorepo, a security fix lands everywhere in one pull request.
Deployment stays separate
Sharing code does not mean sharing fate at runtime. Each product builds its own container and deploys on its own schedule. A Resikonet release cannot take Cirva down. The monorepo is a development-time decision, not a runtime one, and keeping those two ideas separate is most of the trick.
Share the code that must never diverge. Isolate the deployments that must never fail together. That is the entire philosophy.


