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:

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