In recent work I am involved in, the HL7 FHIR DSTU4 resources were converted to the openEHR formalism known as Basic Meta-Model (BMM), which is published as an open specification. BMM is an object-oriented formalism, conceptually similar to UML (minus the diagramming), with a fully formal definition. It has been in use since 2009 within the openEHR ADL Workbench, since about 2011 in HL7 CIMI, and since about 2016 in openEHR Archie (ADL2/BMM libraries and tools) and commercial tools including Marand ADL-designer and Veratech LinkEHR.
The BMM file encoding the FHIR resources is available within the openEHR reference-models Git repo on Github. It compiles within the ADL Workbench, which allows similar views as an IDE or modelling tool. Views of the translated model shown in this post are from this tool.
The manual ‘element-by-element’ conversion of the FHIR resources enabled the resources to be compared at the most detailed level across the whole collection.
From a modelling point of view, a few things became apparent that I had not realised or expected. The overall impression from reviewing all of the resources in one go is one of inconsistency and semantic incoherence. The resources cannot be said to constitute a ‘model’, since none of the usual inheritance, encapsulation of common elements or typing practices has been used. Indeed the FHIR resources appear to be the result of separate committees working with almost no cross-referencing, methodology, or common design basis. The result is that each Resource is something like a ‘bag-of’attributes’, presumably due to the application of the so-called 80/20 rule in the committee context.
In this post, I provide a sample of inconsistencies.
Different names, same semantics
The FHIR resources abound with definitions which appear to represent the same semantics but which are defined as unrelated model entities, and named and/or structured slightly differently. Some examples of slightly different names:
// supporting info ServiceRequest.supportingInfo MedicationRequest.supportingInformation // instantiates ServiceRequest.instantiatesCanonical ServiceRequest.instantiates Procedure.instantiatesUri MedicationAdministrationInstantiates // language DeviceDefinition.languageCode: CodeableConcept [*] Attachment.language: Code <<fhir:CommonLanguages>> [0..1] Resource.language: Code <<fhir:CommonLanguages>> [0..1] Communication.language: CodeableConcept <<fhir:CommonLanguages>> [0..1] // recorded v recordedDate Provenance.recorded: Instant Condition.recordedDate: Date[0..1] AllergyIntolerance.recordedDate: Date[0..1] // reasonCode v reason ServiceRequest.reasonCode: CodeableConcept[*] Appointment.reasonCode: CodeableConcept[*] MedicationRequest.reasonCode: CodeableConcept[*] Provenance.reason: CodeableConcept[*] // dosage v dosageInstruction MedicationDispense.dosageInstruction MedicationStatement.dosage
Names that are different, but appear to designate the same thing:
Location.hoursOfOperation: HoursOfOperation[*] HealthcareService.AvailableTime: AvailableTime[*] Slot.start: Instant + Slot.end: Instant
The two types HoursOfOperation and AvailableTime are defined as follows (ADL Workbench visualisation):
One can only conclude that these were created by different committees with no communication, and no overall governance process.
The concept of Ingredient is similarly modelled twice with very similar but unrelated Resources:
Here again there is no attempt at capturing these data elements in a common definition (possibly with specialised child types) that could be implemented once in software and would result in coherent data and querying.
// length v duration Encounter.length: Duration Collection.duration: Duration InitialFill.duration: Duration Repeat.duration: Decimal, durationUnit: UnitsOfTime
The general problem with failing to rationalise similar / same data structures and behaviour across a model environment is that little or no software reuse is achieved – every item of data has its own separate piece of software, each with its own private semantics. Similarly, a similar lack of re-use of data or querying can occur: software has no way of knowing that, say, Ingredient and MedicationIngredient are fundamentally the same thing, and could be queried the same way.
Same names & semantics, different structures
A problem related to the above, and almost certainly a result a lack of global review or model governance (or indeed, the activity we normally call ‘modelling’) is different formal definitions for the same-named things.
An example is the bodySite atribute is CodeableConcept[*] in Procedure and Coding[0..1] in ImagingStudy/Series. In Series, laterality is also included; it’s difficult to imagine how laterality would not be relevant to Procedure – presumably there it is meant to be included in the SNOMED code via post-coordination?
The definition of priority in two Resources is typed differently for no obvious reason:
StatusReason is usually defined as one single-valued attribute, e.g. in MedicationRequest:
but in MedicationAdministration it is a container attribute:
and in MedicationDispense, it is a choice:
This actually gives us 3 attribute/accessor names for the same thing: statusReason, statusReasonCodeableConcept and statusReasonReference.
Communication is represented two different ways – in Patient:
and Practitioner, with no obvious reason:
In contrast, the Organization resource has no ‘communication’ attribute. In real life however, organisations do have communication capabilities (and regularly advertise them), e.g. ‘we speak English, Urdu and Hindi’.
Performer is another entity represented in numerous ways. In Procedure, it is:
While in MedicationDispense it is:
Yet, these types have no relation to each other. Once again, any possibility of software re-use is subverted.
Another instance of inconsistency in the use of repeated choice attributes is ‘effective’ within Observation versus other resources.
Here is the same attribute from MedicationStatement.
Variant forms of Dosage also appear to be describing the same thing, but using unrelated definitions. Dosage is a resource, as follows:
However, within MedicationAdministration, Dosage has the following local definition:
It is clear that these would be related by inheritance in a more typical model. Additionally, dose and rate, which have the same meanings in both, are defined differently, preventing even the smallest re-use in software.
Same names, Different Semantics
There are also Resource elements/component types that are defined with the same names, but whose semantics are different. For example, MedicationRequest.substitution, a BackboneElement type, is to do with ‘restrictions on substitution’ (presumably with generics):
MedicationDispense also has a substitution, also a subordinate resource, but whose meaning is to do with whether a substitution was actually performed, as follows:
At best, this risks confusion. More obvious naming of both the elements and the types would have been:
substitutionAllowed: SubstitutionAllowed substitutionOccurrence: SubstitutionOccurrence.