Orders Service — Migration Plan
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · SERVICE_OVERVIEW · DOMAIN_MODEL
1. Background
The orders-service consolidates two previously separate bounded contexts per ADR-0046:
| Legacy module | FR prefix | Status |
|---|---|---|
orders-cpoe | FR-CPOE-, FR-ORD- | Retired as standalone; absorbed into orders-service |
requisitions-referrals | FR-REQ-, FR-REF- | Retired per ADR-0046; RETIRED.md in _sources/requisitions-referrals/ |
FR mappings: all FR-REQ-* and FR-REF-* references are re-numbered as FR-ORDERS-* in this service's documentation. Legacy refs are preserved in the FR table for traceability.
2. Migration Scope
| Migration concern | Detail |
|---|---|
| Legacy CPOE order data | Orders created in the legacy CPOE module need migration to the unified orders schema |
| Legacy requisition data | All lab and radiology requisitions created via requisitions-referrals-service migrate to orders schema as laboratory and radiology order types |
| Referral records | Legacy referral records migrate as referral order type orders |
| Order set templates | CPOE legacy order set templates migrate with ords_ ID prefix |
| FHIR resources | Legacy FHIR ServiceRequests re-issued with new resource IDs via interop-service |
3. Migration Phases
Phase 0: Pre-Migration Preparation
- Audit legacy CPOE database for record count, status distribution, and schema gaps.
- Map legacy status values to new
OrderStatusenum (draft/active/completed/cancelled/entered-in-error). - Map legacy requisition types to new
orderTypeenum (laboratory,radiology,referral). - Identify legacy orders without an
encounterId(encounter-bound is now required; assign to a synthetic encounter or flag asentered-in-error). - Identify any legacy controlled-substance orders requiring dual-sign audit records.
Phase 1: Schema Deployment
- Deploy orders-service schema alongside legacy schema (blue-green; both active).
- Run initial schema migration scripts — no data movement yet.
- Validate RLS policies in staging with synthetic data.
Phase 2: Historical Data Migration (Batch)
Run migration scripts against a DB snapshot (not live):
-- Example: migrate legacy lab requisitions to orders
INSERT INTO orders.orders (
id, tenant_id, patient_id, encounter_id, order_type,
order_code, status, priority, ordered_by, ordered_at,
lab_detail, version, created_at, updated_at
)
SELECT
'ord_' || legacy_req.id,
legacy_req.tenant_id,
legacy_req.patient_id,
COALESCE(legacy_req.encounter_id, 'enc_legacy_synthetic'),
'laboratory',
jsonb_build_object('system', 'http://loinc.org', 'code', legacy_req.loinc_code, 'display', legacy_req.test_name),
CASE legacy_req.status
WHEN 'OPEN' THEN 'active'
WHEN 'COMPLETED' THEN 'completed'
WHEN 'CANCELLED' THEN 'cancelled'
ELSE 'entered-in-error'
END,
LOWER(legacy_req.priority),
legacy_req.created_by,
legacy_req.created_at,
jsonb_build_object('priority', LOWER(legacy_req.priority), 'fastingRequired', legacy_req.fasting_required),
1,
legacy_req.created_at,
legacy_req.updated_at
FROM legacy_schema.requisitions legacy_req
WHERE legacy_req.tenant_id = :tenant_id;
Migration script approach:
| Step | Script | Idempotency |
|---|---|---|
| 1. Migrate CPOE orders | scripts/migrate-cpoe-orders.ts | Upsert on id — safe to re-run |
| 2. Migrate lab requisitions | scripts/migrate-lab-requisitions.ts | Upsert on id |
| 3. Migrate radiology requisitions | scripts/migrate-radiology-requisitions.ts | Upsert on id |
| 4. Migrate referrals | scripts/migrate-referrals.ts | Upsert on id |
| 5. Migrate order sets | scripts/migrate-order-sets.ts | Upsert on id |
| 6. Validate row counts | scripts/validate-migration-counts.ts | Comparison report |
| 7. Generate FHIR re-issue manifest | scripts/generate-fhir-reissue-manifest.ts | ID mapping CSV |
Phase 3: Cut-Over
| Step | Action |
|---|---|
| Stop writes to legacy CPOE and requisitions-referrals services | Feature flag LEGACY_ORDERS_WRITE=false |
| Run final incremental sync (delta migration) | Capture all records written after Phase 2 snapshot |
| Validate record counts match | Automated count comparison script |
| Switch read traffic to new orders-service | Kong route weight: 0% → 100% new service |
| Enable FHIR re-issue via interop-service | Publish new ServiceRequest / MedicationRequest IDs |
| Archive legacy service | Set legacy service replicas to 0; retain DB for 90 days read-only |
Phase 4: FHIR ID Remapping
Legacy FHIR ServiceRequest and MedicationRequest IDs must be updated in consuming services (patient-chart-service, patient-portal-service). interop-service maintains a fhir_id_redirect table during the 90-day retention window:
Legacy FHIR ID → New FHIR ID (via orders-service)
External clients following legacy FHIR IDs receive a 301 Moved Permanently response pointing to the new ID for 90 days after cut-over.
4. Tenant Onboarding
For tenants onboarded after orders-service GA (no legacy data):
- No migration required — fresh schema, RLS policies applied automatically.
- Order set templates may be imported from system-global templates (flagged
is_global=true). - CDS rules are provisioned by terminology-service independently.
For tenants migrating from legacy CPOE systems:
- Tenant-specific migration run is scheduled during a maintenance window.
- Migration team provides count validation report before go-live.
- Tenant's active orders are validated by clinical team (sample check: 10% of active orders).
5. Rollback Plan
| Trigger | Rollback action |
|---|---|
| Migration count mismatch > 0.1% | Halt migration; investigate delta; re-run from Phase 2 |
| Post-cutover order creation failure rate > 0.5% | Kong route weight back to legacy; investigate |
| FHIR ID mismatch in patient-chart-service | Re-run FHIR re-issue manifest; extend redirect window |
| Data integrity failure in RLS validation | Emergency: set legacy service back to 100% weight; page on-call |
Legacy DB is preserved read-only for 90 days post-cutover. All rollback actions are documented in the operations runbook at infra/runbooks/orders-migration-rollback.md.
6. Legacy FR Mapping
| New FR | Legacy FR | Source module |
|---|---|---|
| FR-ORDERS-001 | FR-CPOE-001 | orders-cpoe |
| FR-ORDERS-002 | FR-CPOE-002 | orders-cpoe |
| FR-ORDERS-010 | FR-REQ-001 | requisitions-referrals |
| FR-ORDERS-011 | FR-REQ-002 | requisitions-referrals |
| FR-ORDERS-020 | FR-REF-001 | requisitions-referrals |
| FR-ORDERS-021 | FR-REF-002 | requisitions-referrals |
Full mapping table maintained in services/orders-service/_sources/orders-cpoe/TRACEABILITY_MATRIX.md.
7. Open Questions
- Confirm legacy encounter-less orders handling with clinical informatics team.
- Agree on FHIR ID redirect window duration with interop-service team (90 days proposed).