A FHIR-ready architecture is one where exposing or consuming HL7 FHIR is a mapping job, not a rebuild. A product that bolts FHIR on late can discover that its identifiers, codes and data shapes do not line up with what partners expect, and the fix touches every table.
This guide covers the design decisions that make FHIR cheap later: where the canonical model sits, how identifiers and terminology are handled, how the API layer maps to FHIR, and how versions are managed once partners depend on it.
What is a FHIR-ready architecture?
Designing a product that must speak FHIR?
Agnotic designs and builds FHIR-ready systems for clients, from the canonical model to a US Core API layer.
A FHIR-ready architecture is a system design in which internal data already carries the identifiers, codes and structure that HL7 FHIR needs, so a separate API layer can expose it as FHIR resources without reshaping the product. It separates source systems, integration, a canonical model with terminology, a FHIR API layer and consumers.
The goal is not to make every table a FHIR resource. It is to avoid the design choices that make FHIR mapping lossy or slow: free-text codes, identifiers with no issuing system, dates without precision, and records that cannot say where they came from.
Should the product store FHIR natively or map to it?
A product should store FHIR natively only when FHIR is its main interface and its data fits FHIR resources well. A product with its own workflows is better served by a canonical model designed for their own workflows, with a mapping layer to FHIR at the edge.
Native FHIR storage works well for data exchange hubs, clinical data repositories and products whose users are other systems. It gives search and validation for free, but it ties the internal model to FHIR's structure and version, and workflow data such as task queues and billing state does not always fit.
A canonical model with a mapping layer suits products with rich internal workflows. The model can change for product reasons while the FHIR API stays stable, and one model can feed FHIR, HL7 v2 and file exports at the same time.
Either way, the base version matters. FHIR R4, version 4.0.1, is the release US exchange is built on, and HL7 lists US Core 9.0.0, also on 4.0.1, as the current published US Core version. Design to the US Core version your partners use, not only the newest one.
How do the layers of a FHIR-ready system fit together?
The layers fit together as a pipeline from source systems to consumers, with the canonical model in the middle. Each layer has one job, and keeping them separate is what makes the design FHIR-ready.
- 1. Source systems: EHRs, devices, labs, payer feeds and the product's own application database, each with its own formats and identifiers.
- 2. Integration layer: interfaces that receive HL7 v2 messages, FHIR resources, files and events, validate them and pass normalized records on, keeping the raw payload for audit.
- 3. Canonical model and terminology: the product's own model of patients, encounters, observations and other entities, with every code checked against a terminology service and every record carrying provenance.
- 4. FHIR API layer: a service that maps the canonical model to FHIR resources and US Core profiles, handles search, enforces SMART on FHIR authorization and publishes a CapabilityStatement.
- 5. Consumers: apps, partner systems, analytics and the product's own user interface, which read through the API layer or through internal services built on the same model.
The direction of dependency matters. Source formats should never leak past the integration layer, and FHIR structures should never leak below the API layer. When a partner changes its feed or HL7 publishes a new US Core version, only one layer changes.
Designing the canonical model
The canonical model should be designed around the product's entities and workflows, with each entity's fields chosen so it can map to the matching FHIR resource without guessing. Review the target US Core profiles while designing tables, not after.
- Coded fields: store code, system and display together for every coded value, and keep the original source code alongside any mapped standard code.
- Dates and times: store precision and time zone, since FHIR distinguishes a year, a date and an instant, and clinical data often arrives with only part of a date.
- Status fields: model the lifecycle states the product needs, then map them to FHIR status value sets in the API layer rather than adopting FHIR's states internally.
- Provenance: record the source system, the received message or file and the time of receipt for every record, so the API can populate provenance and meta fields.
- Missing data: distinguish unknown, not asked and not applicable where it matters, so the API layer can omit an element or send a data absent reason correctly.
Keep the model independent of any single source. If the product's patient table mirrors one EHR's columns, the second EHR integration becomes a redesign.
Model consent and access as data, not code. Store who may see which records, under which purpose and for how long, in the canonical model, so the API layer can enforce the same rules for every consumer and an auditor can see why a request was allowed.
How should identifiers work across systems?
Identifiers should always be stored as a pair: the value and the system that issued it. FHIR represents business identifiers this way, and a value without its system cannot be matched safely across organizations.
Give every entity an internal identifier the product controls, then attach external identifiers such as medical record numbers, payer member IDs and device serial numbers as separate rows, each with its issuing system URI. A patient can then carry several MRNs from several sites without confusion.
Decide early which identifier the FHIR API exposes as the resource id. Using the internal identifier keeps ids stable even when external identifiers change or merge. Never expose a medical record number as a resource id.
Plan for merges and unmerges. When two patient records turn out to be the same person, the model must redirect references and keep an audit trail, and the API must return something sensible for the retired id.
Where does terminology belong?
Terminology belongs in a dedicated service that the integration layer, the canonical model and the API layer all call. Spreading code lists across application tables and config files leads to drift between what the product stores and what it says it sends.
The service holds the code systems the product uses, such as LOINC, SNOMED CT, RxNorm and local codes, plus the value sets US Core binds to and the maps between local and standard codes. It answers three questions: is this code valid, which value set does it belong to, and what is its standard equivalent.
Map on the way in, not only on the way out. Normalizing codes at the integration layer means search, analytics and the API all see the same standard codes, while the original source code stays stored for audit.
Version the terminology content too. Code systems such as LOINC and SNOMED CT publish new releases, and value sets change with them. Record which release each mapping was made against, and rerun validation of stored codes when a new release is loaded, so retired or replaced codes are found before a partner rejects them.
How do you version a FHIR API layer?
A FHIR API layer is versioned by the FHIR release and the profile versions it conforms to, both declared in its CapabilityStatement. Changes inside a version must not break existing consumers, and a new US Core version is a planned release with notice.
Keep mappings as versioned code with tests. Each mapping from canonical model to profile should have fixture records and expected FHIR output, validated against the target profile packages in the build pipeline.
When a new US Core version arrives, run both mappings side by side for a period if partners need time. Route requests by base URL or by a declared profile, and retire the old version on a date agreed with consumers.
Remember the certification context. Certified EHRs must provide a FHIR read API under the 170.315(g)(10) Standardized API criterion, so partners may compare your API with theirs. Matching their US Core version removes a whole class of integration questions.
Design checks before the first partner connects
These checks catch the failure points that make FHIR expensive later. Run them during design review and again before the first external connection.
- Codes: every coded field stores system and code, and no clinical meaning lives only in free text.
- Identifiers: every external identifier has an issuing system, and resource ids are internal and stable.
- Provenance: every record can say where it came from and when it arrived.
- Layer boundaries: no source format appears above the integration layer, and no FHIR structure appears below the API layer.
- Terminology: one service owns code systems, value sets and maps, and both ingestion and the API call it.
- Validation: the build pipeline validates sample output against the target US Core profiles.
- Versioning: the CapabilityStatement declares the FHIR and profile versions, and a written policy covers upgrades.
- Security: the API layer enforces SMART on FHIR scopes and logs every access, separate from internal service authorization.
How Agnotic can help
The cheapest moment to make a system FHIR-ready is before the first table is created. Agnotic Technologies designs and builds FHIR-ready systems for clients: canonical models reviewed against US Core, identifier and provenance design, terminology services, FHIR API layers with SMART on FHIR security, and validation in the build pipeline.
For an existing product, Agnotic can assess the current model against the checks above and plan a staged path that adds an API layer without stopping feature work.
Design a FHIR-ready system with Agnotic
Explore Agnotic's FHIR development services, or browse the FHIR articles hub.
Related reading on Agnotic
Continue with healthcare software architecture, FHIR facades, choosing FHIR standards and mapping HL7 v2 to FHIR.


