The master data model and FHIR interoperability
What AWS HealthLake provides as a FHIR R4 store, how the four master entities map onto FHIR resources, and the master schema that has to exist outside FHIR.
markdownexamplev1J8ZS-SF0Gstage: designtrust: mixedservice: healthlakeservice: auroraservice: athenaservice: glueservice: comprehend-medicalservice: eventbridgeinterface: architectureAWS HealthLake FHIR R4 data storeFHIR Patient Practitioner Organization LocationSMART on FHIR and Bulk Data Accessmaster entity schema in Auroracrosswalk and attribute provenance tablesHealthLake integrated medical NLPFHIR Linkage resourcesOrganization partOf hierarchy limits
A healthcare master needs two data models, not one. FHIR is the interoperability model — the shape in which clinical systems exchange information. The master model is the governance shape, carrying survivorship decisions, provenance per attribute and the crosswalk. Conflating them is the most common design error in this pattern.
What HealthLake gives you
AWS describes it as "a HIPAA eligible service for storing, analyzing, and sharing health data in the cloud using the Fast Healthcare Interoperability Resources (FHIR) R4 specification". The version is stated unambiguously and repeatedly: FHIR R4.
The capabilities that matter for a master:
| Capability | What the documentation states |
|---|---|
| Transactional FHIR server | "FHIR APIs for standard resource validation, SMART on FHIR authorization" |
| Import | Native import "from an Amazon S3 bucket to an HealthLake data store" |
| Export | "Bulk data FHIR API export capabilities" |
| Chronological patient view | "creates a complete view of each patient's medical history" |
| Integrated NLP | Extracts entities and PHI, "stored as native FHIR R4 resources" |
| SQL access | NLP entities "accessed through FHIR R4 APIs or Amazon Athena (SQL)" |
| Duplicate detection | "automatically detects and links duplicate records (Preview)" |
| Legacy conversion | Data Transformation Agent for C-CDA and CSV (Preview) |
The standards conformance claim is specific, and it is the sentence that matters for any 21st Century Cures Act discussion: HealthLake "supports customer conformity to the latest ONC and CMS regulatory standards including: HL7 FHIR R4 APIs, FHIR Bulk Data Access, US Core IG STU, HL7 SMART App Launch Framework IG, OAuth 2.0, and OpenID Connect."
Two constraints belong next to that list. The clinical disclaimer is explicit — HealthLake "is not a substitute for professional medical advice, diagnosis, or treatment" and "should only be used in patient care or clinical scenarios after review by trained medical professionals applying sound medical judgment". And the two most MDM-relevant features, resource matching and the Data Transformation Agent, are both labelled Preview, which raises the eligibility question set out in document 04.
Mapping the four master entities
| Master entity | FHIR R4 resource | Natural identifiers |
|---|---|---|
| Patient | Patient | MRN per assigning system, SSN, member id |
| Provider | Practitioner, plus PractitionerRole | NPI, DEA, state licence |
| Organisation | Organization | NPI type 2, tax id, CCN |
| Location | Location | Facility id, NPI where applicable |
FHIR's identifier element is the reason HealthLake's matching works at all, and its semantics line
up exactly with how healthcare identity actually behaves. HealthLake distinguishes "globally unique
identifiers" such as an NPI or SSN, "matched on value", from "scoped identifiers" such as an MRN,
which are "only unique within a namespace (a hospital, a state, a manufacturer)" and are "matched on
the combination of the identifying system and value, so that MRN 456 at one hospital is never
linked to MRN 456 at another". It also normalises identifier system URIs, treating an SSN under
http://hl7.org/fhir/sid/us-ssn and urn:oid:2.16.840.1.113883.4.1 as the same identifier.
Design consequence: get the identifier.system URIs right at ingestion. A per-source registry of
assigning authority to canonical system URI is small, dull and completely load-bearing. If two
hospitals' MRNs arrive under the same system URI, you will create false links that no amount of
downstream logic can repair.
When HealthLake finds a match it "creates a FHIR Linkage resource that points to the linked
resources and leaves the originals unchanged". A Linkage is a first-class FHIR resource, so
consumers that understand FHIR can traverse it. It also means the duplicates remain visible, which
is correct for clinical safety and inconvenient for naive consumers — another reason the master
needs its own resolved view.
The master schema outside FHIR
FHIR has no construct for "this attribute came from the registration system, was last confirmed on this date, won under this survivorship rule, and was overridden by this steward". That lives in Aurora PostgreSQL. A minimal but sufficient shape:
master_entity
master_id uuid PK -- yours, minted here, never a vendor match ID
entity_type text -- patient | provider | organization | location
status text -- active | merged_into | retired
merged_into uuid -- set when a steward merges two masters
created_at, updated_at, version -- version increments on any attribute change
master_attribute
master_id uuid FK
attribute_name text -- family_name, birth_date, primary_address ...
attribute_value jsonb
source_system text -- which source won
source_record_id text -- the record in that source
raw_object_key text -- the immutable S3 object it came from
survivorship_rule text -- which rule selected this value
confidence numeric
effective_from timestamptz
steward_override boolean
PK (master_id, attribute_name, effective_from)
crosswalk
master_id uuid FK
source_system text
source_record_id text
match_id text -- the Entity Resolution match ID for this run
match_rule text -- rule number, or null for ML
confidence numeric -- 0.0-1.0 for ML matches
linkage_id text -- the HealthLake Linkage, where one exists
first_seen, last_seen timestamptz
PK (source_system, source_record_id)
-- current state only; per-run history lives in an append-only journal (see document 06)
steward_decision
decision_id uuid PK
decision_type text -- merge | split | attribute_override | reject_match
subject_master_ids uuid[]
payload jsonb
decided_by text -- the federated identity, never a shared account
decided_at timestamptz
rationale text
Four properties make this schema do its job.
master_id is yours. The Entity Resolution glossary defines a match ID only as "the ID
generated by AWS Entity Resolution and applied to each matched record set ... part of the matching
workflow metadata". It is run output. master_id is the identifier consumers see, and the crosswalk
is what reconciles the two on every run.
Provenance is per attribute, not per record. A golden patient routinely has a surname from the
EHR and an address from registration. Auditing "where did this value come from" only works at
attribute granularity, with raw_object_key pointing back into the Object Lock bucket.
master_attribute is append-only. effective_from in the key gives history for free, which is
both the audit trail and the ability to answer "what did we believe on this date".
steward_decision is an input, not a log. It is replayed after every match run, which is what
stops reprocessing from reversing human judgement. See document 05.
Keep the source's own Unique ID discipline in mind on the way in: Entity Resolution requires "a
unique identifier that you designate ... assigned to each row of input data", unique within a table,
"maximum Unique ID length is 38 for a matching workflow". A composite of source system and record
id can exceed 38 characters; hash it if so.
Which store answers which question
| Question | Store | Why |
|---|---|---|
| Who is this patient, resolved? | Aurora | The golden record with provenance |
| What is this patient's clinical history? | HealthLake | FHIR R4, chronological |
| Which source records map to this master? | Aurora crosswalk | The mapping is governance data |
| Give me a FHIR Bundle for an external app | HealthLake | FHIR APIs, SMART on FHIR |
| How many duplicates did we resolve last month? | Athena over S3 | Cheap analytics over match output |
| What changed in the master, and when? | Aurora history | Append-only attribute versions |
Both directions of flow are legitimate. HealthLake can be a source — its Patient resources are
input to the curated zone and then to matching. It can also be a consumer — the resolved master
writes back a canonical Patient plus Linkage resources. A mature deployment does both, and the
order matters: match in the resolve account, then write the resolved view to HealthLake, so the FHIR
store reflects governed decisions rather than competing with them.
Unstructured text
Both HealthLake's integrated NLP and Comprehend Medical can mine free text. HealthLake's is the
better fit when the text already lives in the FHIR store, because extracted entities "are stored as
native FHIR R4 resources within a HealthLake data store". Comprehend Medical is the better fit in
the pipeline, before data lands, and offers ontology linking: InferICD10CM links conditions to
ICD-10-CM codes (the page names "the 2021 version"), InferRxNorm links medications to RxNorm, and
InferSNOMEDCT links concepts to SNOMED CT.
Its DetectPHI operation detects the entity types AGE, DATE, NAME, PHONE_OR_FAX, EMAIL,
ID, URL, ADDRESS and PROFESSION, all under the category PROTECTED_HEALTH_INFORMATION and
each with a confidence score; document 04 covers what that does and does not amount to under HIPAA.
For a master specifically, the realistic use is narrow: recovering demographics from unstructured registration notes to improve match coverage, and detecting PHI that arrived in a field where it should not be. It is English-only — Comprehend Medical "only detects medical entities in English language (US-EN) texts" — and its confidence scores need a threshold you choose and defend.
Where FHIR is the wrong model
FHIR does model organisational structure, and getting this wrong is an easy way to lose a healthcare
audience. The R4 specification states that "The Organization resource often exists as a hierarchy of
organization resources, using the _part-of_ property to provide the association of the child to its
parent organization", and Organization.partOf is defined as "The organization of which this
organization forms a part". A separate OrganizationAffiliation resource exists alongside it.
The real limitation is narrower and more interesting, and the cardinality is the evidence: partOf
is 0..1, one parent per organisation, with no element for effective dating. A health system
needs several simultaneous rollups over the same organisations — legal entity, billing and TIN,
service-line reporting — each with dates on it, so that "this rollup was true until April" is
answerable. That is what an MDM hierarchy manager supplies and FHIR does not.
What FHIR genuinely has no concept of is a survivorship rule, a trust score or a steward. Linkage
records that two resources are related; it does not express which attribute of which resource
prevails. None of that is a defect — FHIR is an exchange format, not a governance model.
The practical division follows. Master lookups belong in Aurora, because the governance data they need — provenance, survivorship rationale, steward decisions, the crosswalk — exists only there. HealthLake serves exchange: FHIR APIs, Bulk Data export, SMART on FHIR. Its cost shape is worth knowing when sizing that role: a standing $0.27 per data store hour, roughly $197 per month per store, plus storage, with FHIR queries beyond 3,500 per hour charged at $0.015 per 10,000 on the Standard tier.