FHIR — Grundlagen für Leute, die aus HL7 v2 kommen
Der Umstieg ist weniger eine neue Syntax als ein anderes Denkmodell. Wer v2 kann, hat den schwierigen Teil (Semantik im Gesundheitswesen) schon — hier steht, was sich strukturell ändert.
Der Modellwechsel in einem Satz
v2 überträgt Ereignisse, FHIR verwaltet Zustände. Eine ADT^A01 sagt „dieser Patient wurde aufgenommen”; ein Encounter sagt „dieser Kontakt existiert und hat den Status in-progress”. Daraus folgt fast alles Weitere: Ein Ereignis schickt man einmal, einen Zustand schreibt man idempotent — sonst legt die zweite Nachricht denselben Patienten ein zweites Mal an.
| v2 | FHIR | Anmerkung |
|---|---|---|
| Segment (PID, PV1) | Ressource (Patient, Encounter) | keine 1:1-Abbildung |
Feld (PID-5) | Element (Patient.name) | FHIR-Elemente sind typisiert und oft strukturiert |
| Nachricht | Bundle | Typ transaction bei uns |
Trigger (A01) | Status + Interaktion | der Trigger geht in Status/Klasse auf |
| Trennerhierarchie | JSON/XML | kein MSH-2-Problem mehr |
Version: ISiK ist R4
ISiK baut auf FHIR R4, konkret 4.0.1. R5 existiert und ist nicht abwärtskompatibel — es spielt hier keine Rolle. Wer Beispiele aus dem Netz übernimmt, muss die Release-Angabe prüfen; R5-Snippets sehen fast gleich aus und sind es nicht. [Fakt, gematik/Simplifier, 28.07.2026]
Profil, IG, Paket — die drei Wörter, die man auseinanderhalten muss
- StructureDefinition (Profil) — die Einschränkung einer Basis-Ressource: welche Elemente Pflicht sind, welche Codes erlaubt, wie Slices aussehen.
- Implementation Guide (IG) — die menschenlesbare Spezifikation drumherum: Profile + Prosa + Beispiele.
- Paket — die maschinenlesbare Auslieferung (npm-artig,
de.gematik.isik-basismodul,de.basisprofil.r4), mit Versionsnummer.
Der Validator kennt nur die dritte Ebene. Genau daraus entsteht die wichtigste Regel aus Terminologien und Codes: Ein Code ist nur gegen eine konkrete Paketversion wahr.
Zwei Fallstricke, die uns real getroffen haben [Fakt, ADR-002]:
- Kanonische Profil-URLs tragen bei ISiK Stufe 3 ein
/v3/im Pfad (…/isik/v3/Basismodul/StructureDefinition/ISiKPatient) — die Version steckt in der URL, nicht nur in den Metadaten. - Geladene Pakete landen im NPM-Cache des Servers, nicht als Server-Ressourcen.
GET /fhir/StructureDefinition?...findet sie deshalb nicht, obwohl die Validierung einwandfrei damit arbeitet. Wer über diesen Weg prüft, ob ein Profil geladen ist, kommt zum falschen Schluss.
Must-Support ist kein Pflichtfeld
Der meistmissverstandene Marker. Drei verschiedene Dinge:
- Kardinalität
1..1— das Element muss da sein, sonst ist die Instanz invalide. - Must-Support — der Empfänger muss damit umgehen können, wenn es kommt. Es zwingt den Sender zu nichts.
extensibleBinding — der Code soll aus dem ValueSet kommen, darf aber begründet abweichen.
„ISiK verlangt das Feld” ist deshalb selten die richtige Aussage. Die richtige lautet: „Kardinalität X, Must-Support ja/nein, Binding-Stärke Y.” [Fakt]
Idempotenz: die praktisch wichtigste Technik
Ein Feed schickt dieselbe Fachlichkeit mehrfach — S12 legt den Termin an, S13 verschiebt ihn, S15 storniert ihn, und alle drei tragen denselben Patienten. Mit POST entstünden drei Patienten.
Die Lösung im Projekt (ADR-003) ist der Conditional Update: PUT Patient?identifier=… — „schreibe den Patienten mit dieser Kennung; existiert er, aktualisiere, sonst lege an”. Der fachliche Schlüssel steuert die Identität, nicht die technische FHIR-id. Damit ist jede Wiederholung derselben Nachricht folgenlos, und genau das braucht ein Feed, der auch mal doppelt liefert.
Dazu Transaction-Bundles mit urn:uuid-Referenzen: Ressourcen, die einander referenzieren, werden in einem atomaren Schritt geschrieben, die temporären UUIDs löst der Server auf. Bei uns nicht optional — Einzel-POSTs scheiterten am AkteurPatient-Slice, weil actor.resolve() die noch nicht existierende Referenz nicht auflösen konnte. [Fakt]
REST in fünf Zeilen
GET /Patient/123 lesen
GET /Patient?identifier=… suchen (Suchparameter sind Teil des Profils!)
POST /Patient anlegen (Server vergibt id)
PUT /Patient/123 ersetzen
PUT /Patient?identifier=… Conditional Update — siehe oben
POST / (Bundle type=transaction) mehreres atomar
POST /Patient/$validate gegen Profile prüfen
Suchparameter sind Konformitätsgegenstand, kein Komfort: ISiK schreibt vor, wonach ein bestätigtes System suchbar sein muss. Ein Server, der die Daten hat, sie aber nicht unter dem geforderten Parameter findet, ist nicht konform. [Fakt]
Was der Umstieg nicht löst
FHIR macht das Format eindeutig, nicht die Semantik. Ob die Fallnummer in Account oder Encounter gehört, ob die KVNR 10- oder 30-stellig gemeint ist, ob ein Haus „Fachabteilung” als PV1-3 oder PV1-10 sendet — all das bleibt genau so ungeklärt wie in v2. Die Interoperabilitätsarbeit verschiebt sich von der Syntax zur Semantik; sie verschwindet nicht. Deshalb wird HL7 v2 auf Jahre nicht abgelöst: selbst Dedalus sagt öffentlich, Integrationen liefen „bisher auf Basis von HL7 V2” (siehe den eigenen Vendor-Notizen). [Fakt für das Zitat, Einordnung Hypothese]
- Quelle / Datum: ADR-002, ADR-003; ISiK Basis Stufe 5 (Simplifier); FHIR-R4-Spezifikation 4.0.1 — Recherche 28.07.2026
- Sicherheit der Aussage: [x] Fakt (Versionslage, Profil-/Paketmechanik, Conditional Update, die beiden ADR-002-Fallstricke) [x] Hypothese (Einordnung zur Ablösung von v2) [ ] Spekulation