Repo restructure — May 2026
This document records the structural refactor that ran across May 2026 and the order it landed in. Status: the refactor is complete; this file is now historical reference. The live architecture is described in:
README.md(entry point)CLAUDE.md(terse map)docs/STATUS_LEGEND.md(whatLive / Partial / Planned / Stubmean)docs/adr/(decisions; ADR-001…013)
Status tags below follow docs/STATUS_LEGEND.md.
Why
The previous structure (libs/ heavy, apps/ mixing FE + BE + Go services, vague Nx tags) had:
- Cognitive overload — engineers couldn't tell where to add a new module.
- Placeholder folders without code.
- Vague ownership —
libs/api/*placeholders,libs/shared/*README-only stubs. - Process composition (
apps/) mixed with deploy units (Go services). - Frontend names that didn't say who the audience was (
business,client,fi).
Final structure
apps/ composition roots only — bootstrap + wiring
api/ NestJS modular monolith
admin-web/ platform admin
employer-web/ employer admin (was: business)
employee-web/ employee app (was: client)
fi-web/ financial institution
merchant-web/ BNPL / merchant (was: bnpl-partner)
docs-web/ public docs site (was: docs)
*-e2e/ Playwright per app
services/ independently deployable
ledger/ Go — double-entry journal
integration-gateway/ Go — bank/wallet adapters
notifications/ worker — SMS/email/push
packages/
<domain>/ bounded contexts (only when code exists)
backend/{domain,application,infrastructure,presentation}
frontend/ domain-aware FE primitives (optional)
contracts/ how others talk to this domain
schemas/ Zod validation
tests/ integration tests
shared/
money/ exact-integer Money type
idempotency/ Idempotency-Key store + interceptor
audit/ audit-log emitter
events/ transactional outbox + Kafka publisher
tenant-context/ AsyncLocalStorage tenant ctx
auth/ JWT, guards, decorators
database/ Prisma client wrapper
logging/ pino + PII redaction
validation/ Zod helpers
ui/ React design system
contracts/
grpc/ .proto files
openapi/ public REST contracts
infra/ terraform, helm, argocd, docker (local dev)
tooling/ eslint custom rules, prisma-lint, generators
docs/ adr, runbooks, architecture, onboarding, security
Phases
| # | What | Status |
|---|---|---|
| 0 | This doc + ADR-002 | Live (landed May 2026) |
| 1 | apps/{ledger,integration-gateway,notifications} → services/* | Live — three Go service skeletons in services/. Note: ledger is Partial (DB Live, Go server Compile-only); integration-gateway and notifications are Stub (/health only). |
| 2 | Frontend renames to *-web | Live |
| 3 | Create packages/shared/*; move libs/shared/money + libs/ui | Live |
| 4 | packages/contracts/{grpc,openapi} + first .proto files | Partial — .proto sources Live, Go stubs require buf generate (not yet run on a Go-equipped host). |
| 5–7 | Domain extraction (identity, tenancy, workforce, payroll, wallet, kyc, risk) | Planned. Two domains have shipped outside this group: ewa (Partial) and lending (Partial). The rest are unbuilt. |
| 8 | New Nx tag + boundary rules | Live — ESLint module boundaries enforced. |
| 9 | Docs rewrite (CLAUDE.md, README.md) | Live. Status tags added by Phase 2A of the May 2026 doc consolidation. |
| 10 | Delete empty libs/; clean tsconfig path aliases | Live |
Capabilities added AFTER this restructure (covered by ADRs 006…013, not this doc):
| Capability | Status |
|---|---|
| Ledger schema + DB-enforced double-entry / append-only / RLS | Live |
Ledger Go server (PostTransaction, GetBalance, Reverse, GetEntries, ReconcileAccount) | Partial — code written + DB invariants proven; Go binary not yet built on this host |
| Tenant RLS migration with verify guard | Live (ADR-013) |
BYPASSRLS publisher role + OUTBOX_DATABASE_URL | Live |
| better-auth (email + phone + org plugins) | Partial — email sign-up Live; phone OTP Partial (stub SMS sender); 2FA Planned |
/healthz + /readyz + Prometheus /metrics | Live |
| Outbox publisher worker | Live (disabled by default; gated by OUTBOX_PUBLISHER_ENABLED) |
Non-goals for this restructure
- Migrating
Decimal(15,2)money columns toNUMERIC(20,0)santim. Separate migration. - Removing the
Wallet.balanceandLedgerAccount.balancederived columns. Separate migration. - Renaming
BNPLPartnerPrisma model toMerchant. Separate migration. - Renaming
mfienum value tofi. Separate migration. - Domain extraction (Phases 5–7).
These are deliberately deferred — each is a real DB migration and warrants its own ADR + PR.
How to read this repo after the restructure
- Want to add an HTTP endpoint? Find the right domain in
packages/. If it doesn't exist yet, create it viapnpm gen:domain <name>. Endpoint goes inpackages/<name>/backend/presentation/. - Want to add a money primitive? Use
@demoz-pay/shared-money. Don't reinvent. - Want to add a new external integration? Adapter goes in
services/integration-gateway/. Never inapps/api. - Want to add a UI screen? Goes in
apps/<audience>-web/. Reusable design-system components go inpackages/shared/ui/. - Want to change the ledger? Go service in
services/ledger/. Contract ispackages/contracts/grpc/ledger.proto.
ADRs landed alongside this restructure
- ADR-002 —
packages/overlibs/; target folder layout - ADR-003 — Domain package shape (domain/application/infrastructure/presentation)
- ADR-004 — Frontend rename:
*-websuffix - ADR-005 — Money is
NUMERIC(20,0)santim; floats forbidden - ADR-006 — Ledger is sole source of money truth
- ADR-007 —
Idempotency-Keyrequired on money-moving POSTs - ADR-008 — Audit + outbox in the same DB transaction
- ADR-009 — No
DELETEon financial rows - ADR-010 — Two-language ceiling: TypeScript + Go
- ADR-011 — Cross-domain communication is event-only