Warum ein Server fĂŒr zwei Versionen
R6 befindet sich in seinen abschlieĂenden Abstimmungsrunden, und die Frage hat sich verĂ€ndert. Es geht nicht mehr darum, âwas ist neu in R6", sondern âwie lange werden wir es neben R4 betreiben". Alles, was wir wissen, deutet darauf hin: jahrelang.
- Regulatorische Vorgaben fixieren Versionen. Die US-amerikanische Zertifizierung (US Core) sowie die CMS-InteroperabilitĂ€tsregeln fĂŒr Patient Access, Provider Access, Payer-to-Payer und Prior Authorization basieren alle auf R4. Neue Regeln werden mit ĂbergangszeitrĂ€umen kommen, nicht mit einem festen Umstellungsdatum. Andere Jurisdiktionen folgen ihren eigenen ZeitplĂ€nen.
- Partner rĂŒsten nicht gemeinsam auf. Wenn Sie eine Plattform, ein HIE oder ein KostentrĂ€ger sind, erhalten Sie die Vereinigung aller Versionen Ihrer Partner.
- Lange AuslÀufer sind normal. Epic stellt DSTU2, STU3 und R4 jeweils unter einer eigenen Basis-URL bereit. Oracle Health soll DSTU2 etwa sechs Jahre lang neben R4 betrieben haben, bevor es 2025 eingestellt wurde.
- R5 wurde gröĂtenteils ĂŒbersprungen. Produktivsysteme blieben auf R4 und ĂŒbernahmen die benötigten R5-Features â etwa themenbasierte Subscriptions â ĂŒber R4-Backport-IGs. Der realistische Sprung ist also R4 direkt zu R6, mit allen Ănderungen von R4 â R5 und R5 â R6 auf einmal.
- Daten ĂŒberdauern Versionen. Heute geschriebene DatensĂ€tze werden in zehn oder zwanzig Jahren gelesen. Archive und PrĂŒfpfade werden nicht neu geschrieben.
Die Ănderungen sind auch nicht kosmetischer Natur. Einige, auf die wir gestoĂen sind:
| R4 | R6 | Warum es problematisch ist |
|---|---|---|
Encounter.class: ein Coding | Encounter.class: eine Liste von CodeableConcept | Mehrere Klassen passen nicht in einen Wert |
Encounter.status finished, onleave, arrived | completed, on-hold, in-progress + subjectStatus | Unterschiedliche Codes, keine Eins-zu-eins-Entsprechung |
Encounter.period, hospitalization, participant.individual | actualPeriod, admission, participant.actor | Umbenennungen |
BodyStructure.location und morphology: je einer | BodyStructure.includedStructure: einer oder mehr | Eine R6-Ressource kann beschreiben, wofĂŒr R4 mehrere Ressourcen benötigt |
Observation.bodySite: ein Code | ergÀnzt Observation.bodyStructure: eine Referenz | R4 hat kein Element, das den Link aufnehmen kann |
Die Koexistenz ist also gegeben. Die eigentliche Frage lautet, wie man sie innerhalb eines einzigen Systems umsetzt.
Was die Runde beim FHIR Camp sagte
Beim HL7 FHIR Camp 2026 in Cascais, Portugal, leitete ich eine Birds-of-a-Feather-Session mit dem Titel Koexistenz mehrerer FHIR-Versionen im selben System. Die Frage: Soll ein System mehrere Versionen aus einem einzigen Datensatz mit Konvertern bereitstellen, eine separate Kopie pro Version vorhalten oder jede Ressource in der Version speichern, in der sie eingegangen ist?
Die Antwort der Runde war eindeutig: Die Teilnehmer wollen einen einzigen Datensatz. Die GrĂŒnde sind pragmatisch:
- Eine einzige Quelle der Wahrheit. Ein Patient existiert genau einmal. Mit einer Kopie pro Version laufen die Kopien auseinander, und jemand muss entscheiden, welche die richtige ist.
- Keine Konflikte zwischen Versionen. Wenn eine R4-Kopie und eine R6-Kopie beide Schreibzugriffe akzeptieren, können zwei Clients denselben Datensatz gleichzeitig ĂŒber verschiedene Versionen Ă€ndern. Die RĂŒcksynchronisierung fĂŒhrt zu Konflikten, ReihenfolgeÂproblemen und Merge-Regeln. Mit einem einzigen Store geht jeder Schreibvorgang in dieselbe Zeile, und
If-Matchfunktioniert unabhĂ€ngig davon, welche Version der Client verwendet. - Ein Verlauf und ein PrĂŒfpfad. Versionen, Löschungen und Provenance werden einmal erfasst, nicht einmal pro Kopie.
- Ein Ort fĂŒr Suche und Analyse. Abfragen mĂŒssen nicht wissen, in welcher Version eine Ressource eingegangen ist.
Das lĂ€sst nur ein Design ĂŒbrig: einen einzigen Store, aus dem jede Version als Projektion davon bereitgestellt wird. Es konzentriert auch alle Schwierigkeiten an einem einzigen Ort: der Konvertierung. Sie muss in beide Richtungen funktionieren, fĂŒr echte Daten, bei jeder Anfrage.
Ein Hackathon-Projekt
Wir haben daraus ein Hackathon-Projekt gemacht, mit zwei Zielen: zu sehen, wie weit das Single-Dataset-Design trĂ€gt, und zu bewerten, ob die FHIR Mapping Language (FML) eine geeignete Mapping-Engine fĂŒr diese Architektur ist.
Das Team: Brian Postlethwaite, Artur Kurvits, Patrick Werner, Francisco Novillo Perez, Vadim Peretokin, Ralf Pohl und Aleksandr Kislitsyn.
Ein besonderer Dank gilt Gino Canessa. Er war nicht Teil des Teams, teilte jedoch einige der schwierigsten FĂ€lle â solche, die grundsĂ€tzlich schwer korrekt zu lösen sind.
Das Setup
Der Server ist bewusst klein gehalten:
- Ein Store, nur FHIR R4. PostgreSQL 18, eine Tabelle pro Ressourcentyp plus eine Verlaufstabelle. Wir haben R4 gewĂ€hlt, weil dort die heutigen Daten und Clients sind. Mit R6 als Store wĂŒrden dieselben Konvertierungen einfach in die andere Richtung laufen.
- Zwei APIs.
/R4liest und schreibt den gespeicherten JSON ohne Konvertierung./R6liest, sucht, erstellt, aktualisiert und löscht ĂŒber die FHIR Mapping Language (FML). - Die FML-Engine ist Brian Postlethwaites FML-Executor aus dem FHIRPath Lab. Er fĂŒhrt seine generierten R4 â R6-Maps unverĂ€ndert aus, einschlieĂlich aller Importe: 164 Maps. Die Richtung R6 â R4 umfasst 63 Maps: Datentypen umgekehrt aus den generierten Maps, plus handgeschriebene Maps fĂŒr Patient, Encounter, BodyStructure und Observation.
- Validierung verwendet R4 4.0.1 und R6 6.0.0-ballot5, geladen mit
@atomic-ehr/fhir-canonical-managerund geprĂŒft mit@atomic-ehr/fhirschema.
Eine Regel hĂ€lt alles zusammen: Jeder Schreibvorgang, aus beiden Versionen, muss in beiden Versionen gĂŒltig sein und den Round-Trip R6 â R4 â R6 ĂŒberstehen.
Jede R6-Antwort trĂ€gt dieselbe id und versionId wie die gespeicherte R4-Ressource, sodass beide Versionen stets ĂŒbereinstimmen, welchen Datensatz und welche Version sie betrachten.
Patient: der einfache Fall
Patient ist seit R4 normativ und hat sich kaum verĂ€ndert. Die generierte Map ist eine Feld-fĂŒr-Feld-Kopie und funktioniert unverĂ€ndert. R6 fĂŒgt drei Dinge zu Patient.contact hinzu: role, additionalName und additionalAddress. Auf dem Weg nach R4 werden sie in versionsĂŒbergreifende Extensions gespeichert und auf dem Weg nach R6 wieder herausgelesen.
Dieses Muster taucht im gesamten Projekt immer wieder auf, daher lohnt es sich, es einmal festzuhalten. FHIR definiert ein URL-Schema fĂŒr versionsĂŒbergreifende Extensions, http://hl7.org/fhir/[version]/StructureDefinition/extension-[element], fĂŒr Daten, die in der anderen Version keinen Platz haben. http://hl7.org/fhir/6.0/StructureDefinition/extension-Patient.contact.role ist R6s Patient.contact.role, gespeichert in R4.
Encounter: wo sich Versionen wirklich unterscheiden
Ein R6-Client erstellt einen Encounter mit zwei Klassen und dem Status âcompleted":
POST /R6/Encounter
{
"resourceType": "Encounter",
"status": "completed",
"class": [
{ "coding": [{
"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
"code": "AMB" }] },
{ "coding": [{
"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
"code": "VR" }] }
],
"participant": [{ "actor": { "reference": "Practitioner/dr-smith" } }],
"actualPeriod": { "start": "2026-11-06T09:00:00Z", "end": "2026-11-06T09:30:00Z" },
"admission": { "admitSource": { "text": "Referral" } }
}
Die handgeschriebene Encounter_6to4.fml wandelt ihn in R4 um. Status-Codes folgen HL7s R5 â R4-ConceptMap, da R5 und R6 dieselben teilen. Die erste Klasse wird zum R4-class, jede weitere Klasse wird zu einer versionsĂŒbergreifenden Extension, und die umbenannten Elemente erhalten ihre R4-Namen zurĂŒck:
conceptmap "EncounterStatus" {
s:"planned" - t:"planned"
s:"on-hold" - t:"onleave"
s:"completed" - t:"finished"
âŠ
}
group Encounter(source src : EncounterR6, target tgt : EncounterR4) extends DomainResource <<type+>> {
src.status as v where ($this != 'in-progress' and $this != 'discharged')
-> tgt.status = translate(v, '#EncounterStatus', 'code');
src.class first as c -> tgt.class as k then EncounterFirstClass(c, k);
src.class not_first as v -> tgt.extension as x,
x.url = 'http://hl7.org/fhir/6.0/StructureDefinition/extension-Encounter.class',
x.valueCodeableConcept as t then CodeableConcept(v, t);
src.actualPeriod -> tgt.period; // renamed
âŠ
}
Das wird gespeichert und von einem R4-Client gelesen:
{
"resourceType": "Encounter",
"status": "finished",
"class": {
"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
"code": "AMB"
},
"extension": [{
"url": "http://hl7.org/fhir/6.0/StructureDefinition/extension-Encounter.class",
"valueCodeableConcept": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
"code": "VR"
}]
}
}],
"participant": [{ "individual": { "reference": "Practitioner/dr-smith" } }],
"period": { "start": "2026-11-06T09:00:00Z", "end": "2026-11-06T09:30:00Z" },
"hospitalization": { "admitSource": { "text": "Referral" } }
}
Das ZurĂŒcklesen ĂŒber /R6 liefert exakt das, was der Client gesendet hat, zuzĂŒglich id und meta. Die Round-Trip-PrĂŒfung stellt das sicher, bevor der Schreibvorgang akzeptiert wird.
Ist die Status-Ăbersetzung verlustbehaftet?
translate() allein ist nur dort sicher, wo die Codes eins zu eins ĂŒbereinstimmen. R6 hat mehr Encounter-Status-Codes als R4, sodass einige davon zusammenfallen:
| R6-Status | R4-Status | Verlustbehaftet mit translate() allein? |
|---|---|---|
completed | finished | Nein |
on-hold | onleave | Nein |
discharged | finished | Ja: finished wird als completed zurĂŒckgelesen |
discontinued | kein R4-Code | Ja: nichts, wohin ĂŒbersetzt werden könnte |
Bei einem Viele-zu-eins-Fall wie discharged speichert die Map den nĂ€chstgelegenen R4-Code und bewahrt den ursprĂŒnglichen R6-Code in einer versionsĂŒbergreifenden Extension am Element selbst. Das ist HL7s Regel fĂŒr Required-Bindings mit zusĂ€tzlichen Codes:
{
"status": "finished",
"_status": {
"extension": [{
"url": "http://hl7.org/fhir/6.0/StructureDefinition/extension-Encounter.status",
"valueCode": "discharged"
}]
}
}
Ein R4-Client sieht einen gĂŒltigen R4-Status. Die R6-Richtung prĂŒft zuerst die Extension, sodass discharged als discharged zurĂŒckkommt, nicht als completed. Das allgemeine Prinzip lautet also: Ăbersetzen, wo die Codes ĂŒbereinstimmen, und wo nicht, den Originalwert neben dem ĂŒbersetzten bewahren.
discontinued ist noch nicht gelöst. Es gibt keinen R4-Code dafĂŒr, daher wird ein Schreibvorgang, der ihn verwendet, vorerst abgelehnt, anstatt mit einem geraten Status gespeichert zu werden.
Suche bedeutet in jeder Version etwas anderes
Der gespeicherte Encounter hat nun nativ die Klasse AMB und VR in einer Extension. Welche Suchen sollten ihn ĂŒber VR finden?
FĂŒr R6 ist die Antwort einfach: class ist eine Liste, VR ist darin enthalten, also trifft class=VR zu. Die Frage ist, was R4-class tun soll.
Option 1: R4-class die R6-Bedeutung geben. Wir hĂ€tten den R4-Parameter auch in die versionsĂŒbergreifende Extension schauen lassen können. Dann wĂŒrden beide Versionen fĂŒr dieselbe Abfrage dieselben Encounters zurĂŒckgeben. Aber schauen Sie, was ein R4-Client zurĂŒckbekommt: Er fragt nach class=VR und erhĂ€lt einen Encounter, dessen class AMB ist. Der gesuchte Wert befindet sich nicht in dem Feld, nach dem der Parameter benannt ist. Er steckt in einer Extension, die der Client möglicherweise gar nicht liest. Die Suche wĂŒrde der Datenform widersprechen, die der Client sieht.
Option 2: Die Standardbedeutung beibehalten. R4-class trifft nur auf das, was in R4-class steht, findet den Encounter also ĂŒber AMB, nicht ĂŒber VR. Die Ergebnisse stimmen mit der Ressource ĂŒberein, die der R4-Client erhĂ€lt, und Standardparameter behalten ihre veröffentlichte Definition. Der Preis: Dieselben gespeicherten Daten beantworten dieselbe Frage unterschiedlich, je nachdem, ĂŒber welche Version man fragt.
Wir haben Option 2 gewĂ€hlt und einen expliziten Parameter fĂŒr R4-Clients hinzugefĂŒgt, die das R6-Verhalten wĂŒnschen:
- R6-
classtrifft aufAMBundVR: die Standard-R6-Bedeutung. - R4-
classtrifft nur aufAMB: die Standard-R4-Bedeutung. - R4-
class-r6trifft aufAMBundVR: die R6-Bedeutung, namentlich opt-in, gibt R4-Ressourcen zurĂŒck.
Ist das die beste Antwort? Ich bin nicht sicher, ob es eine gibt. Eine R4-App, die anzeigt, was in class steht, möchte Option 2. Ein Bericht, der alle virtuellen Besuche zĂ€hlen muss, unabhĂ€ngig davon, in welcher Version sie erfasst wurden, möchte die R6-Bedeutung. Jeder Implementierer muss das fĂŒr seinen eigenen Anwendungsfall entscheiden. Wichtig ist, bewusst zu wĂ€hlen und die Entscheidung fĂŒr Clients sichtbar zu machen, anstatt sie in einem Standardparameter zu verstecken.
Wir haben class-r6 als ordnungsgemĂ€Ăen R4-SearchParameter in einem kleinen lokalen FHIR-Paket definiert. Der Canonical Manager lĂ€dt ihn neben R4-Core, der Server stellt ihn unter /R4/SearchParameter/Encounter-class-r6 bereit, und das CapabilityStatement verweist darauf:
{
"resourceType": "SearchParameter",
"url": "http://example.org/fhir/SearchParameter/Encounter-class-r6",
"derivedFrom": "http://hl7.org/fhir/SearchParameter/Encounter-class",
"code": "class-r6",
"base": ["Encounter"],
"type": "token",
"expression": "Encounter.class | Encounter.class.extension('http://hl7.org/fhir/6.0/StructureDefinition/extension-Encounter.class').value | Encounter.extension('http://hl7.org/fhir/6.0/StructureDefinition/extension-Encounter.class').value"
}
Die Suche selbst lĂ€uft als SQL auf dem gespeicherten R4. FML konvertiert nur die Ergebnisse fĂŒr /R6.
BodyStructure: eine Ressource, mehrere Zeilen
BodyStructure ist der Punkt, an dem die beiden Modelle aufhören, aufeinander zu passen. In R6 ist includedStructure eine Liste: Eine BodyStructure kann eine LĂ€sion ĂŒber den Unterarm, den Oberarm und die Schulter beschreiben. R4 hat ein location und ein morphology.
Unsere Lösung â ein eigenes Design, nicht HL7s â besteht darin, eine R6-BodyStructure als eine R4-Ressource pro Struktur zu speichern, mit den IDs abc, abc--2, abc--3. Eine vom Server verwaltete Extension verknĂŒpft sie: Die erste Zeile listet die anderen auf, und jede weitere Zeile verweist auf die erste zurĂŒck.
GET /R4/BodyStructure/abc
{
"resourceType": "BodyStructure",
"id": "abc",
"location": { "text": "Forearm" },
"extension": [{
"url": "http://example.org/fhir/StructureDefinition/bodystructure-row-group",
"extension": [
{ "url": "position", "valuePositiveInt": 1 },
{ "url": "member", "valueReference": { "reference": "BodyStructure/abc--2" } },
{ "url": "member", "valueReference": { "reference": "BodyStructure/abc--3" } }
]
}]
}
Der Rest ergibt sich daraus:
- Ein Schreibvorgang schreibt die gesamte Zeilengruppe in einer Transaktion neu, aus beiden Versionen.
- Nur in R6 vorhandene Werte gehen in versionsĂŒbergreifende Extensions, beispielsweise zusĂ€tzliche Morphologien, LateralitĂ€t und Landmarks.
- Zeilen-IDs bleiben stabil, und die
includedStructure.idjeder Struktur in R6 ist ihre Zeilennummer. - R6 zeigt immer nur
abc.GET /R6/BodyStructure/abc--2ergibt einen404.
Es gibt eine andere Aufteilungsmöglichkeit, die möglicherweise die klarere ist. Jede Struktur nummerieren â abc--1, abc--2, abc--3 â und gar keine R4-Ressource namens abc speichern. Ein Lesezugriff auf GET /R4/BodyStructure/abc gibt dann ein OperationOutcome zurĂŒck, das die echten IDs auflistet:
GET /R4/BodyStructure/abc
{
"resourceType": "OperationOutcome",
"issue": [{
"severity": "information",
"code": "informational",
"diagnostics": "BodyStructure/abc is stored in R4 as three resources: BodyStructure/abc--1, BodyStructure/abc--2, BodyStructure/abc--3"
}]
}
Die AbwÀgungen verlaufen in beide Richtungen:
abc, abc--2, abc--3 (was wir gebaut haben) | abc--1, abc--2, abc--3 + OperationOutcome | |
|---|---|---|
Lesen von abc ĂŒber R4 | Gibt die erste Struktur zurĂŒck, die wie das Ganze wirkt | Sagt klar, dass es aufgeteilt ist, und wo die Teile sind |
Bestehende R4-Referenzen auf abc | Funktionieren weiter und zeigen auf die erste Struktur | Zeigen auf etwas, das nicht als BodyStructure lesbar ist |
| Entfernen der ersten Struktur | Die nĂ€chste rĂŒckt in abc nach, Referenzen mĂŒssen neu geschrieben werden | Nichts verschiebt sich: jede Struktur behĂ€lt ihre ID |
| R4-Clients | Können die Aufteilung ignorieren, bis sie relevant wird | MĂŒssen das OperationOutcome von Anfang an verstehen |
Wir haben uns dafĂŒr entschieden, abc als echte Ressource beizubehalten, hauptsĂ€chlich damit bestehende R4-Referenzen und Clients weiterhin funktionieren. Wie bei den Such-Semantiken bin ich auch hier nicht ĂŒberzeugt, dass es eine einzig richtige Antwort gibt. Jedes System muss die Entscheidung anhand seiner eigenen Clients treffen.
Referenzen auf eine aufgeteilte Ressource
Die Aufteilung einer Ressource gibt ihr zwei IdentitĂ€ten: R4-Clients sehen Zeilen, R6-Clients sehen eine Ressource. Jede Ressource, die auf eine BodyStructure zeigt, muss nun zwischen beiden ĂŒbersetzt werden.
In R6 verweist eine Observation ĂŒber bodyStructure auf eine BodyStructure. R4 hat kein solches Element, daher trĂ€gt die Standard-Extension http://hl7.org/fhir/StructureDefinition/bodySite den Link. Da eine R6-BodyStructure mehrere R4-Zeilen sind, reicht ein einzelner Link nicht aus: In R4 ist BodyStructure/abc nur der Unterarm. Die R6 â R4-Map löst die Referenz auf und schreibt einen Link pro Zeile:
bs.reference as r where (reference.startsWith('BodyStructure/'))
-> evaluate(r, resolve() as BodyStructure) as row
then BodyStructureRows(row, t);
group BodyStructureRows(source row : BodyStructure, target t) {
row.extension as g where (url = 'âŠ/bodystructure-row-group') then {
g.extension as m where (url = 'member') then {
m.value : Reference as member -> t.extension as x,
x.url = 'http://hl7.org/fhir/StructureDefinition/bodySite',
x.valueReference as v then Reference(member, v);
};
};
}
Auf dem RĂŒckweg entfernt Observation_4to6 das Suffix --N, und die Duplikate verschmelzen zur einzelnen Referenz, die der Client gesendet hat. Drei Server-Regeln sorgen fĂŒr Konsistenz:
- Beibehalten. Hat ein R4-Client den Link auf eine Zeile eingeschrĂ€nkt, bewahrt ein R6-Update diese EinschrĂ€nkung, weil R6 sie nicht ausdrĂŒcken kann.
- Neu ausrichten. Wird die erste Struktur entfernt, rĂŒckt die nĂ€chste in die erste Zeile nach, und jede Observation, die auf die alte Zeile zeigte, wird in derselben Transaktion neu geschrieben.
- Ablehnen. Alles, was den Round-Trip R6 â R4 â R6 nicht ĂŒbersteht â beispielsweise ein als Konzept angegebenes
bodyStructure â wird mit422abgelehnt.
Was das fĂŒr bestehende R4-Clients bedeutet
Ein einziger Datensatz bedeutet nicht, dass R4-Clients unberĂŒhrt bleiben. Sie werden wahrscheinlich einige Ănderungen benötigen:
- Ein Lesezugriff kann unvollstÀndig sein.
GET /R4/BodyStructure/abcgibt nur die erste Struktur zurĂŒck. Die ĂŒbrigen befinden sich in Zeilen, die ein Client nur ĂŒber die row-group-Extension findet. - R6-Daten kommen als Extensions an. ZusĂ€tzliche Encounter-Klassen und
statusReasonbei Observations zum Beispiel sind versionsĂŒbergreifende Extensions. Ein Client, der sie ignoriert, verpasst Daten; einer, der sie bei einem Update entfernt, verlĂ€sst sich darauf, dass der Server sie vorwĂ€rts trĂ€gt (unser Server tut das). - Suchen finden weniger. R4-
classtrifft nicht auf eine Klasse, die nur in R6 existiert. Der Client muss vonclass-r6wissen. - Daten Àndern sich unter ihnen. Referenzen zeigen auf Zeilen, die sich verschieben können, und der Server schreibt Observations neu, wenn das passiert. Versionen Àndern sich ohne Schreibvorgang des Clients, sodass
If-Match-Konflikte wahrscheinlicher werden.
Erkenntnisse
1. Es ist eher eine Modellierungsaufgabe als eine technische
Die technische Seite hat gehalten. Eine FML-Engine fĂŒhrte die generierten R4 â R6-Maps unverĂ€ndert aus, und handgeschriebene Maps deckten Umbenennungen, Code-Ăbersetzungen, in einen einzigen Wert gefaltete Listen plus Extensions sowie das Aufteilen einer Ressource in mehrere ab. FML ist eine geeignete Mapping-Engine fĂŒr diese Architektur. Der Canonical Manager, fhirschema und PostgreSQL haben ihre Aufgaben erfĂŒllt.
Die schwierigen Fragen betrafen alle das Modell, und die Spezifikation wird sie Ihnen nicht beantworten. R4 und R6 bieten unterschiedliche Formen fĂŒr dieselben Daten: eine Klasse oder eine Liste von Klassen, eine Körperstruktur oder mehrere, ein codierter Körperstelle oder eine Referenz. Die Entscheidung, wie eine Form in die andere ĂŒberfĂŒhrt wird, hĂ€ngt davon ab, was die Daten in Ihrem System bedeuten â weshalb Ihr eigenes fachliches Modellieren letztlich in den Mappings landet. Keine Version der Spezifikation wird diese Entscheidungen fĂŒr Sie treffen.
2. Das Modell muss Ànderbar bleiben
Diese Entscheidungen werden sich Ă€ndern. HL7s versionsĂŒbergreifende Arbeit wird Konventionen veröffentlichen, Ihre Clients werden RĂŒckmeldung geben, und einige Entscheidungen werden sich als falsch herausstellen. Daher mĂŒssen die Regeln sichtbar und umkehrbar sein, nicht in Code vergraben: Maps statt handgeschriebener Konvertierungen, SearchParameter als veröffentlichte Definitionen und eine dokumentierte Liste, was wohin gehört.
Es gibt einen Haken: Da der Store R4 ist, ist jede Modellierungsentscheidung auch ein Speicherformat. Wir haben die URL der Encounter-class-Extension einmal wĂ€hrend des Hackathons geĂ€ndert, und bereits unter der alten URL gespeicherte Encounters verloren ihre zusĂ€tzliche Klasse in der R6-Ansicht stillschweigend. Eine Entscheidung rĂŒckgĂ€ngig zu machen bedeutet, die darunter geschriebenen Daten zu migrieren â planen Sie das von Anfang an ein.
3. R4-Clients kommen nicht ohne Ănderungen davon
Es ist verlockend, versionsĂŒbergreifende Extensions als zusĂ€tzliche Daten zu betrachten, die ein R4-Client getrost ignorieren kann. Das sind sie nicht. Was in ihnen landet, ist oft der Kern des Datensatzes: der Link von einer Observation zu den Körperstrukturen, die sie beschreibt, die zweite Klasse eines Encounters, die Zeilen einer BodyStructure jenseits der ersten. Ein Client, der sie ignoriert, verpasst nicht nur Details. Er bekommt eine falsche Antwort, und seine Suchen verhalten sich anders als die R6-Suchen. Bestehende R4-Clients werden zumindest einige Ănderungen benötigen, um die Daten korrekt zu lesen.
Ausprobieren
- Code, Maps und Tests: github.com/aleksandrkislitsyn/fml-r4-r6-fhir-server. Die README erklĂ€rt, wie man es ausfĂŒhrt, und
docs/mappings.htmldokumentiert jede Mapping-Entscheidung. - Mehr zu R6: FHIR R6-Veröffentlichungsdatum und Neuigkeiten, FHIR R4 vs. R5 vs. R6, R6: Subtraktion durch zusÀtzliche Ressourcen und R6: Was passiert mit einer Referenz, wenn ihr Ziel in einen Inkubator wandert.




