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.

v2FHIRAnmerkung
Segment (PID, PV1)Ressource (Patient, Encounter)keine 1:1-Abbildung
Feld (PID-5)Element (Patient.name)FHIR-Elemente sind typisiert und oft strukturiert
NachrichtBundleTyp transaction bei uns
Trigger (A01)Status + Interaktionder Trigger geht in Status/Klasse auf
TrennerhierarchieJSON/XMLkein 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.
  • extensible Binding — 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