Wir betreiben Master Data Management in MDMbox seit zwei Jahren im produktiven Einsatz – wir deduplizieren Patienten, fusionieren Organisationen und gleichen Ärzte über verschiedene Einrichtungen hinweg ab. Dabei haben wir gelernt, dass jede Organisation anders vorgeht und kein einziger serverseitiger Algorithmus die Vielfalt realer Merge-Richtlinien abdecken kann.
FHIR R5 führte Patient/$merge ein – eine lang erwartete Operation, mit der sich doppelte Patientendatensätze zusammenführen lassen. Sie definiert Eingabeparameter (source-patient, target-patient, result-patient), erwartet vom Server die Handhabung von Referenz-Updates und gibt das zusammengeführte Ergebnis zurück. Die Operation befindet sich derzeit auf Reifegrad 0 und wurde seit ihrer Einführung nicht weiterentwickelt.
Das ist ein guter Anfang. Nach der Implementierung von Merge im Produktivbetrieb haben wir jedoch festgestellt, dass die Spezifikation nicht weit genug geht.
Das Problem mit servergesteuertem Merge
Die FHIR-$merge-Spezifikation setzt voraus, dass der Server weiß, wie zusammengeführt wird. Der Server deaktiviert die Quelle, kopiert Identifier und aktualisiert Referenzen. Die Merge-Logik variiert jedoch von Organisation zu Organisation erheblich:
- Krankenhaus A löscht den Quell-Patienten vollständig und überschreibt alle Referenzen
- Krankenhaus B behält die Quelle als inaktiv und erstellt eine Linkage-Ressource zur Nachvollziehbarkeit
- Krankenhaus C führt Encounters und Observations in das Ziel zusammen, bewahrt aber separate AllergyIntolerance-Datensätze zur manuellen Prüfung
- Gesundheitsinformationsverbund D muss Organizations und Practitioners zusammenführen, nicht nur Patients
Nichts davon ist durch die Spezifikation abgedeckt – sie ist auf Patient beschränkt, und der Schritt „alle Referenzen aktualisieren" ist nur vage umrissen.
Was wir benötigten, war eine Merge-Operation, bei der:
- Der Client entscheidet, was geschieht (welche Ressourcen aktualisiert, erstellt oder gelöscht werden)
- Der Server Atomarität garantiert, einen Audit-Trail erstellt und Sicherheitsprüfungen durchsetzt
- Sie für beliebige Ressourcentypen funktioniert, nicht nur für Patient
Unser Ansatz: der plan-Parameter
Wir haben ein FHIR-kompatibles $merge entwickelt, das ein Standard-Transaktions-Bundle als zusätzlichen plan-Parameter akzeptiert:
POST $merge
{
"resourceType": "Parameters",
"parameter": [
{"name": "source", "valueReference": {"reference": "Patient/123"}},
{"name": "target", "valueReference": {"reference": "Patient/456"}},
{"name": "preview", "valueBoolean": false},
{
"name": "plan",
"resource": {
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"resource": {"resourceType": "Patient", "id": "456", "...": "merged state"},
"request": {"method": "PUT", "url": "Patient/456", "ifMatch": "W/\"3\""}
},
{
"resource": {"resourceType": "Encounter", "id": "789",
"subject": {"reference": "Patient/456"}},
"request": {"method": "PUT", "url": "Encounter/789", "ifMatch": "W/\"1\""}
},
{
"request": {"method": "DELETE", "url": "Patient/123", "ifMatch": "W/\"2\""}
}
]
}
}
]
}
Der Client sendet jedes PUT, POST und DELETE explizit. Der Server ergänzt den Plan um Audit-Ressourcen (Task + Provenance) und führt alles als einzelne FHIR-Transaktion aus. Entweder gelingt alles – einschließlich des Audit-Trails – oder nichts wird durchgeführt.
Beachten Sie die ifMatch-Header bei jedem Eintrag – dies ist optimistisches Sperren. Wurde eine Ressource zwischen dem Zeitpunkt der Planerstellung durch den Client und dem Zeitpunkt der Ausführung verändert, schlägt die Transaktion fehl. Keine stillen Überschreibungen, keine verlorenen Aktualisierungen.
Eine Obermenge, keine Abspaltung
Sie werden bemerken, dass wir source, target und result verwenden statt der in der Spezifikation vorgesehenen source-patient, target-patient und result-patient. Das ist beabsichtigt – die Operation ist ressourcentyp-agnostisch, sodass patientenspezifische Bezeichnungen irreführend wären. Doppelte Organizations zusammenführen? Practitioners? Dieselbe Operation, derselbe Audit-Trail.
Lässt man den plan-Parameter weg und übergibt stattdessen result, erhält man das Standard-FHIR-$merge-Verhalten. Identifier-basierte Suche für Quelle und Ziel wird unterstützt. Der plan ist das, was die Operation deutlich leistungsfähiger macht: Wenn er vorhanden ist, übernimmt der Client die vollständige Kontrolle über die Merge-Logik.
Das fehlende Stück: $referencing
Um einen guten Merge-Plan zu erstellen, muss der Client eine Frage beantworten: „Welche Ressourcen verweisen auf Patient/123?"
FHIR bietet _revinclude für Suchergebnisse und $everything für Patient, aber keine generische Operation, die lautet: „Gib mir alle Ressourcen in der Datenbank, die auf diese Referenz zeigen." Wir haben eine solche entwickelt.
Intern handelt es sich um eine PostgreSQL-Abfrage, die JSONPath verwendet, um über Ressourcentabellen hinweg zu suchen:
SELECT id, resource_type, resource
FROM <resource_table>
WHERE jsonb_path_query_array(resource, '$.** ? (@.reference == "Patient/123")')
!= '[]'::jsonb
Die obige Abfrage ist zur Veranschaulichung vereinfacht, der Kerngedanke bleibt jedoch derselbe: $.** durchläuft rekursiv die gesamte Ressourcenstruktur und findet jedes verschachtelte Objekt, in dem reference dem Ziel entspricht – unabhängig davon, an welcher Stelle im Ressourcenbaum es sich befindet. Keine hartcodierten Pfade, keine ressourcentypspezifische Konfiguration. Eine einzige Abfrage findet Referenzen in Encounter.subject, Observation.performer, Claim.provider oder jedem anderen Referenzfeld.
Zur Performance: Diese Abfrage verwendet einen GIN-Index auf der JSONB-Spalte, sodass die rekursive Traversierung gegen den Index erfolgt – nicht als vollständiger Tabellen-Scan. Bei einem typischen Merge schließt die Operation in Millisekunden ab. Bei Ressourcentypen mit Millionen von Zeilen parallelisieren wir die Suche über Tabellen hinweg und streamen die Ergebnisse zurück an den Client.
Wir haben dies als eigenständige $referencing-Operation in MDMbox bereitgestellt – ein Baustein, den jeder Merge- oder Auswirkungsanalyse-Workflow nutzen kann.
Task + Provenance: mehr als nur Audit
Jeder Merge erstellt einen Task und eine Provenance innerhalb derselben Transaktion. Das ist mehr als Compliance:
Subscriptions. Externe Systeme können die Erstellung von Merge-Tasks abonnieren und sofort reagieren – Caches aktualisieren, nachgelagerte Workflows auslösen, Benutzer benachrichtigen. Standard-FHIR-Subscriptions, kein individuell entwickeltes Plumbing.
Versionierte Snapshots. Die Provenance erfasst eine versionierte Referenz (z. B. Patient/456/_history/3) für jede durch den Merge geänderte oder gelöschte Ressource. In Kombination mit der History-API von FHIR ist der vollständige Zustand vor dem Merge jederzeit wiederherstellbar.
Lifecycle-Tracking. Der Task protokolliert den Merge-Status (requested → in-progress → completed oder failed), wer ihn ausgelöst hat, wann er stattfand, sowie Verknüpfungen zu Quelle und Ziel. Die Abfrage „Zeige mir alle Merges, die diesen Patienten betroffen haben" ist eine einzige FHIR-Suche auf Task.
Ausblick: $unmerge
FHIR definiert noch keine Unmerge-Operation. Betrachten Sie folgendes Szenario: Ein Merge wird am Montag ausgeführt, und am Mittwoch stellt jemand fest, dass es sich um zwei verschiedene Personen handelte. Zu diesem Zeitpunkt wurden bereits 50 Ressourcen geändert – Encounters wurden umgeleitet, Observations neu zugeordnet, der Quell-Patient gelöscht.
Mit dem Task, der den Merge-Lifecycle verfolgt, der Provenance, die jede betroffene Ressource mit ihrer Version vor dem Merge erfasst, und der History-API, die alle früheren Zustände bewahrt – haben wir alles, was nötig ist, um einen Merge zuverlässig rückgängig zu machen. Die $unmerge-Operation liest die Provenance, ruft die Versionen vor dem Merge aus der History ab und erstellt ein umgekehrtes Transaktions-Bundle, das jede Ressource in ihren früheren Zustand zurückversetzt.
Wir werden $unmerge im nächsten Beitrag behandeln – jetzt veröffentlicht: Designing $unmerge: Reversing the FHIR Patient Merge. Der zweite Beitrag in der Reihe, MPI Vendors Couldn't Agree. We Solved Patient Merge, erläutert, warum ein einziges servergesteuertes $merge nicht alle Anforderungen im Produktivbetrieb erfüllen kann. Hintergrundinformationen zu den zugrunde liegenden Master Patient Index-Grundlagen finden Sie unter Master Patient Index (MPI): How It Works + Examples.
Möchten Sie clientgesteuertes Merge in Ihrem Projekt ausprobieren? MDMbox ist bereits heute verfügbar.



