Billing Service — Deployment Topology
Status: populated Owner: TBD Last updated: 2026-04-17 Companion: Service Template · 17 tech stack
1. Runtime
| Property | Value |
|---|---|
| Runtime | Node.js 22 LTS |
| Framework | NestJS 11 |
| Language | TypeScript 5.x (strict) |
| ORM | Drizzle |
| DB | PostgreSQL 16 (RLS enforced) |
| Events | NATS JetStream |
| Cache | Redis (optional — rate limit, idempotency) |
| Object store | S3-compatible (MinIO local, AWS S3 / MoPH-approved cloud in prod) |
| Telemetry | OpenTelemetry → Grafana Tempo + Loki + Prometheus |
2. Containers
| Container | Image | Purpose |
|---|---|---|
billing-api | ghasi/service-billing:{sha} | HTTP + event handlers |
billing-worker | same image with WORKER=true | Statement run, GL export, outbox relay |
billing-migrate | same image | Drizzle migrations (Job, not a long-running container) |
3. Kubernetes topology
# deployment (API)
spec:
replicas: 3
strategy: RollingUpdate
maxSurge: 1
maxUnavailable: 0
# deployment (worker)
spec:
replicas: 2
# hpa
metrics:
- cpu: target 60 %
- custom: billing_http_requests_total rate
minReplicas: 3
maxReplicas: 10
| Component | Replicas (prod) | Autoscale |
|---|---|---|
| billing-api | 3–10 | CPU + request-rate |
| billing-worker | 2–6 | Queue depth |
4. Networking
| Port | Use |
|---|---|
3035 | HTTP (Kong upstream) |
9464 | Prometheus scrape |
- Ingress via Kong with per-route JWT verification.
- Intra-service traffic via service mesh (mTLS).
- Egress allowlist: tenant-service, terminology-service, interop-service, ai-gateway-service, payment gateway host (tenant-config).
5. Dependencies
| Dependency | Protocol | Fallback |
|---|---|---|
| PostgreSQL primary + read replica | TLS | Read replica for query-only endpoints |
| NATS JetStream 3-node cluster | mTLS | Outbox retains events; relay retries |
| tenant-service | HTTPS mTLS | Short-TTL cache for entitlement checks |
| terminology-service | HTTPS mTLS | Fail closed on charge code validation |
| interop-service (fhir-gateway) | HTTPS mTLS | Async retry on transient failure |
| ai-gateway-service | HTTPS mTLS | Feature disabled if unavailable |
| payment gateway (adapter) | HTTPS | Bounded circuit breaker per tenant |
| audit-service | async via NATS | Outbox retention covers outage |
6. Regions & data residency
| Country profile | Region | Data residency |
|---|---|---|
| Afghanistan (AFG) | MoPH-approved cloud region | All PHI stays in-country or per MoPH allowance |
| UAE | UAE region | UAE-only residency; no cross-border without tenant opt-in |
| Multi-tenant default | Regional cluster per country | Tenants cannot share region across borders without policy flag |
7. Secrets
| Secret | Store | Rotation |
|---|---|---|
DATABASE_URL | Kubernetes Secret sourced from Vault | 90 d |
NATS_CREDS | Vault | 90 d |
JWKS_URL | config (public) | — |
PAYMENT_GATEWAY_API_KEY_{TENANT} | Vault per tenant | tenant policy, default 30 d |
KMS_KEY_ARN | env | — |
8. Env vars
See .env.example. Critical: DATABASE_URL, NATS_URL, JWKS_URL, TENANT_SERVICE_URL, TERMINOLOGY_SERVICE_URL, FHIR_GATEWAY_URL, OBJECT_STORE_BUCKET, OTEL_EXPORTER_OTLP_ENDPOINT.
9. Canary + rollback
- Canary 5 % traffic for 30 min.
- Rollback triggers: availability < 99.5 %, payment latency p95 > 3 s, ledger integrity mismatch > 0, error rate > 2 %.
- Blue/green fallback for DB migrations: expand → migrate → contract.
10. Scaling notes
- Statement runs are tenant-partitioned and offloaded to worker pool; can scale independently.
POST /paymentsis DB-bound — increase Postgres connection pool (default 20 per replica) before scaling replicas.- Outbox relay scales vertically; one relay per replica; NATS consumer-group fan-out.