We put a FHIR layer in front of the systems already running — EHR, laboratory, imaging, legacy systems with no API — so they speak the standard without being replaced, with conformance verified and operations untouched.
Replacing the system is not always the answer. Leaving it mute is not either.
The systems that carry clinical operations can rarely be switched off: they hold years of data, processes tied to them, live contracts and users who depend on them working tomorrow at seven. Yet they are increasingly asked to take part in exchanges they were never designed for: expose a result, report to an authority, feed a consolidated view, connect to a network. A FHIR layer resolves that tension by translating between what the system stores and what the ecosystem requires, without touching its database or its operation. Designed well, it buys years of headroom. Designed badly, it becomes the next legacy system: one more box nobody dares to touch.
Starting point
What you are actually starting from
«Legacy systems» says nothing. What defines the work is the real shape of the source and what you find once you open it.
The EHR database
Direct access to the clinical system's relational schema, almost always read-only and against a replica, so it does not compete with operations.
What you run into
Undocumented tables, fields reused for two different things, status flags only their author understands, and business rules that live in the application rather than the model.
HL7 v2 over MLLP
Admission, order and result messaging already flowing between systems, which can be used as a source instead of building a new extraction.
What you run into
Site-specific Z segments, optional fields used creatively, and the fact that a message describes an event, not the patient's current state.
Flat files and batches
CSV, fixed-width or XML the system already generates for reports or for other consumers, processed in batches.
What you run into
Fixed generation windows, partial or repeated deliveries, legacy character encodings, and no mechanism to tell what changed since the previous file.
SOAP services and proprietary APIs
Interfaces the vendor already exposes, built for one specific consumer and with a data model of their own.
What you run into
Granularity that does not match the FHIR resource, undocumented pagination and concurrency limits, and contract changes that arrive without notice.
PACS and DICOM
Imaging studies and reports, where the metadata lives in the equipment and the PACS before it reaches the clinical record.
What you run into
Patient identifiers different from the EHR's, reports as free text inside the study, and volumes that make materializing unworkable without clear criteria.
Spreadsheets and manual catalogs
The catalogs holding real processes together even though they live in no system: services, practitioners, code equivalences.
What you run into
No version control, no formal owner, and the truth spread across several copies that do not agree with each other.
Delivery modes
Translate on the fly or materialize: the decision that shapes everything else
It is the first architectural decision and the one that determines cost, latency and who feels an outage. It is almost never a pure choice.
On the fly
The layer translates each query against the source system, in the moment.
Materialized
Data is transformed and loaded into a FHIR repository that serves the queries.
Data freshness
On the flyAlways the source's current state. There is no staleness window to explain or to defend clinically.
MaterializedAs fresh as the last load cycle. That window has to be defined, communicated and confirmed as clinically acceptable.
Load on the source
On the flyEvery external query hits the system carrying clinical operations. A badly behaved consumer becomes a clinical incident.
MaterializedExtraction is scheduled in low-demand windows and external queries never touch the source.
Coupling to availability
On the flyIf the source goes down or into maintenance, the FHIR API goes with it: its maintenance window becomes yours.
MaterializedThe layer keeps answering while the source is stopped. Its downtime turns into older data, not a dead service.
Query and performance
On the flyEvery FHIR search must translate into a query the source can resolve quickly. What its schema does not index cannot be offered.
MaterializedIndexes follow the search parameters the guide requires, not however the source's schema was left fifteen years ago.
History and reprocessing
On the flyYou see what the source keeps. If it overwrites instead of versioning, the history does not exist and cannot be reconstructed.
MaterializedVersions can be kept one by one, and the full history reprocessed when the mapping changes or an error surfaces.
Writing back
On the flyWriting into the source means honoring its business rules and validations, which are hardly ever documented.
MaterializedWrites are accepted at the layer and propagated later, which forces you to resolve conflicts and declare which version wins.
Governing the copy
On the flyThere is no copy: nothing to decide about who owns the duplicate, how long it is kept or how it is deleted.
MaterializedThere is a copy of clinical data, with everything that entails: owner, retention, access control, audit and right to erasure.
Cost of operating
On the flyLittle infrastructure, but performance depends on a system you do not control and each new consumer changes the load profile.
MaterializedInfrastructure and operations of your own — storage, monitoring, reprocessing — in exchange for predictable behavior.
What actually gets used
Pure extremes are uncommon. Most layers that operate well are deliberate combinations of both.
Cache with a declared lifetime: translation happens on the fly, but the response is kept for a defined period documented to the consumer.
Event-based change capture: the source announces what changed — through messaging, triggers or CDC — and only that gets transformed.
Read on the fly, write deferred: queries go to the source while writes are queued and reconciled, which is where the conflicts live.
Partial materialization: only the domain that needs it — results, reports — is materialized, and the rest translates on the fly.
A separate index for what the source cannot resolve: searches its schema does not support are served from a structure of their own.
Methodology
How we build the layer
Six stages. The first decides what can be taken out of the source without risking clinical operations.
01
Source analysis
Before mapping anything, we work out what the system really stores and what can be extracted without endangering what already works.
Schema, interfaces and available access mechanisms
Data profiling and data quality
Operational windows and load limits
Business rules living in the application
02
Model and mapping
We decide which resource represents each data point, which profile applies and where every element comes from — including the ones the source does not have.
Element-by-element mapping
Terminologies and ConceptMaps for local codes
Identifier and reference strategy
What to do with missing data
03
Layer design
Delivery mode, scope per domain, security and the contract with consumers. This is where what is expensive to change later gets settled.
On the fly, materialized or hybrid
Authentication, authorization and audit
Target CapabilityStatement
Versioning and compatibility policy
04
Build and transformation
Transformations, exposure and error handling get implemented, with edge cases treated as part of the scope rather than as surprises.
Extraction and transformation pipelines
Endpoints and search parameters
Idempotency and retries
A record of what could not be transformed
05
Conformance validation
The layer is tested against the profiles it must meet, with valid examples and with examples that must be rejected.
Validation against the applicable guide
Search and pagination tests
Comparison against the source
Load testing in a real window
06
Operation and observability
A layer without monitoring fails silently and is discovered when someone complains about data that never arrived.
Latency, error and freshness metrics
Alerts on untransformed data
Traceability from source to resource
A procedure for when the source changes
Conformance
Actually FHIR, not JSON with FHIR names
The difference between a layer that survives a demo and one that holds up an ecosystem is in details that only surface when a third party tries to consume it.
Validation against profiles
The usual shortcut
The JSON is assembled by hand until it «looks right» and tested only against your own client.
What holds up
Every resource is validated against the applicable guide's profile, in continuous integration, with examples that must pass and examples that must fail.
An honest CapabilityStatement
The usual shortcut
A copied CapabilityStatement is published, declaring operations and searches nobody implemented.
What holds up
It declares exactly what the layer supports. A consumer should be able to plan their integration by reading it, not by discovering the hard way what works.
Searches that work
The usual shortcut
Two or three parameters are implemented and the rest are silently ignored, returning everything as if the filter did not exist.
What holds up
Declared parameters are implemented, or they are not declared. And pagination is tested with the source's real volumes, not with test data.
Identifiers, references and codes
The usual shortcut
Internal keys are used as if they were FHIR ids, resources the layer never exposes get referenced, and local codes are sent without saying where they come from.
What holds up
Every reference resolves or is declared logical, business identifiers travel in identifier with their system, and every code carries its own.
Errors you can debug
The usual shortcut
A generic 500 is returned on failure or, worse, a 200 carrying an incomplete resource.
What holds up
Errors come back as an OperationOutcome, with severity, code and a location that tells the consumer what to fix.
The data that does not exist
The usual shortcut
It gets filled with a default value, an invented date or a generic code so the resource will validate.
What holds up
What is absent is represented as absent. Where the guide requires the element, the mechanisms meant for unknown data are used and the gap is documented, rather than fabricating it.
Results
What your organization receives
The layer in operation, plus everything needed to consume it, audit it and evolve it without us.
The FHIR layer in operation
The service deployed, with its endpoints, its security and its configuration documented.
Documented mappings
Element-by-element correspondence between the source and the resources, with decisions recorded.
ConceptMaps and catalogs
Declared equivalences between local codes and the standard terminologies.
CapabilityStatement
What the layer actually supports: resources, operations and search parameters.
Validation suite
Valid examples, examples that must fail, and the tests that run on every change.
Monitoring and alerts
Latency, error and freshness metrics, with alerts on whatever could not be transformed.
Consumer guide
How to authenticate, what to search, what to expect and what limits the layer has.
Evolution path
What gets materialized next, which domains follow, and when to revisit the delivery mode.
Experience
FHIR layers in operation
FHIR infrastructure built over systems and platforms that already existed and kept running.
What the layer solvedReceiving and validating reports
First national FHIR server — EDUS/CCSS
Meddyg implemented and deployed to production the first FHIR server to receive reports of notifiable diseases, vaccines and laboratory results from EDUS/CCSS, later extended to other health providers.
What the layer solvedEntry point for regional reporting
AEFI receiving infrastructure
Part of the Meddyg team acted as a consultant on the design and implementation of the infrastructure that would receive regional AEFI reports, alongside the guide defining their format.
What the layer solvedThe standard over national transport
FHIR transactions over X-Road
Proofs of concept with Costa Rica's National Digital Government Agency to validate FHIR resource transactions over the national X-Road platform, as part of the country's functional interoperability demonstration.
What the layer solvedNon-repudiation at the exchange layer
Digitally signing FHIR resources — BCCR
Technical assistance so the Central Bank of Costa Rica's teams could enable signing of FHIR resources with the JAdES format inside GAUDI, its digital signature and sealing platform.
The cases above are FHIR infrastructure built over systems and platforms that were already running. The same method — source analysis, mapping, choice of delivery mode and conformance validation — is what we apply to a facade over a legacy EHR or departmental system.
Profile
Who is this service for?
They all share one constraint: the source system cannot be stopped or replaced within the timeframe in which they have to interoperate.
Hospitals with a legacy EHR
A clinical system that will not be replaced and is now being asked to expose data, report, or connect to a network.
Health software vendors
Products that must demonstrate FHIR capability to clients and tenders without rebuilding their database.
Laboratories and imaging centers
Results and reports that must reach multiple external receivers with the same structure and the same codes.
Public institutions
Mission-critical systems that must take part in a national ecosystem without opening their database or stopping operations.
Health information exchanges
Ecosystems onboarding participants whose systems do not speak the standard and that need a repeatable connection pattern.
Insurers
Exchange with providers whose systems expose formats of their own and evolve at their own pace.
Self-assessment
Do any of these sound familiar?
We are being asked for a FHIR API and our system has no API at all.
Replacing the EHR is off the table, but interoperating is not.
Every new integration ends up as a bespoke export.
We expose JSON we call FHIR and no validator accepts it.
We do not know whether to translate on the fly or load into a repository.
External queries hit the same database clinical operations run on.
When the source system goes down, everything we exposed goes with it.
Our vendor does not expose what we need and it is not on their roadmap.
A consumer flagged a badly mapped field and we could not tell since when.
We publish a CapabilityStatement that does not match what the API does.
We have no way of knowing which records failed to transform.
We need to connect to a national network and our data cannot leave the system.
If any of these describe your organization, a FHIR layer can solve the exchange without putting at risk what already works.
Is FHIR being asked of a system that does not speak it?
A well-designed layer lets today's systems take part in the ecosystem without touching their operation or mortgaging the modernization path.