Konzept: Eine Referenz ist kein Fremdschlüssel

Wenn eine Ressource auf eine andere zeigt, muss sie sich entscheiden, welchen der beiden Namen aus Konzept 01 sie benutzt. FHIR erlaubt beide, es erlaubt beide gleichzeitig, und es erlaubt, dass keiner von beiden auf etwas Existierendes zeigt.

Eine FHIR-Referenz ist kein Fremdschlüssel. Sie ist eine Behauptung über einen Zusammenhang, die der Standard weder prüfen lässt noch prüfen muss.

Wer aus der Datenbankwelt kommt, liest Encounter.subject als Foreign Key und erwartet eine Integritätsgarantie. Wer aus HL7 v2 kommt, liest sie als Gegenstück zu „PV1 steht hinter PID, also gehört es dazu” — also als etwas, das durch die Nachrichtenstruktur verbürgt ist. Beide Lesarten führen zum selben Betriebsbild: Daten, die vollständig vorhanden sind und trotzdem nirgends auftauchen.

Vier Arten, in FHIR auf etwas zu zeigen

Sie werden regelmäßig verwechselt, weil drei davon „Identifier” enthalten:

VerweisartBeispielZeigt auf
literale Referenzsubject.reference: "Patient/a1b2"eine Ressource an einer Adresse
logische Referenzsubject.identifier: {system, value}eine Ressource über ihren Geschäftsnamen
canonicalquestionnaire: "https://…/Questionnaire/x|2.1"eine Definition, über ihre kanonische URL
Attachment-URLcontent.attachment.urlNutzdaten — gar keine FHIR-Referenz

Davon zu unterscheiden ist Resource.identifier (etwa Appointment.identifier, DocumentReference.masterIdentifier): Das ist der Geschäftsname dieser Ressource und gehört auf die Achse von Konzept 01, nicht hierher.

Literal und logisch

ElementWas es trägtAuflösung durch den Empfänger
referenceeine URL — relativ oder absolutdirekt: ein GET genügt
identifierein Identifier (system + value)indirekt: Suche nötig, wenn sie überhaupt jemand ausführt
typeder erwartete RessourcentypPrüfhilfe
displayKlartext für Menschengar nicht

Keines der beiden ist Pflicht; eine Reference, die nur ein display trägt, ist strukturell gültig. Die Definition der logischen Referenz enthält ihre eigene Einschränkung:

„An identifier for the target resource. This is used when there is no way to reference the other resource directly, either because the entity it represents is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location.”

Der Abschnitt Logical References zählt die rechtfertigenden Lagen auf: kein Server, der eine solche Ressource bereitstellt; ein Server, der für die Quellanwendung nicht erreichbar ist; eine nicht-RESTful-Umgebung. Alle drei beschreiben Fälle, in denen eine Adresse nicht existiert oder nicht ermittelbar ist. Keiner beschreibt „die Adresse wäre ermittelbar, aber das kostet eine Abfrage”. Das ist keine Verbotsnorm — es steht kein SHALL NOT da —, aber eine deutliche Absichtserklärung.

Warum die Formwahl beim Empfänger ankommt

Die praktische Folge steht nicht im Referenzkapitel, sondern im Suchkapitel. Ein Referenz-Suchparameter matcht literale Referenzen:

„If the search parameter value is Patient/123, then this will find references like this: <patient><reference value="Patient/123"/></patient>”

Für die logische gibt es einen eigenen Modifier — und einen Satz, der die halbe Betriebsrealität beschreibt:

„References are also allowed to have an identifier. The modifier :identifier allows for searching by the identifier rather than the literal reference”

„Chaining is not supported when using the :identifier modifier, nor are chaining, includes or reverse includes supported for reference elements that do not have a reference element.”

Eine Referenz, die nur identifier trägt, ist damit:

  • von der gewöhnlichen Referenzsuche (?subject=Patient/123) nicht auffindbar,
  • über ?subject:identifier=<system>|<value> auffindbar, aber nur bei aktiver Anfrage,
  • für Chaining (?subject.name=…) unerreichbar,
  • für _include / _revinclude unerreichbar.

Der letzte Punkt trifft jede Anwendung, die eine Liste samt zugehöriger Ressourcen in einem Aufruf holt. Von den drei Bausteinen eines typischen Listenaufrufs funktioniert bei einer rein logischen Referenz keiner.

Die Wahl zwischen literaler und logischer Referenz ist keine Stilfrage. Sie entscheidet, welche Abfragen der Empfänger überhaupt noch stellen kann.

