Facility Service — Migration Plan
Status: populated Owner: TBD Last updated: 2026-04-17 Companion: SERVICE_TEMPLATE §17
1. Context
facility-service consolidates two legacy modules (facility-management and hierarchy) into one bounded context. Migration addresses:
- Code & ownership migration from
apps/services/facility-managementandapps/services/hierarchyintoservices/facility-service. - Database merge of
facility_dbandhierarchy_dbintofacilityschema inside the unified service DB. - Event renaming from legacy subjects to canonical
facility.*subjects. - Existing tenants' hierarchies preserved unchanged.
2. Migration tracks
| Track | Description | Phase |
|---|---|---|
| T1 | Code merge: copy aggregates, ports, handlers; re-wire imports | M0 |
| T2 | Schema merge: ETL hierarchy_db.* → facility.hierarchy_*; preserve IDs | M0 |
| T3 | Event bridge: dual-publish legacy (hierarchy.node.created) + canonical (facility.hierarchy_node.created.v1) for 30 days | M0 → M1 |
| T4 | Consumer cutover: every consumer switches to canonical subjects | M1 |
| T5 | Legacy retirement: drop dual-publish, drop legacy DBs | M1 end |
3. Data mapping
| Legacy table | Legacy DB | New table | Notes |
|---|---|---|---|
hierarchy_nodes | hierarchy_db | facility.hierarchy_nodes | Preserve id; map profile_id; tenant_id unchanged |
hierarchy_edges | hierarchy_db | facility.hierarchy_edges | Preserve id |
hierarchy_profiles | hierarchy_db | facility.hierarchy_profiles | Seed platform profiles if missing |
provider_node_memberships | hierarchy_db | facility.provider_node_memberships | Preserve id |
locations | facility_db | facility.locations | Require hierarchy_node_id; backfill from joined record |
location_hours | facility_db | facility.location_hours | 1:1 copy |
beds | facility_db | facility.beds | Preserve IDs; ensure unique (location_id, bed_number) |
resource_catalog_items | facility_db | facility.resource_catalog_items | 1:1 |
4. Event renaming
| Legacy subject | New subject | Strategy |
|---|---|---|
hierarchy.node.created | facility.hierarchy_node.created.v1 | Dual-publish 30d |
hierarchy.node.updated | facility.hierarchy_node.updated.v1 | Dual-publish 30d |
hierarchy.node.deactivated | facility.hierarchy_node.deactivated.v1 | Dual-publish 30d |
hierarchy.edge.created | facility.hierarchy_edge.created.v1 | Dual-publish 30d |
facility.node-config.created | facility.location.created.v1 | Dual-publish 30d |
facility.node-config.updated | facility.location.updated.v1 | Dual-publish 30d |
facility.bed.status-changed | facility.bed.status_changed.v1 | Dual-publish 30d |
facility.resource.created | facility.resource.created.v1 | Dual-publish 30d |
facility.resource.updated | facility.resource.updated.v1 | Dual-publish 30d |
5. Procedure (per tenant)
- Freeze window (announced 72h ahead): no hierarchy changes for 15min.
- Snapshot legacy DBs.
- Run ETL into new
facilityschema, preserving IDs. - Smoke-test read APIs; compare counts per tenant.
- Unfreeze; enable writes on new service with dual-publish.
- Monitor for 30 days; consumer cutover.
- Retire legacy DB; drop topics.
6. Rollback
- Retain legacy DBs read-only for 90 days.
- If regression detected: disable new service writes, route all traffic to legacy (events re-subscribed via inverse-bridge), investigate, redo ETL.
7. Client impact
| Surface | Impact | Mitigation |
|---|---|---|
| Admin UI | Endpoint URL moves from /hierarchy/* to /api/v1/hierarchy/* | UI config update |
| scheduling-service | Subject rename | Listener updated in same release train |
| Licensing | resolveEffectiveLicenses(nodeId) preserved | None — Licensing switches to RPC against new service |
| FHIR | /fhir/R4/Location URL unchanged | None |