From archetype to profile - openEHR and FHIR on one blood pressure
Second in the series. A worked, evidenced derivation of a FHIR blood-pressure profile from the openEHR blood pressure archetype.
openEHR and HL7 FHIR are often presented as rivals. They are not. They solve different problems, and a well-built health system uses both: openEHR to model and persist the clinical record, FHIR to expose and exchange slices of it. This post shows that complementarity in technical detail, by deriving a FHIR blood pressure profile from the openEHR blood pressure archetype - the one concept both communities model canonically.
Everything below is traceable to a primary source; identifiers and fragments are quoted from the published artefacts, linked at the foot.
Two ways to constrain a base model
Both standards are constraint models layered on a stable base, which is exactly why they map cleanly.
openEHR uses two-level modelling:
a small, stable Reference Model (RM) of classes like OBSERVATION and
DV_QUANTITY, and an upper level of archetypes that constrain the RM into
clinical concepts ("blood pressure"), further narrowed by templates into a
specific form. Archetypes are maximal - they capture every field a concept could
need - and are governed internationally in the
Clinical Knowledge Manager.
FHIR does the same one layer up: a base resource (Observation) constrained
by a profile (a StructureDefinition) that fixes cardinalities, codes,
terminology bindings and slices for a use case.
An openEHR archetype and a FHIR profile are therefore the same shape of artefact - a constraint on a base type - at two different altitudes. Deriving one from the other is a path-to-path mapping plus a datatype transform.
The source: the blood pressure archetype
The archetype openEHR-EHR-OBSERVATION.blood_pressure.v2 constrains the RM
OBSERVATION class. Its systolic value node, quoted from the
published ADL:
ELEMENT[at0004] occurrences matches {0..1} matches { -- Systolic
value matches {
C_DV_QUANTITY <
property = <[openehr::125]> -- pressure
list = <["1"] = <
units = <"mm[Hg]">
magnitude = <|0.0..<1000.0|>
precision = <|0|>
>>
>
}
}
Diastolic is the same constraint at node at0005. The archetype also carries
at1006 (mean arterial pressure), at1007 (pulse pressure), a state element
at0008 (Position: Standing/Sitting/Reclining/Lying), and protocol elements
at0013 (Cuff size) and at0014 (Location of measurement). Note the units are
already UCUM (mm[Hg]) and the node IDs (at0004) are stable, language-neutral
anchors - both matter for the mapping.
The target: the FHIR blood pressure profile
HL7 publishes a vital-signs blood pressure profile, canonical URL
http://hl7.org/fhir/StructureDefinition/bp, on the Observation resource. Its
key constraints, from the R5 specification:
Observation.categoryfixed tovital-signs.Observation.codefixed to LOINC85354-9(blood pressure panel).- The main
Observationcarries novalueQuantity; the readings live in two required component slices. component:SystolicBP(1..1): code LOINC8480-6,valueQuantityin UCUMmm[Hg].component:DiastolicBP(1..1): code LOINC8462-4, UCUMmm[Hg].
The derivation, node by node
| openEHR node | RM constraint | FHIR (bp profile) target | Terminology / units |
|---|---|---|---|
OBSERVATION (root) |
OBSERVATION |
Observation, category |
vital-signs |
| concept (the panel) | archetype concept | Observation.code |
LOINC 85354-9 |
Systolic at0004 |
C_DV_QUANTITY, mm[Hg] |
component:SystolicBP |
LOINC 8480-6, UCUM mm[Hg] |
Diastolic at0005 |
C_DV_QUANTITY, mm[Hg] |
component:DiastolicBP |
LOINC 8462-4, UCUM mm[Hg] |
Position at0008 |
DV_CODED_TEXT (internal) |
Observation.bodyPosition (R5) |
bind internal codes to SNOMED CT |
Cuff size at0013 |
DV_CODED_TEXT |
no native element - extension | local ValueSet |
occurrences {0..1} |
archetype cardinality | element min/max |
- |
Two transforms do most of the work. Datatype: openEHR DV_QUANTITY{units}
becomes FHIR Quantity with system = http://unitsofmeasure.org; the mm[Hg]
string carries across unchanged because both already use UCUM. Terminology:
the archetype's value sits at a stable node (at0004); the profile binds the
external LOINC code (8480-6) to the matching component. The archetype's
occurrences become the element's min/max.
Expressed as a FHIR StructureDefinition differential, the systolic slice is:
{ "id": "Observation.component:SystolicBP.code",
"path": "Observation.component.code",
"patternCodeableConcept": {
"coding": [{ "system": "http://loinc.org", "code": "8480-6" }] } },
{ "id": "Observation.component:SystolicBP.valueQuantity.system",
"path": "Observation.component.valueQuantity.system",
"fixedUri": "http://unitsofmeasure.org" },
{ "id": "Observation.component:SystolicBP.valueQuantity.code",
"path": "Observation.component.valueQuantity.code",
"fixedCode": "mm[Hg]" }
A reading of 120/80 captured against the archetype (at0004 = 120 mm[Hg],
at0005 = 80 mm[Hg]) then serialises to a profile-conformant resource:
{ "resourceType": "Observation", "status": "final",
"category": [{ "coding": [{ "code": "vital-signs",
"system": "http://terminology.hl7.org/CodeSystem/observation-category" }]}],
"code": { "coding": [{ "system": "http://loinc.org", "code": "85354-9" }] },
"component": [
{ "code": { "coding": [{ "system": "http://loinc.org", "code": "8480-6" }] },
"valueQuantity": { "value": 120, "unit": "mmHg",
"system": "http://unitsofmeasure.org", "code": "mm[Hg]" } },
{ "code": { "coding": [{ "system": "http://loinc.org", "code": "8462-4" }] },
"valueQuantity": { "value": 80, "unit": "mmHg",
"system": "http://unitsofmeasure.org", "code": "mm[Hg]" } } ] }
Where the gaps are - and why that is the point
The mapping is not total, and the residue is instructive. The archetype's cuff
size (at0013) and measurement location (at0014) have no native home in the
bp profile; they become FHIR extensions or are simply not exchanged. This is
the maximal-versus-minimal distinction in practice: openEHR retains the full
clinical context for the record, while a FHIR profile carries the subset a given
exchange needs. It is also why tooling tends to map FHIR to openEHR rather
than the reverse - the openEHR side is the superset.
This is not theoretical. The official
openEHR FHIR Test IG
publishes a StructureDefinition generated from this very archetype, preserving
the at-codes as element labels (paths such as OBSERVATION.protocol.items:at0013).
Better's FHIRconnect
specifies declarative, bidirectional mappings between openEHR templates and FHIR
profiles, and peer-reviewed work has
mapped openEHR archetypes to FHIR resources in oncology.
The takeaway
Model the concept once as a governed archetype; persist the maximal record and query it with AQL; then derive a FHIR profile as the exchange contract for each use case. openEHR is the clinical source of truth, FHIR is the interface to it. Used together, the "right exists in law, the infrastructure does not yet exist" gap from the last post gets a little smaller: this is what the infrastructure actually looks like, one archetype at a time.
Sources
- openEHR - Archetypes and templates (two-level modelling); Clinical Knowledge Manager
- openEHR - blood_pressure.v2 archetype (ADL source)
- HL7 FHIR - Blood pressure profile (R5) and Vital Signs profiles; US Core Blood Pressure
- LOINC - panel
85354-9, systolic8480-6, diastolic8462-4(loinc.org) - Bridging work - openEHR FHIR Test IG; FHIRconnect mapping spec; Mapping openEHR archetypes to FHIR in oncology (PubMed)