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.
Has this been raised to FHIR-I Work Group?
Most FHIR resources aren’t yet normative and inconsistencies abound. When you find one please submit a change request (tracker) and it will usually be fixed in the next release.
The deep inheritance structures in the RIM was one factor which inhibited widespread adoption of the HL7 V3 standards. A lot of implementers just had trouble understanding it. So the FHIR designers decided to go a different way. The recommended approach to maintaining consistency between FHIR resources is through Design Patterns. In R4 there are four such patterns. In an OO language like Java a FHIR Design Pattern would typically be represented by an interface which can be implemented by multiple resource type classes. If you have found other potential commonalities between resources you might want enter a tracker to create additional Design Patterns.
I have not submitted any change requests so far for a few reasons. Firstly I am told that many such change requests are knocked back. The main reason however is that I am not clear on what the release situation really is – how can there be so many obvious consistency problems and yet FHIR is R4? Additionally, people in INTEROPen UK (to whom I did point out the problems) tell me that these kinds of ‘problems’ are not concerns.
Deep inheritance was not the problem with H7v3. It didn’t have deep inheritance, although it did have other major problems and anti-patterns.
All I can say is that there is no real ‘modelling’ going on in the FHIR resources – the only structure is inline component resources in some resources, and the Reference() construct, but that’s it. Everything else that would normally be common is just replicated. So if you go and look in the HAPI FHIR classes you see wall to wall replication. Now, let’s imagine someone decides that the semantics of ServiceRequest.status should be different, or Practioner.address. Do you also go and change MedicationRequest.status, Patient.address, RelatedPerson.address? Are these things really the same things, or really distinct? How would anyone know?
This problem is obvious if you lift your head above one specific FHIR domain (which many unfortunately don’t). I was looking at the workflow aspects of FHIR a while back and realized that the medication resources were not following the common workflow pattern.
Conceptually, an Event is basedOn a Request. This feels like a robust model.
DiagnosticReport.basedOn Reference(CarePlan | ImmunizationRecommendation | MedicationRequest | NutritionOrder | ServiceRequest) What was requested
Observation.basedOn Reference(CarePlan | DeviceRequest | ImmunizationRecommendation | MedicationRequest | NutritionOrder | ServiceRequest) Fulfills plan, proposal or order
Procedure.basedOn Reference(CarePlan | ServiceRequest) A request for this procedure
Encounter.basedOn Reference(ServiceRequest) The ServiceRequest that initiated this encounter
But when it comes to Medication events, they do not have basedOn, instead they seem to have corresponding elements with different names
MedicationDispense.authorizingPrescription Reference(MedicationRequest) Medication order that authorizes the dispense
MedicationAdministration.request Reference(MedicationRequest) Request administration performed against
Workflow is a very complex area, and these inconsistencies do not make it easier. What surprised me was that I got pushback when pointing this out (https://chat.fhir.org/#narrow/stream/179166-implementers/topic/Medication.20workflow.20and.20reference.20to.20MedicationRequest).
The inconsistency to me shows there is a level of governance that is missing around the FHIR resource models and their semantics. With that said, I belive FHIR is doing a lot of good things for solving actual business problems. Hopefully these issues can be recognized and ironed out.