Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Architecture

FerroBRIDGE is a standalone bridge between openEHR and two interoperability targets: HL7 FHIR through the FHIRconnect specification, and the OMOP Common Data Model through the OMOCL specification. It runs beside an openEHR CDR and reaches it only over the openEHR ITS-REST API, so it works against a CDR it did not build. This page is the short tour. The design authority, with the ground for every decision, is docs/architecture.md in the repository.

The shape

graph LR
    FHIRCLIENT["A FHIR client"] -->|"FHIR R4 REST"| FACADE["FerroBRIDGE: the FHIR facade"]
    FACADE -->|"FHIRconnect mappings"| CORE["The shared mapping foundation"]
    ETL["FerroBRIDGE: the OMOP ETL runner"] -->|"OMOCL mappings"| CORE
    CORE -->|"openEHR ITS-REST"| CDR["An openEHR CDR"]
    FACADE -->|"$lookup, $translate, $validate-code"| TERM["A FHIR terminology server"]
    ETL -->|"typed CDM rows"| CDM["An OMOP CDM v5.4 database"]
    VOCAB["The OHDSI vocabulary, loaded"] --> ETL

Both sides read openEHR content the same way and write to different places. The FHIR side answers requests one at a time and keeps no clinical data of its own. The OMOP side runs as a batch job, queries the CDR with AQL, and writes rows.

One foundation, two interpreters, two sinks

FHIRconnect and OMOCL are written by the same author and share one header (grammar, type, metadata, spec, and spec.openEhrConfig pinning the archetype and revision). Below that header they are different languages: one is bidirectional and tree-shaped against FHIR paths, the other is one-directional and flat against CDM columns. FerroBRIDGE shares the header model, the YAML loader, the archetype-keyed mapping registry, and the RM-path model, then gives each language its own parser, validator, and interpreter. A single intermediate representation over both grammars would be the union of two dissimilar languages, and it would break the first time either specification moved.

The two mapping specifications page has what each language actually says.

The openEHR side

Mapping paths in both languages are RM and archetype paths such as $archetype/data[at0001]/items[at0077], never FLAT paths. A path alone is not executable: the leaf RM type and the template-specific identifiers come from the Web Template built from the template’s operational template. The order is the operational template, then the Web Template, then path resolution, then the composition. FerroBRIDGE fetches the operational template from the CDR, builds the Web Template locally with the published openehr-* crates, resolves paths against the canonical composition tree, and commits and reads canonical JSON. FLAT never crosses the wire.

FHIRconnect requires the context mapping’s start entry to slot in a reusable COMPOSITION.<archetype>.<Resource> mapping, and it defaults the composition-level fields that have no FHIR counterpart: composer, context/start_time, setting, language, territory, and category. FerroBRIDGE follows those defaults and records every defaulted value in the composition’s FEEDER_AUDIT, so a reader can tell a mapped value from a defaulted one.

The FHIR side

FerroBRIDGE exposes a FHIR R4 REST facade and maps each request onto CDR operations. It stores no clinical data. A transaction Bundle is all or nothing and a batch Bundle answers per entry, as FHIR R4 defines them. Bundles are split by the profiles in meta.profile, a resource that two mappings both need becomes a linked mapping, and unresolved references are fetched from the sending site with cycle protection. Beside the facade, the bridge serves the two operations the FHIRconnect specification is adding in its draft REST API chapter, $tofhir and $toopenehr, as the engine’s own conformance surface; the facade is a client of the same in-process engine.

flowchart LR
    F["FHIRconnect YAML<br/>model, extension, context"] --> M["model<br/>published schemas exercised,<br/>strict schemas, semantic checks"]
    M --> R["resolve<br/>one immutable program<br/>per profile and template"]
    R --> E["engine<br/>one traversal, both directions,<br/>data-type lenses"]
    T["tree<br/>path model over FHIR JSON<br/>guided by the element table"] --- E
    W["Web Template index"] --- R
    E --> O1["$tofhir, $toopenehr"]
    E --> O2["The facade over the CDR"]
    E -->|"external codes only"| TS["Terminology server"]

FHIRconnect states that it “does not focus specifically on AQL and FHIRsearch”, so FHIR search has no specification behind it here. The search design is FerroBRIDGE’s own: an AQL projection per resource type declared beside the context mapping, executed over POST /query/aql, with the CapabilityStatement naming exactly which search parameters each resource supports and an OperationOutcome refusing the rest. The FHIR facade page has the detail.

The OMOP side

OMOP is a relational analytics schema rather than an API, so the deliverable is rows in a populated CDM v5.4 database. The OMOP engine is a batch ETL job runner over AQL, writing typed CDM rows into a PostgreSQL CDM database built from the OHDSI DDLs, with the derived tables generated rather than mapped.

OMOP concept resolution is SQL over the locally loaded OHDSI vocabulary: the CONCEPT table, standard_concept, the domain, and the CONCEPT_RELATIONSHIP “Maps to” traversal. It is never a FHIR terminology operation. An unmapped code lands as concept_id = 0 with the source value kept, which is what the CDM means by “no matching concept”, and it is never dropped. The OMOP ETL page has the detail.

flowchart TB
    Q["AQL result set, streamed"] --> C["One composition"]
    C --> E["omocl engine"]
    E --> G["Record graph<br/>rows and FACT_RELATIONSHIP links"]
    G --> R["Concept resolver<br/>SQL over the loaded vocabulary"]
    R --> K["Side table<br/>natural key, surrogate id, watermark"]
    K --> W["binary COPY<br/>one composition, all or nothing"]
    W --> DB[("OMOP CDM v5.4")]
    W --> REP["Run report<br/>rows, concept 0, refusals"]

Generated and hand-written

Two models are generated, because both are published in machine-readable form and a hand-transcribed copy drifts from its source with no way to detect it. The FHIR model, the fhir-types crate, is emitted by fhir-codegen from the HL7 FHIR packages; it moves into this repository from the sibling terminology server, which then consumes it from crates.io. The OMOP CDM v5.4 row types are emitted from the OHDSI CommonDataModel field definitions, with the OHDSI PostgreSQL DDL vendored verbatim beside them. The openEHR model, and the ITS-REST client the bridge calls the CDR through, come from the published openehr-* crates. Everything that makes FerroBRIDGE a bridge is hand-written: the mapping foundation, both mapping languages’ types, validators and interpreters, the FHIR path model, the terminology client, the facade, and the ETL runner.

Where the specifications are silent

FerroBRIDGE labels its own decisions as its own, in the code and in these pages. The FHIRconnect engine chapter is explicitly a set of recommendations, and no specification governs FHIR search, identity, extension ordering, or failure policy. Each of those is pinned as a FerroBRIDGE decision on the failure and identity page and in the architecture document.