Modular Multi-Country Deployment
Two-axis model: country-agnostic modules times per-country market packs, module.json contracts, dependency-resolving installer.
How medOS is sold per module and deployed per (module × country) from one codebase. This is the architecture reference for the module-deploy system (
infrastructure/modules/), the market-pack system (infrastructure/market-packs/), and how they compose. Read this before touching module manifests, the installer, market packs, entitlement flags, or per-country deployment.
1. Thesis — two orthogonal axes
medOS deployment is the cross-product of two independent axes:
MODULES (country-agnostic features — the SQL/edge/UI is identical everywhere)
────────────────────────────────────────────────────────────────────────▶
MARKET PACKS │ read-model-core fpa-warehouse rollout-kpi scheduling telemetry …
(per-country │ ──────────────────────────────────────────────────────────────────────
overlays: │ 🇨🇳 china locale zh · CNY · commercial+社保 insurers · ICD-10-CN · Fapiao
locale, │ 🇯🇵 japan locale ja · JPY · kaigo rates · MEDIS-DC
seeds, │ 🇵🇭 philippines locale fil · PHP · PhilHealth case rates
insurers, │ 🇹🇭 thailand locale th · THB · NHSO 16-file
terminology) │ 🇺🇸 united-states locale en · USD · CPT/CMS-1500
▼ … any future country = drop a market pack
- One codebase. Application code (16 services + React frontend + ~80 edge functions) is byte-identical across every country. Proven by 6 live packs.
- A deployment = choose feature modules (what the hospital bought) + one market
pack (which country).
install-module.sh <modules> --region=<country>. - This is why the marginal country/site is cheap: it’s data + config, not code.
2. What is modular at each layer
| Layer | Modular unit | Mechanism |
|---|---|---|
| Frontend | federated miniapp + VITE_* entitlement flag |
module-federation (web/public/module-federation/module-manifest.json) + central registry + moduleEnabled() |
| Backend services | Moleculer service | 16 services on NATS; run what you need |
| Read-model SQL | module (infrastructure/modules/<id>/module.json) |
dependency-resolved install-module.sh |
| Country config | market pack (infrastructure/market-packs/medos-<c>/) |
manifest.json + idempotent seed SQL/TS; --region=<c> |
| Edge functions | named Deno fn | supabase functions deploy (reported by the installer) |
3. The two deploy contracts
3a. module.json — a country-agnostic feature
{
"id": "rollout-kpi",
"catalogCategory": "Reporting", // maps to docs/pricing-module-catalog.csv
"requires": ["fpa-warehouse","read-model-core","scheduling","telemetry"], // dependency edges
"migrations": ["medbase/migrations/20260602b_gold_rollout_kpi.sql", …], // schema — always
"seeds": ["medbase/migrations/095_fpa_seed_data.sql"], // demo — only with --with-seed
"edgeFunctions": ["slot-backfill-dispatcher"], // supabase functions deploy
"miniapp": "fpa-dashboard", // frontend federation id
"flags": { "VITE_ROLLOUT_KPI_ENABLED": "true" }, // entitlement contract
"postInstall": "SELECT fn_refresh_rollout_kpi();" // run after this module's SQL
}
Paths are relative to infrastructure/.
3b. market-pack manifest.json — a country overlay
{
"name": "medos-japan", "locale": "ja", "timezone": "Asia/Tokyo", "currency": "JPY",
"country_code": "JP", "terminology": { … }, "facility_types": [ … ],
"seeds": ["seed-hospital-facility.sql","seed-kaigo-rates.sql", …], // applied by --region=japan
"scripts": ["seed-blood-donors.ts"], // TS seeds (npx tsx)
"complianceProfile": { "dataResidency": "…", "retentionDays": …, "phiMaskingLevel": "…" }
}
A country needing more than seeds (edge fns, deps) can also be expressed as a full
module.json — medos-china is the worked example (commercial-insurer connector packs +
close-encounter-billing bridge + dependency on read-model-core).
4. The engine
| Piece | Role |
|---|---|
infrastructure/modules/resolve-modules.mjs |
reads all module.json, computes the dependency closure, topologically sorts (deps first), emits an ordered plan. Detects cycles + unknown modules. |
infrastructure/modules/install-module.sh |
<module-ids> [--region=<c>…] [--dry-run] [--with-seed] [--with-edge] — applies module SQL in order via psql (idempotent), applies any country’s market-pack seeds dynamically from its manifest, reports edge fns + flags. |
infrastructure/medbase/apply-fpa-rollout.sh |
the original hand-built bundle for the FP&A+rollout stack — superseded by the manifest-driven installer, kept as a fixed recipe. |
.github/workflows/apply-modules.yml |
manual one-click CI (workflow_dispatch) that runs the installer against a SUPABASE_DB_URL secret. NOT on-push (auto-applying migrations to a live DB is unsafe). |
install-module.sh rollout-kpi --region=japan --with-seed
# → resolve: read-model-core, scheduling, telemetry, fpa-warehouse, rollout-kpi (deps first)
# → apply each module's migrations (idempotent) + 095 demo seed
# → apply medos-japan manifest seeds (facility, kaigo rates, pathology-ja, …)
# → report: deploy slot-backfill-dispatcher; set VITE_ROLLOUT_KPI_ENABLED, locale ja
5. Entitlement — catalog → flags → deploy
hospital buys modules → entitlement (VITE_ENABLED_MODULES) → install-module.sh <ids> --region=<c>
(pricing-module-catalog.csv, (per-site allowlist; (resolve deps → apply SQL →
201 features / 20 categories) unset = all-on, demo-safe) report edge fns + flags)
moduleEnabled(id) (fpa-dashboard/entitlements.ts) reads VITE_ENABLED_MODULES (comma list):
- unset → all modules on (dev/demo — nothing breaks)
- set → only listed modules render (production purchase gate)
6. Multi-country
- Any country works via
--region=<country>, read dynamically from the pack manifest — no hardcoded country list, so future countries need zero code change. - Existing packs: china, japan, philippines, thailand, united-states, english.
- Add a country: drop
market-packs/medos-<c>/manifest.json(+ bilingual idempotent seeds);--region=<c>discovers it. Graduate to amodule.jsonif it needs edge fns/deps. - Topologies (
docs/ARCHITECTURE_DEPLOYMENT_PLAN.md): cloud / on-prem / air-gapped; LITE vs FULL. - Data residency: each site keeps its own read model in-region; only de-identified aggregates
federate to a group tier (
docs/architecture/cross-region-policy-gates-deployment.md,china-pipl-dsl-compliance.md).
7. Invariants (the rules that make it safe)
- Idempotent — every migration/seed uses
IF NOT EXISTS/ON CONFLICT/to_regclassguards, so applying any module-set repeatedly never damages anything. (This is the property that makes a package-manager-style installer safe.) - Dependency-closed — a module deploys with its full
requiresclosure, topologically ordered. Skipping a dependency is the42P01: relation … does not existerror (e.g. the rollout-KPI views readfpa_fact_encounter,department_queues,booking_slots,ui_interaction_events). - Order-independent where possible — view-creating migrations (e.g.
20260602b) self-guard onto_regclass, so a partial apply degrades gracefully instead of hard-failing. - Entitlement defaults open in dev, closed in prod —
VITE_ENABLED_MODULESunset = all-on. - Country = data, not code — application code never branches on country; differences are market-pack rows.
- No PHI crosses a region boundary — group roll-ups are de-identified aggregates only.
8. How to extend
- Add a module: create
infrastructure/modules/<id>/module.json(declare migrations/seeds/ edge fns/flag/requires); the installer + CI pick it up. WiremoduleEnabled('<id>')at the UI surface to gate by purchase. - Add a country: create
infrastructure/market-packs/medos-<c>/manifest.json+ idempotent bilingual seeds;--region=<c>works immediately.
9. Honest status (what’s real vs. intent)
- ✅ Real: dependency resolution, ordered idempotent SQL apply, dynamic multi-country
--region, edge-fn/flag reporting, one-click CI, entitlement gating of the Rollout-KPI surface. - 🟡 Intent: not every module yet gates its UI on its flag (extend
moduleEnabled()per surface); flags must be synced into each Vercel project. - ⚠️ Pragmatic layer: native
supabase db pushis blocked by a drifted migration-history table — this installer is the workaround until that’s repaired. Auto-on-push CI is intentionally avoided. - ⚠️ Per-region prod targets need per-region
SUPABASE_DB_URLsecrets/environments.
10. Cross-links
- Catalog:
docs/pricing-module-catalog.csv· Editions/topologies:docs/ARCHITECTURE_DEPLOYMENT_PLAN.md - Market-pack mechanism: root
CLAUDE.md(Multi-Region Deployment) · Seeds:docs/ai-seed-guide.md - China overlay:
docs/architecture/china-pipl-dsl-compliance.md· Build log:docs/uat/medos-china-build-log.md - Module system:
infrastructure/modules/README.md