Terminology Service — Deployment Topology
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 03 platform-services · 02 DDD
1. Runtime
| Attribute | Value |
|---|---|
| Runtime | NestJS 11 / Node.js 22 |
| Language | TypeScript 5.x |
| Container | Docker (linux/amd64 + linux/arm64) |
| Orchestration | Kubernetes (production), Docker Compose (local dev) |
| Base image | node:22-alpine |
2. Kubernetes Deployment
| Parameter | Value |
|---|---|
| Namespace | ehealth-platform |
| Deployment name | terminology-service |
| Min replicas | 2 |
| Max replicas | 6 (HPA on CPU + request rate) |
| CPU request / limit | 500m / 2000m (bulk import queries are CPU-intensive) |
| Memory request / limit | 512Mi / 1Gi |
| Pod disruption budget | min 1 available |
| Rollout strategy | RollingUpdate — maxUnavailable 1, maxSurge 1 |
3. Networking
| Concern | Config |
|---|---|
| Internal address | terminology-service.ehealth-platform.svc.cluster.local:3000 |
| FHIR path | GET /fhir/R4/CodeSystem/$lookup, etc. — exposed via Kong for external FHIR clients |
| Internal API | /internal/terminology/* — cluster-internal only (no Kong exposure) |
| Health check | GET /health — liveness + readiness (includes DB + Redis dependency check) |
| TLS | Terminated at Kong for external; mTLS in cluster mesh (Linkerd) |
4. Dependencies (Runtime)
| Dependency | Type | Notes |
|---|---|---|
| PostgreSQL 16 | Primary store | Concepts, drug data, value sets (non-PHI; global region or AFG) |
| Redis 7 | Cache | Hot concept lookups, TTL 1h for lookups/validate, 5 min for value set expansions |
| NATS JetStream | Event bus | Outbox relay for concept mutation events |
| Keycloak | Auth | JWT validation for /v1/terminology/* and /fhir/R4/* |
| Kong | Ingress | External FHIR operation exposure |
No outbound service-to-service calls. Terminology-service is a leaf node in the dependency graph.
5. ETL Pipeline (Offline Data Load)
Licensed terminology data is loaded via the ETL pipeline at deployment time. The ETL runs as a Kubernetes Job (not part of the running service):
kubernetes/jobs/terminology-etl/
├── loinc-import.job.yaml
├── snomed-import.job.yaml
├── rxnorm-import.job.yaml
├── icd10-import.job.yaml
ETL jobs read from licensed data files in a secure object storage bucket (not committed to the repository). The service's POST /internal/terminology/import endpoint is used by the ETL and is disabled in production unless IMPORT_ENABLED=true.
6. Scaling Triggers
| Metric | Scale-out threshold |
|---|---|
| CPU utilization | > 65% for 2 min (bulk import elevates CPU) |
| Request rate (RPM) | > 1000 RPM per pod |
| Memory utilization | > 80% for 5 min |
7. Environment Variables
| Variable | Description |
|---|---|
DATABASE_URL | PostgreSQL connection string |
REDIS_URL | Redis connection string |
NATS_URL | NATS JetStream URL |
KEYCLOAK_REALM_URL | Keycloak issuer URL |
INTERNAL_TOKEN | Optional shared secret for /internal/* routes |
IMPORT_ENABLED | true to enable CSV import endpoint (default: false) |
CACHE_TTL_LOOKUP_SECONDS | Default 3600 |
CACHE_TTL_EXPAND_SECONDS | Default 300 |
MAX_EXPANSION_SIZE | Max concepts returned per ValueSet expand (default 500) |
8. Multi-Region Notes
Terminology data (SNOMED, LOINC, etc.) is non-PHI and may be deployed from a shared global read replica if latency allows. For edge/offline deployments (e.g., rural Afghan health posts), a snapshot bundle is packaged and deployed alongside the service — see SYNC_CONTRACT.md for offline snapshot strategy.