Bei canonical gibt es diesen Ausweg prinzipiell nicht: „The :identifier modifier is not supported on canonical elements since they do not have an identifier separate from the reference itself.” Die URL ist dort der Identifier.

Referentielle Integrität ist eine Serveroption

Kein Server muss die Existenz eines Referenzziels prüfen. Er muss nur ansagen, was er tut — in CapabilityStatement.rest.resource.referencePolicy, Kardinalität 0..*:

CodeZusage
literal„The server supports and populates Literal references (i.e. using Reference.reference)“
logical„The server allows logical references (i.e. using Reference.identifier).”
resolves„The server will attempt to resolve logical references to literal references”
enforced„The server enforces that references have integrity — e.g. it ensures that references can always be resolved.”
local„The server does not support references that point to other servers.”

Vier Punkte, die im Betrieb zählen:

  1. logical und resolves sind zwei verschiedene Zusagen. Ein Server mit logical, aber ohne resolves, nimmt logische Referenzen entgegen und lässt sie liegen. Das ist konform und der Normalfall.
  2. enforced ist optional. Ohne dieses Flag darf eine Referenz ins Leere zeigen, und der Server sagt nichts.
  3. Das Fehlen eines Flags ist eine Aussage. Wer die zwei genannten Codes als symmetrische Optionsliste liest, hat die eigentliche Information übersehen.
  4. 0..* erlaubt dem CapabilityStatement zu schweigen. Dann ist gar nichts zugesagt.

FHIR garantiert die Form der Referenz, nicht ihr Ziel. Ob am anderen Ende etwas liegt und ob der Empfänger es findet, sind zwei weitere, voneinander unabhängige Fragen — und beide werden außerhalb des Standards beantwortet.

Das ist die Fortschreibung des Satzes aus Konzept 01: FHIR macht genau die Fehlerklasse laut, die es typisieren kann — und nur die. Die Form der Referenz ist typisierbar und wird laut. Ihr Ziel ist es nicht.

Zwei Zustände, die man auseinanderhalten muss

Eine Referenz, die nicht auflösbar ist, und eine Referenz, die nicht durchsuchbar ist, sind zwei verschiedene Zustände. enforced adressiert den ersten. Der Regelfall im Betrieb ist der zweite: Das Ziel existiert, ist über seinen Geschäftsidentifier auffindbar — und die Beziehung ist trotzdem für keine Suche sichtbar.

Der Auflösungskontext relativer Referenzen

Derselbe String Patient/a1b2 bedeutet nicht überall dasselbe:

„The reference may be a relative reference, in which case it is relative to the service base URL, or, if processing a resource from a bundle, which is relative to the base URL implied by the Bundle.entry.fullUrl.”

Der Auflösungskontext hängt vom Behälter ab. Dieselbe Ressource, aus einem Bundle heraus und einzeln per GET gelesen, kann auf verschiedene Ziele zeigen.

In einer transaction kommt hinzu, dass der Server Referenzen umschreibt:

„When the server assigns a new id to any resource in the bundle which has a POST method as part of the processing rules above, it SHALL also update any references to that resource in the same bundle as they are processed”

Das greift nur für Referenzen auf die fullUrl eines Eintrags desselben Bundles. Eine Referenz ohne reference-Element ist von der Mechanik gar nicht erfasst — es gibt keinen Link zum Ersetzen.

Conditional reference — literale Referenz ohne Vorab-Suchlauf

„Because of this, in a transaction (and only in a transaction), references to resources may be replaced by a search URI that describes how to find the correct reference”

„When processing transactions, servers SHALL: check all references for search URIs… if there are no matches, or multiple matches, the transaction fails… if there is a single match, the server replaces the search URI with a reference to the matching resource”

Drei Eigenschaften: Gespeichert wird eine literale Referenz — die Suchform existiert nur auf der Strecke. Mehrdeutigkeit wird zum Abbruch, nicht zur stillen Fehlzuordnung. Es funktioniert nur in einer transaction, nicht in einem batch.

Wann die logische Referenz die richtige Wahl ist

Die Lehre „logische Referenzen sind ein Antipattern” ist falsch. ISiK schreibt sie an prominenter Stelle verpflichtend vor: Die Fallnummer wird nicht am Encounter geführt, sondern am Account, und damit Subsysteme, die keine Account-Ressource implementieren, trotzdem an sie kommen, gilt:

„Um insbesondere Subsysteme von der Pflicht zu entbinden, die Account-Ressource zu implementieren, nur um Zugriff zur Fallnummer zu bekommen, ist das Mitführen des Account-Identifiers als logische Referenz auf den Account im Encounter verpflichtend.”

