Entfernt man aus einer FHIR-Ressource alle kodierten Elemente, bleibt nur noch Infrastruktur übrig: IDs, Referenzen, Datumsangaben, Statuswerte. Was die Diagnose ist, was beobachtet wurde, was verordnet wurde — die Bedeutung von Gesundheitsdaten steckt in der Terminologie. Die Struktur sagt Ihnen, dass eine Condition existiert und zu wem sie gehört; die Codes sagen Ihnen, was sie tatsächlich ist.
Und nahezu überall, wo Bedeutung in FHIR auftaucht, erscheint sie als ein einziger Datentyp: CodeableConcept — Condition.code, Observation.code, Procedure.code, MedicationRequest.medication, AllergyIntolerance.code. Wer diesen einen Typ korrekt lesen kann, hat gelernt, die Semantik von FHIR zu lesen; wer ihn falsch liest, erhält eine strukturell einwandfreie Ressource, während die Bedeutung still verlorengeht.
Wie liest und interpretiert man CodeableConcept also korrekt? Zum Beispiel, wenn man eine Diagnose in SNOMED CT betrachtet?
Der erste Gedanke wäre, code.coding.where(system='...snomed...').code.first() zu verwenden.
Das ist jedoch falsch. Das habe ich in einem Meeting der HL7 FHIR-I-Arbeitsgruppe von Lloyd McKenzie gelernt.
Es kann durchaus zwei oder drei SNOMED-Codes geben, und das muss unterstützt werden.
Um zu verstehen warum — und was stattdessen zu tun ist — lohnt es sich, einen Schritt zurückzugehen und kodierte Daten als Gespräch zwischen zwei Parteien zu betrachten, die sich niemals begegnen: derjenigen, die den Code schreibt, und derjenigen, die ihn liest.
Was es bedeutet, Daten zu kodieren
Kodieren bedeutet, Bedeutung durch einen Zeiger in ein gemeinsames Wörterbuch zu ersetzen: ein Konzept aus einem Codesystem. Ein Code ohne System ist bedeutungslos — das Paar (System, Code) ist die Bedeutungseinheit. Das Ziel ist, dass zwei Systeme, die nichts voneinander wissen, die Daten auf dieselbe Weise verstehen.
Drei Dinge erschweren das Bild sofort:
Konzepte haben Granularität. Sie leben in Hierarchien: „Diabetes" ⊃ „Typ-2-Diabetes" ⊃ „Typ-2-Diabetes, gut eingestellt". Eine Ebene zu wählen ist eine Entscheidung darüber, wie viele Informationen erfasst — und wie viele verloren — werden.
Codesysteme basieren auf unterschiedlichen Prinzipien. ICD ist eine Klassifikation: Ihr Ziel ist es, jeden Fall in sich nicht überschneidende Gruppen ohne Rest einzusortieren (für Statistik und Abrechnung) — daher die „sonstige"- und „nicht näher bezeichnete"-Kategorien. Ein Fall → genau eine Kategorie. SNOMED CT ist eine Ontologie: Ihr Ziel ist es, die klinische Realität zu beschreiben — Hierarchien mit mehreren übergeordneten Konzepten, formale Definitionen, komponierbare Ausdrücke. Ein Fall → so präzise eine Beschreibung wie nötig. Sie beantworten unterschiedliche Fragen: ICD — „welche Kategorie für den Bericht", SNOMED — „was ist das". Das Mapping zwischen ihnen ist von Natur aus verlustbehaftet und viele-zu-viele.
Manchmal existiert das Konzept nicht — aber man kann es aufbauen. Das ist Post-Koordination: Die Grammatik des Codesystems erlaubt es, ein Konzept aus bestehenden zusammenzusetzen. SNOMED-CT-Ausdruck 81745001:272741003=7771000 — „Auge : Lateralität = links". Der Ausdruck wird vollständig in einen einzigen Coding.code geschrieben — die Spezifikation ist eindeutig, dass „jeder durch das Codesystem definierte Ausdruck weiterhin als ein ‚Code' gilt und als solcher dargestellt wird":
{
"system": "http://snomed.info/sct",
"code": "128045006:{363698007=56459004}"
}
Hinweis: Das Ergebnis ist immer noch ein Code. Es sind nicht zwei Codings „Auge" + „links" in einem Array — wir werden weiter unten sehen, warum das unzulässig ist.
Was CodeableConcept tatsächlich ist
CodeableConcept trägt eine Bedeutung und hält ihre Repräsentationen in einem coding-Array sowie einem menschenlesbaren text.
{
"coding": [
{ "system": "http://loinc.org", "code": "8867-4", "display": "Heart rate" },
{ "system": "http://snomed.info/sct", "code": "364075005", "display": "Heart Rate" }
],
"text": "Heart rate"
}
Der Kerngedanke: Mehrere Codings sind nicht mehrere Konzepte. Ein Konzept, in verschiedenen „Sprachen" für verschiedene Empfänger geschrieben. Ein ICD-10-Code für den Kostenträger, ein SNOMED-CT-Code für das klinische System, ein lokaler Code zum Debugging — alles in einem Feld.
Warum braucht ein Feld das alles? Wegen der Beteiligten auf beiden Seiten.
Die Lücke: ein Schreiber, viele Leser
Hier liegt die zentrale Asymmetrie des Austauschs kodierter Daten: Der Produzent schreibt einmal — die Konsumenten sind viele, und sie wollen unterschiedliche Dinge. Der Regulierer will grobe Kategorien für einen Bericht, der Forscher will maximale Details, die Abrechnung braucht ICD, die klinische Entscheidungsunterstützung braucht SNOMED. Lloyd McKenzie in der FHIR-I-Sitzung:
Es kann durchaus zwei oder drei SNOMED-Codes geben, und das muss unterstützt werden. Denn es wird Situationen geben, in denen jemand einen Implementation Guide hat, der besagt, dass dies nur mit übergeordneten Codes erfasst wird... Ich brauche diesen Code, damit ich den Regierungsbericht erstellen kann. Und dann gibt es einen anderen Anwendungsfall: Ich möchte wissen, welche Art von Krebs es genau war, ich möchte das Stadium wissen, ich möchte wissen, ob es ein Rezidiv ist oder nicht. Und man hat zwei verschiedene Systeme, die beide leicht unterschiedliche Informationen benötigen, und der einzige Weg, das zu lösen, ist, beide Codes zu senden.
Und der Produzent kann nicht einen „für alle" auswählen:
Wenn Sie eine Instanz erstellen, erstellen Sie sie für alle Empfänger — wir geben allen dasselbe aus. Ein solches Verbot ist also nicht vertretbar.
Diese Asymmetrie erklärt jede „seltsame" Eigenschaft von CodeableConcept:
Es gibt keinen „Haupt"-Code — weil „Haupt" eine Eigenschaft des Paars (Daten, Konsument) ist, nicht der Daten selbst. Der Produzent hat nicht das Recht, für alle Leser zu entscheiden. Ich fragte Lloyd direkt, ob es ein Flag für den präzisesten Code gibt:
Wir haben ein Kernelement, mit dem man angeben kann, dass dies der vom Benutzer ausgewählte Code ist. Aber in der Situation, in der Post-Kodierung erfolgt, gibt es keinen Mechanismus, um anzugeben, welcher der spezifischste ist. Und es kann Umstände geben, in denen sie gleich spezifisch sind, aber einfach unterschiedliche Dinge aussagen.
Ein einziger „präzisester" Code existiert oft gar nicht:
Beide haben mehr Details als der Basiscode, aber man kann keinen davon als den detailliertesten markieren, und es gibt in SNOMED keinen Code für sitzenden digitalen arteriellen rechten systolischen Blutdruck. Der existiert nicht.
Reihenfolge bedeutet nichts — der CI-Build der Spezifikation stellt das normativ fest: „Ordering of codings is undefined and SHALL NOT be used to infer meaning." CodeableConcept.coding hat kein orderMeaning (Zulip-Thread). .first() greift also nicht den Hauptcode — sondern einen zufälligen.
Zusätzliche Codes sind keine Synonyme. Elliot Silver stellte in derselben Sitzung genau diese Falle auf:
Angenommen, wir haben Ihr CodeableConcept, das mit Lungenkrebs, Krebs und Lungenerkrankungen kodiert ist. Mein System erkennt Lungenkrebs nicht. Es erkennt Lungenerkrankungen und es erkennt Krebserkrankungen... Ich werde eine Übereinstimmung für Lungenerkrankungen finden und das ist der Code, den ich behalte. Und ich werde davon ausgehen, dass alles andere darin ein Synonym ist...
Lloyd McKenzies Korrektur trifft den Kern der Sache:
Es kodiert ein Konzept, drückt aber nicht unbedingt mit jedem Code dieselbe Information aus... ein Code vermittelt einen Aspekt oder eine Menge von Informationen, die der andere nicht vermittelt, und umgekehrt. Das passiert mit Codesystemen: Sie haben unterschiedliche Präzisionsstufen und unterschiedliche Ausdrucksfähigkeiten.
Wenn Elliots System also stillschweigend Lungenerkrankung auswählt und Lungenkrebs als „Synonym" verwirft, landet in der Analysedatenbank „Lungenerkrankung" statt „Lungenkrebs" — und niemand bemerkt es.
Die Lücke erstreckt sich auch über die Zeit. Der heutige Konsument kennt die morgigen Codes im selben Feld nicht. Lloyd:
Empfänger, die anfangs nur mit einem oder zwei Codes umgehen mussten, könnten irgendwann mit 7 oder 8 Codes konfrontiert werden. Als Konsument können Sie nicht davon ausgehen, dass das, was Sie heute sehen, in Zukunft gleich bleibt.
Warum hat FHIR dieses Modell gewählt? Grahame Grieve:
Die Erwartung und der Wunsch in FHIR ist, dass die Informationen in mehreren Umgebungen ohne spezifischen Kontext und ohne individuelle Vereinbarung robust sind, weil die Menschen die Informationen ohnehin so verwenden. Das hat seinen Preis — und hier sind wir.
Der Preis fällt asymmetrisch aus: Der Produzent zahlt wenig (alles ins Array packen), der Konsument zahlt viel (eine Auswahlrichtlinie, ein Terminologieserver, Mappings). CodeableConcept ist im Wesentlichen ein Protokoll zur Kommunikation über die Lücke hinweg: Der Produzent maximiert die Chance, verstanden zu werden; der Konsument ist verpflichtet zu wissen, wie er auswählt.
Schreiben wir also die Regeln für jede Seite auf.
Die eine verbindliche Regel, die das Array bindet
Vor den Regeln — die einzige Einschränkung, die die Spezifikation für mehrere Codings festlegt. Sie wurde im Thread Multiple codes in CodeableConcept (#terminology, 113 Nachrichten) und in der FHIR-I-Sitzung erarbeitet, woraus zwei JIRA-Tickets entstanden — FHIR-50188 und FHIR-50189 — sowie ein neu geschriebener Abschnitt der Spezifikation.
Die veröffentlichte Spezifikation (bis R5) besagte, Codings „may have slightly different granularity", und dieses Wort hat Implementierer jahrelang verwirrt. Rutt Lindström:
Die meisten Diskussionen, an denen ich teilgenommen habe (meist über Medication.code), haben sich am Wort „slightly" erschöpft. Ist der folgende Unterschied gering oder ein gewaltiger?
Grahame Grieve antwortete, dass die Regel nicht die Größe des Unterschieds betrifft:
Zulässig ist jede Granularitätsdifferenz, solange der Schnittmenge nicht leer ist; gute Praxis ist, dies nicht auf die Spitze zu treiben und die Granularität so wenig wie möglich zu variieren.
„slightly" ist verschwunden: Der CI-Build lautet nun: „When multiple codes are present, the intersection of their meanings SHALL NOT be empty. (Strictly, based on ISO 704, the intersection of their 'extensions' is not empty.)"
Gültig: LOINC 8867-4 (Heart rate) + SNOMED CT 364075005 (Heart Rate).
Ungültig: LOINC 9279-1 (Respiratory Rate) + SNOMED CT 364075005 (Heart Rate). Michael Lawley:
Es gibt kein Ding, das sowohl eine Atemfrequenz als auch eine Herzfrequenz ist — streng genommen ist Bottom (⊥) das Konzept, das die Schnittmenge jedes Konzepts ist, aber per Definition keine Instanzen hat.
Hinweis: Die Regel ist nicht maschinell prüfbar — sie ist eine Empfehlung, kein Validator. Und in der FHIR-I-Sitzung gab es keine formelle Abstimmung — das Quorum fiel am Ende ab. Inhaltlich wurde Einigkeit erzielt; der neu formulierte Text aus FHIR-50188 ist bereits im CI-Build gelandet, während veröffentlichte Releases bis R5 noch den alten Wortlaut tragen.
Regeln für den Produzenten
Die Asymmetrie des Produzenten: Sie sind der letzte Punkt, an dem das Wissen existiert. Der Kliniker hat den Patienten gesehen; das Gerät hat die Messung durchgeführt. Der Moment der Kodierung ist der Moment des Kontakts mit der Realität — nachdem Sie die Ressource geschrieben haben, schließt sich das Fenster. Alles, was Sie jetzt nicht festhalten, ist verloren, weil:
- Verallgemeinern ist später berechenbar; Spezialisieren ist es nicht. Jeder Konsument mit einem Terminologieserver kann „Typ-2-Diabetes, gut eingestellt" auf „Diabetes" hochrollen (
$subsumes, die Hierarchie). Die umgekehrte Richtung ist grundsätzlich unmöglich: Die Information ist nicht in den Daten. - Systemübergreifendes Mapping ist später ein Glücksspiel. ConceptMaps zwischen Systemen sind viele-zu-viele mit „wider / narrower / inexact"-Übereinstimmungen. Der nachgelagerte Mapper sieht nur den Code; Sie haben den Fall gesehen. Sie lösen die Mehrdeutigkeit mit Wissen;
$translatelöst sie mit einer Schätzung.
Daraus ergibt sich die Kernregel:
P0. Erfassen Sie zum Kodierungszeitpunkt alles, was nur Sie wissen:
- Den präzisesten Code, den Sie rechtfertigen können. Niemand downstream kann jemals wieder Präzision hinzufügen.
- Codes in alternativen Systemen, die Sie kennen (ICD-Kategorie und SNOMED-Konzept). Ihre Wahl der Kategorie ist Wissen; ein später berechnetes Mapping ist eine Schätzung.
- Immer
text— die vollständige Bedeutung in Worten. Es ist der Kanal der letzten Instanz, den 100 % der Empfänger (einschließlich Menschen) verstehen, und der einzige Ort, um Details zu bewahren, die in keinen Code gepasst haben:"text": "Negative for Chlamydia Trachomatis rRNA"neben einer Kodierung, die nur „Negativ" aussagt. - Provenienz kennzeichnen:
userSelected = truebei dem Code, den ein Mensch tatsächlich ausgewählt hat. In einem Array, in dem alle Codes gleich sind, gibt es eine verborgene Asymmetrie der Herkunft — einen Code hat eine Person gewählt, die den Patienten angesehen hat, die übrigen wurden abgeleitet. Das ist aus den Daten nachträglich nicht mehr feststellbar. (Beachten Sie jedoch:userSelectedkennzeichnet die Quelle, nicht die Granularität.) - Die angewandten Regeln deklarieren:
meta.profile. Drei Codes in einem Array — ist das „wir haben ausgespuckt, was wir hatten", oder „US Core Required Binding + Legacy-Code + Regulatorenübersetzung"? Strukturell nicht zu unterscheiden. Die Profilangabe teilt dem Leser mit, welchen Vertrag Sie erfüllt haben — und ermöglicht die Validierung.
Und drei Schutzmaßnahmen gegen die Verfälschung der Bedeutung beim Verpacken:
P1. Jede Coding drückt das gesamte Konzept eigenständig aus. Keine Teile, keine Modifikatoren. Lloyd über den „Auge" + „links"-Trick (Thread):
Man interpretiert sie nicht durch Kombination. Da diese Codes SNOMED sind, können Sie die Konzepte via Post-Koordination kombinieren:
81745001:272741003=7771000— „Auge:Lateralität=links". Es gibt kein ‚modifier'-Element in Coding.
Warum das die wichtigste Regel ist: Der Konsument wird mit Sicherheit einen Code aus dem Array nehmen — das ist sein Algorithmus. Wenn Ihre Codes Teile sind, ist Fehlinterpretation kein Risiko, sondern eine Gewissheit.
Und wenn das Codesystem keine Post-Koordinationsgrammatik hat — etwa ein LOINC-Code, der einen SNOMED-Qualifier benötigt — kann man das auch nicht mit zwei Codings simulieren und keinen kombinierten Code erfinden. Lloyd beantwortet genau diese Frage im Thread Handling post-coordination in CodeableConcept:
Wenn Sie einen post-koordinierten Ausdruck senden möchten, muss Coding.system ein Codesystem sein, das die Post-Koordinationssyntax und die Interpretation des Ausdrucks definiert... In Ihrem Fall gibt es keine veröffentlichte Syntax weder von LOINC noch von SNOMED für das, was Sie tun, also könnten Sie das Ergebnis nicht als LOINC- oder SNOMED-Code bezeichnen... Das Senden eines LOINC-Codes mit einer Erweiterung (oder die Verwendung von ‚method' oder anderen Elementen bei Observation) zur Übermittlung Ihrer Qualifikation wird wahrscheinlich besser verstanden.
Die legalen Auswege liegen außerhalb von coding[]: eine Extension für den Qualifier, dedizierte Ressourcenelemente (Observation.method, bodySite) oder eine separate verlinkte Ressource (Observation.hasMember). Die Grammatik gehört dem Codesystem, nicht Ihnen.
P2. Codes dürfen sich nicht widersprechen — die oben genannte Nicht-leere-Schnittmengenregel. Sie macht P1 überhaupt erst möglich: „Jeder Code ist wahr" funktioniert nur, wenn alle Codes dasselbe beschreiben.
P3. Bleiben Sie im Geltungsbereich des Elements, und achten Sie auf die Granularitätsrichtung — besonders bei Negation. Fügen Sie keinen Hierarchiewurzel wie SNOMED 138875005 (SNOMED CT Concept) in Observation.code ein (das ist FHIR-50189). Und der neue Spezifikationstext enthält eine Warnung, die der alte nicht enthielt — gemischte Granularität ist nicht nur unbequem, sie kann unsicher sein:
Ein Rezept für „APO Amoxicillin 250 mg Kapsel" (benötigt, um die Bestellung auszuführen) könnte sicher auch eine zusätzliche Kodierung „Breitspektrum-Penicillin" (möglicherweise benötigt, um Antibiotika-Überwachungsprotokolle auszulösen) enthalten. Wenn es hingegen eine Anordnung gäbe, die besagt „APO Amoxicillin 250 mg Kapsel nicht verabreichen", könnte das Hinzufügen einer Übersetzung „Breitspektrum-Penicillin" fälschlicherweise als Verbot aller Breitspektrum-Penicilline interpretiert werden.
Dieselben zwei Codes, dieselbe nicht-leere Schnittmenge — aber bei einer Negation erweitert die breitere Kodierung stillschweigend das Verbot. Wenn die Aussage negativ ist, wählen Sie enger.
Regeln für den Konsumenten
Die Asymmetrie des Konsumenten spiegelt die des Produzenten: Der Produzent kannte die Realität, aber nicht die Anwendung; Sie kennen die Anwendung — welche Analyse, welche Aktion, was ein Fehler kostet. Dieses Wissen steckt nicht in den Daten, und nur Sie können es anwenden.
C0. Interpretation ist Ihre Aufgabe und basiert auf einer explizit im Voraus gewählten Richtlinie. Lloyds Algorithmus aus demselben FHIR-I-Austausch:
Das Erste, was Sie tun, ist, alle zu verwerfen, die Sie nicht verstehen. Im Idealfall wollen Sie alles, auch wenn Sie es nicht kennen, weil jemand downstream es vielleicht versteht. Wenn ich die Bedeutung aus mehreren Konzepten nicht interpolieren kann, muss ich entscheiden, welches davon für die Art der Analyse, die ich durchführe, am bevorzugtesten ist. Und diese Entscheidung hätten Sie im Voraus getroffen, in Bezug darauf, welche dieser Terminologien meine bevorzugte ist.
Der Schlüsselbegriff lautet im Voraus. Die Auswahlrichtlinie (Systeme → Value Sets → Granularität → userSelected → text) lebt in Ihrer Mapping-Konfiguration — versioniert und geprüft — nicht in einem FHIRPath-Ausdruck zur Lesezeit. .first() ist eine Richtlinie, die niemand genehmigt hat.
Klassifizieren Sie Ihre Nutzungsart zuerst
Bevor Sie Codes auswählen, fragen Sie sich, welche Operation Sie tatsächlich am Array durchführen. Es gibt nur wenige Nutzungsklassen, und jede hat ihre eigene Semantik und ihr eigenes Risiko:
| Klasse | Benötigte Codes | Verlustbehaftet? | Hauptrisiko |
|---|---|---|---|
| Zugehörigkeitstest — „fällt es unter Konzept X?" (CDS-Auslöser, Kohortenzuordnung) | beliebig einer (∃) | nein | Negation |
| Projektion — „gib mir genau einen Code in Vokabular Y" (ETL, FHIR-zu-OMOP, Abrechnung) | genau einer | ja | Auswahl ohne Richtlinie |
| Aggregation — „welche Kategorie?" (Berichterstattung, Statistik) | genau eine Kategorie | ja | Doppelzählung |
| Transit — Store & Forward, Gateway | alle, auch nicht verstandene | nein | „Bereinigung" des Arrays |
| Anzeige — einem Menschen zeigen | keiner (text) | nein | leerer Text |
| Inferenz — Subsumptions-Reasoning, Zusammenführung von Details | alle + TX-Server | nein | Kosten |
Aus dieser Tabelle ergeben sich zwei Beobachtungen.
Erstens ist Zugehörigkeit die sicherste Klasse — keine Codeauswahl, kein Verlust: ein ODER über das Array, bei dem zusätzliche Codings nur helfen (mehr Repräsentationen → höhere Chance, Ihr Value Set zu treffen). Alle Schmerzen dieses Artikels — „welcher Code ist der Haupt-, welcher der präziseste" — sind Schmerzen der Projektions-Klasse. Die halbe Lösung besteht darin zu erkennen, dass die Aufgabe eigentlich Zugehörigkeit ist, und dass Sie aufhören können, einen Code auszuwählen, wo es ausreicht, die Zugehörigkeit zu prüfen.
Zweitens ist .first() Projektion ohne Richtlinie — die schlimmste Klasse auf die schlimmste Weise ausgeführt.
Zugehörigkeit bedeutet Subsumption, nicht Gleichheit
Und hier kommt unser eigenes P0 zurück: Wir haben dem Produzenten gesagt, so präzise wie möglich zu kodieren — also wird ein Konsument, der auf Gleichheit prüft, fast nichts finden. Sie suchen nach „Diabetes" (73211009), aber die Daten sagen „Typ-2-Diabetes, gut eingestellt", weil der Produzent P0 korrekt befolgt hat. Gleichheit → falsch → der Patient fällt aus der Kohorte. Je besser die Daten, desto schlechter arbeitet der naive Konsument.
Der Zugehörigkeitstest muss also sein:
∃ coding : subsumes(X, coding) — „der Code ist X oder ein Nachkomme von X"
Mechanismen nach Wahl: $subsumes für eine Einzelprüfung; ein ValueSet mit einem is-a-Filter oder SNOMED ECL << 73211009 plus $expand / $validate-code für eine materialisierte Menge; in OMOP concept_ancestor — derselbe transitive Abschluss als vorberechnete Tabelle.
Wo es scheitert:
- Hierarchien leben innerhalb eines Systems. Es gibt kein direktes subsumes(SNOMED-X, ICD-Code) — erst mappen, dann hochklettern, und die Verluste beider Schritte multiplizieren sich. Ein weiteres Argument für P0: Ein Produzent, der einen SNOMED-Code einbezogen hat, hat Ihnen einen Einstieg in eine gute Hierarchie gegeben.
- Hierarchien sind nicht gleich. Die Is-a-Relation von SNOMED ist eine formale Ontologie; die „Hierarchie" von ICD ist eine Kapitelorganisation — gut für Berichte, nicht für klinische Logik. Ein System für Zugehörigkeit zu wählen bedeutet, eine Hierarchie zu wählen, der man vertraut.
- Post-Koordination trifft niemals durch Gleichheit — aber ECL-basierte Subsumption erfasst sie, wenn Ihr Terminologieserver Ausdrücke verarbeitet.
- Negation verstärkt erneut: Ein Ausschlusskriterium via Subsumption auf einem breiten Konzept schließt alle seine Nachkommen aus — denken Sie an Amoxicillin. Per breitem Anker einschließen ist sicher; per breitem Anker ausschließen ist gefährlich.
Noch etwas, das $translate für Sie nicht tut — Verallgemeinerung. Michael Lawley, in Antwort an Lloyd:
In der Definition von $translate deutet nichts darauf hin, dass es irgendetwas „Semantisches" tut... Sollte $translate von B sowohl X als auch Y zurückgeben? Das würde wahrscheinlich zu sehr lautem und unhandlichem Verhalten führen.
Wenn Sie Vorfahrenkonzepte benötigen, ist das ein Value Set mit einem generalizes-Filter (oder ECL) — kein $translate-Aufruf.
Das Profil lesen — und die Projektionslücke
Wenn der Produzent meta.profile deklariert hat, nutzen Sie es: Ein Profil verengt „unbekanntes Array" auf „garantiertes Minimum plus mögliche Extras". Ein Required Binding sagt Ihnen, dass mindestens eine Coding aus einem bekannten Value Set stammt; Slicing sagt Ihnen, dass das Array benannte Rollen hat (den „obligatorischen LOINC-Code" in Vital Signs), die durch Diskriminatoren erkennbar sind.
Beachten Sie jedoch, was im Tooling fehlt. Das Profil beschreibt eine Projektion — Slicing unterteilt coding[] in benannte Teile — aber es gibt keine standardisierte Operation, um diese auszuführen. Validatoren lösen Slices intern auf (sie müssen es, um Kardinalitäten zu prüfen) und verwerfen die Zuweisung, geben nur Ja/Nein zurück. Es gibt kein $project(profile), das die Ressource durch das Profil betrachtet zurückgibt, wobei jede Coding mit ihrem Slice-Namen annotiert ist. Heute reimplementieren Sie Diskriminatoren von Hand (coding.where(memberOf('http://...vs'))) oder kodieren die Auswahlrichtlinie in eine SQL on FHIR ViewDefinition. Ein Profil ist ein Vertrag; Validierung ist die produzentenseitige Prüfung des Vertrags; Projektion wäre die konsumentenseitige Ausführung davon — die ersten beiden existieren in der Spezifikation, die dritte noch nicht.
Die verbleibenden Schutzmaßnahmen
C1. Die Struktur des Arrays trägt keine Bedeutung. Reihenfolge, Anzahl, Zusammensetzung — keines davon ist ein Signal. Das Array spiegelt die Konsumentenmenge des Produzenten wider, nicht Ihre Prioritäten.
C2. Einen Code auszuwählen ist ein bewusster Verlust der anderen. Jede Coding kann Details tragen, die den Nachbarn fehlen (Lungenkrebs ≠ Lungenerkrankung). Nehmen Sie einen — wissen Sie, was Sie verworfen haben. Und behalten Sie die Codes, die Sie nicht verstehen, wenn Sie Daten weitergeben: Sie sind nicht der letzte Leser.
C3. Kennen Sie Ihre sichere Fehlerrichtung. Bei positiven Aktionen ist die Interpretation durch den breiteren Code konservativ und sicher; bei negativen (Verbote, Ausschlüsse, „alle außer"-Filter) — nur der enge Code, sonst weiten Sie das Verbot aus.
C4. Der „nichts verstanden"-Zweig muss existieren und bedeutungsvoll sein. Unbekannte Array-Länge, unbekannte Systeme, neue Codes morgen. Nichts hat gepasst → text → Mensch → explizite Ablehnung. Keine Exception und kein stilles Überspringen — sonst schleicht sich .first() als „na ja, zumindest irgendetwas" wieder ein.
Das Fazit
CodeableConcept ist ein Protokoll über die Produzenten-/Konsumentenlücke hinweg. Die Kernpflicht des Produzenten — das Wissen erfassen, das nur zum Kodierungszeitpunkt existiert: den präzisesten Code, die Codes in alternativen Systemen, den vollständigen Text, die Provenienz, das Profil. Jede dieser Angaben beantwortet eine Frage des Konsumenten, die sonst unbeantwortet bleibt: „Wie präzise?", „Welche Kategorie?", „Was bedeutet es in Worten?", „Welchen Code hat der Mensch gewählt?", „Welche Regeln galten?"
Die Kernpflicht des Konsumenten — das Wissen anwenden, das nur auf seiner Seite existiert: den Anwendungsfall. Die Nutzungsart klassifizieren, Zugehörigkeit durch Subsumption statt Gleichheit prüfen, durch Profile projizieren, wo sie existieren, und Codes nach einer expliziten Richtlinie auswählen — niemals nach der Position im Array.
Terminology is fun, wie Lloyd McKenzie gerne sagt. Besonders wenn der Spaß als siebter Code in einem Array ankommt, für das Ihr Parser nicht gebaut wurde.
Quellen
Chat.fhir.org-Threads (Zulip-Anmeldung erforderlich):
- Multiple codes in CodeableConcept —
#terminology, 113 Nachrichten. Die Hauptdebatte und die Überarbeitung der Spezifikation. - Will $translate do generalization? —
#terminology. Warum$translatekeine Verallgemeinerung durchführt. - Does array order ever matter? —
#implementers.orderMeaningund Sortierung in Arrays. - CodeableConcept.coding.userSelected —
#terminology. userSelected vs. coding-purpose. - Adding a tag to coding in CodeableConcept —
#implementers. Ein Anwendungsfall für coding-purpose. - Handling post-coordination in CodeableConcept —
#implementers. - Multiple Coding in Codeable Concept —
#implementers. Der „Auge + links"-Lateralitätsfall. - codings with null —
#terminology. US Core, VA VUID, „do not send UNK". - CodeableConcept Validation —
#terminology. „99 % gültig ist immer noch ungültig".
HL7 JIRA:
- FHIR-50188 — neue Formulierung für mehrere Codings in
CodeableConcept. - FHIR-50189 — Geltungsbereich/Granularität für
Observation.code.
FHIR-Spezifikation:
- CodeableConcept, CI-Build — der überarbeitete Abschnitt „Multiple Codings" mit der Schnittmengenregel und der Negationswarnung.
- CodeableConcept, R5 — der alte Wortlaut „slightly different granularity" zum Vergleich.