Die Begründung ist der Schlüssel: Die logische Referenz steht dort, damit ein Empfänger etwas erfährt, ohne einer Adresse folgen zu müssen.

logische Referenzliterale Referenz
Der Empfänger braucht …den Wert selbstden Gegenstand samt seiner Nachbarn
Sie ersetzt …einen sonst nötigen Auflösungsschrittnichts — sie ist der Zugriffsweg

Steht der Identifier für eine Auskunft, ist die logische Referenz richtig. Steht er für einen Zugriffsweg, ist sie eine Sackgasse mit korrekter Syntax.

Wer daraus einen Änderungsantrag ableitet („ISiK schreibt es doch vor”), überträgt die Zulässigkeit, nicht den Zweck — und landet bei erlaubt ist nicht dasselbe wie vorgesehen.

Die Sprosse, auf der eine Referenz spricht

In der Ebenenleiter sitzt „dieser Aufenthalt gehört zu diesem Patienten” auf Referenzgeflecht. Diese Sprosse hat eine Eigenschaft, die keine andere hat:

Eine Aussage auf der Referenzebene ist nur dann eine Aussage, wenn der Empfänger sie auflösen kann. Eine Referenz, die niemand auflöst, ist strukturell vorhanden und semantisch abwesend.

Bei Wert, Element und Ressource gibt es diesen Zwischenzustand nicht — dort ist etwas da oder nicht da.

In HL7 v2 kann die Beziehung nicht kaputtgehen, ohne dass die Nachricht kaputtgeht. In FHIR schon. Dort ist die Zusammengehörigkeit durch die Nachrichtenstruktur verbürgt; hier ist sie ein eigenes Datum, das getrennt gespeichert, getrennt beschädigt und getrennt übersehen werden kann.

Portalbezug

Eine Anwendung, die Listen zu einem Patienten aufbaut, benutzt in aller Regel Referenzsuche, _include und gelegentlich Chaining gleichzeitig. Alle drei setzen literale Referenzen voraus. Wird auf der Zulieferstrecke von literal auf logisch umgestellt — etwa um einen Vorab-Suchlauf einzusparen —, bleiben Bestand und Zählwerte unverändert, während die patientenbezogene Ansicht leer läuft. Der Zustand erzeugt keinen Fehlercode und wird von einem Monitoring, das Statuscodes und Queue-Längen beobachtet, nicht gefunden.

Der Effekt, der Bestandsdaten einholt

Eine Umstellung trifft nicht nur neue Vorgänge. Wird eine bestehende Ressource fortgeschrieben — bei einer Verlegung, einer Statusänderung, jedem Update —, überschreibt der Sender das ganze subject-Element. Die literale Referenz, die dort stand, wird dabei gelöscht.

Korrekt geschriebene Bestandsdaten überleben eine solche Umstellung genau bis zur ersten Fortschreibung. Deshalb sieht ein solcher Defekt in den ersten Tagen nach dem Rollout harmloser aus, als er ist.

Beispiel (synthetisch)

Fassung A — logische Referenz:

{
  "resourceType": "Encounter",
  "status": "in-progress",
  "class": {"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "IMP"},
  "subject": {
    "type": "Patient",
    "identifier": {"system": "https://fhir.klinik-synth.example/sid/patientennummer", "value": "100045"},
    "display": "Ehrlich, Marlene"
  },
  "period": {"start": "2026-08-19T09:14:00+02:00"}
}

Vollständig konform, wird mit 201 Created angenommen, für einen Menschen sofort lesbar. Eine Suche ?subject=Patient/a1b2c3 findet sie nicht — und liefert keinen Fehler, sondern:

{"resourceType": "Bundle", "type": "searchset", "total": 0, "entry": []}

Fassung B — conditional reference, in derselben Transaktion aufgelöst:

{
  "subject": {
    "reference": "Patient?identifier=https%3A%2F%2Ffhir.klinik-synth.example%2Fsid%2Fpatientennummer%7C100045",
    "type": "Patient"
  }
}

Der Server sucht selbst, findet genau einen Patienten und ersetzt die Such-URI beim Speichern durch "reference": "Patient/a1b2c3". Findet er keinen oder mehrere, scheitert die ganze Transaktion — laut und mit OperationOutcome.


Einordnung: Kategorie 4, und die Trennlinie zu 3

Bedingung 1 — beide Seiten konform: Der Sender schickt eine standardkonforme Ressource, der Server nimmt sie konform an (logical ist zugesagt), der Empfänger führt eine konforme Referenzsuche aus.

Bedingung 2 — die Abbildung ist falsch: Die Information liegt vollständig vor und wird vollständig übertragen; die Abbildung legt sie in ein Element, das die Zielarchitektur nicht abfragt, und nimmt ihr damit die Eigenschaft, für die sie übertragen wird.

Nächstliegende Alternative: 3 (Empfänger rät) — scheitert an beiden Bedingungen. Die Aussage ist eindeutig, nicht mehrdeutig. Und der Empfänger wählt nicht; er findet nichts.

Bei 3 fehlt der Aussage etwas, das der Empfänger braucht, und er ergänzt es durch eine Wahl. Bei 4 ist die Aussage vollständig und liegt an einer Stelle, an der der Empfänger nicht nachsieht. Kurzform: 3 ist ein Mangel im Inhalt, 4 ein Mangel in der Form.

Ein operativer Test dazu, der in beide Richtungen hilft:

Ein ratender Empfänger produziert ein falsches Ergebnis, kein leeres. Wo etwas Falsches angezeigt wird, hat jemand gewählt; wo nichts angezeigt wird, hat niemand gewählt. Das ist ein notwendiges, kein hinreichendes Merkmal für Kategorie 3 — auch ein Mapping-Fehler kann Falsches produzieren.

Und zur Frage, ob ein schweigendes Schnittstellenkonzept die Kategorie kippt:

Ein CapabilityStatement ist ein Bezugsdokument, auch wenn niemand es gelesen hat. Wo das Schnittstellenkonzept zur Referenzform schweigt, kann die Zusage trotzdem woanders stehen — und logical ohne resolves beantwortet die Frage.

Offener Gegeneinwand: Ein CapabilityStatement beschreibt, was ein Server tut, nicht, was ein Sender soll — als Sendevorschrift taugt es streng genommen nicht. Der Einwand ist ernstzunehmen und hier nicht abschließend entschieden.

Der stille Sonderfall: die Referenz auf das Falsche

Eine literale Referenz auf eine existierende, aber falsche Ressource wird von keinem der beschriebenen Mechanismen gefangen. Sie ist syntaktisch gültig, auflösbar auch bei enforced, wird von der Suche gefunden, und _include liefert ein vollständiges Zielobjekt mit Namen und Geburtsdatum. Kein Statuscode, kein OperationOutcome, kein Queue-Eintrag.

Eine unauflösbare Referenz kostet eine Anzeige. Eine falsch aufgelöste kostet eine Patientenakte.

Wird eine Referenz aus einem Geschäftsidentifier gebildet, dessen Eindeutigkeit im relevanten Geltungsbereich nicht bewiesen ist, ist die Fehlzuordnung kein Restrisiko, sondern eine Frage der Zeit.


Transfer

Der Defekt ist der Gegenfall zum Konformitätsverstoß aus Konzept 01: Jede Zeile im Log sieht gesund aus. Ressourcen sind vorhanden, konform, vollständig und zählbar — nur die Frage „welche Kontakte hat dieser Patient?” wird mit total: 0 beantwortet. Eine Auswertung auf diesem Bestand bekommt keinen kaputten Datensatz vorgelegt, sondern einen plausiblen und leeren.

Für Datenqualitätsrahmen folgt daraus eine Einschränkung: Der Defekt ist an keinem einzelnen Messpunkt sichtbar. Quellbestand, Zielbestand und Auswertung können je für sich einwandfrei geprüft werden — er entsteht zwischen zwei Messpunkten. Neben der Frage, in welche Kategorie ein Defekt fällt, gehört deshalb die Frage erhoben, an welchem Messpunkt er überhaupt auftaucht.

Fragen an die Kunden-IT

  1. Welches referencePolicy weist das CapabilityStatement des Gegenübers aus — und ist resolves oder enforced dabei?
  2. Werden Referenzen auf der Strecke literal, logisch oder in beiden Formen gesetzt, und ist das irgendwo schriftlich vereinbart?
  3. Wenn eine Suche nach Zugehörigkeit leer bleibt: Ist geprüft, ob die betroffenen Ressourcen ein reference-Element tragen?
  4. Werden Bundles als transaction oder als batch verarbeitet — und ist bekannt, dass conditional references nur im ersten Fall aufgelöst werden?
  5. Aus welchem Identifier wird eine Referenz gebildet, und in welchem Geltungsbereich ist dessen Eindeutigkeit belegt?
  6. Nutzt die Zielanwendung _include oder Chaining? Wenn ja, ist die Referenzform damit festgelegt, nicht mehr wählbar.

Lektüre & Belege