Lesson 41 of 51 · Conformance
Profiles, Extensions, and StructureDefinition
Why base resources need constraining
Base FHIR resources are written to be broad. A Patient resource, for example, has to serve maternity systems, national identity registries, veterinary clinics, and research cohorts alike, so almost every element is optional and most codeable elements bind only loosely to a value set. That generality is deliberate, but it means “a valid Patient” is a weak guarantee: two conformant instances can share almost no populated data. To make resources interoperable for a specific exchange, an implementer narrows what “valid” means for their context.
This is the same problem v2 message profiles solved by constraining v2 messages, and the same idea behind value-set binding from the Terminologies course. FHIR’s answer is profiling, and the machinery is the StructureDefinition resource.
Profiling: tightening, never loosening
A profile is a set of additional constraints applied to a base resource (or data type) for a particular use case. The defining rule is that a profile can only tighten the base, never loosen it — so any instance that conforms to the profile is, by construction, still a valid instance of the underlying base resource 1. A consumer that knows only the base resource can still process the data; a consumer that understands the profile gets stronger guarantees.
The constraints a profile can impose include:
- Cardinality — narrowing the allowed number of occurrences, for example raising a minimum from
0..1to1..1(making an optional element mandatory) or capping a0..*repeat. A profile may never widen cardinality beyond what the base allows. - Must Support — flagging an element so conforming systems are obligated to handle it (see conformance language below).
- Fixed or required values — fixing an element to a constant, or requiring it be present.
- Binding — tying a coded element to a particular value set, typically at a stronger binding strength than the base.
- Slicing — splitting a repeating element into named sub-cases (for instance, separating a list of identifiers into a “medical record number” slice and a “national ID” slice), each with its own constraints.
These constraints can be layered: a profile can itself be derived from another profile, each step only tightening further.
StructureDefinition: the resource behind it all
All of this is expressed by a single resource type, the StructureDefinition 1. The same resource type defines four kinds of structure: the base resources themselves, the data types, profiles (constraints on a base, linked through a baseDefinition element that points at what is being constrained), and extension definitions.
A StructureDefinition carries its content in two complementary views. The differential lists only what this definition changes relative to its base — it is compact and is what authors write. The snapshot is the fully resolved picture, the base merged with every differential applied, so tools can validate an instance without walking the inheritance chain themselves. Both describe the same constraints; they differ only in whether the base is folded in.
Extensions: adding what the base lacks
Sometimes a use case needs data the base resource simply has no element for. FHIR handles this without forking the specification: every element may carry extensions. Crucially, an extension is itself defined by a StructureDefinition, and each extension is identified by a url that gives its meaning 1. A processor that does not recognize the url can ignore the extension; one that does can interpret its value exactly as the definition prescribes.
A profile typically pulls in the extensions it needs and constrains them alongside the base elements, which is how it adds new data in a governed, interoperable way rather than by inventing private fields.
Conformance language
Profiles and the specification state obligations using keywords with precise force: SHALL marks a true requirement, SHOULD a strong recommendation that may be overridden with reason, and MAY a genuinely optional capability. Distinct from these is Must Support: it does not, on its own, require the element to be present in every instance, but it obligates a conforming system to be able to handle the element correctly when it does appear. The exact meaning of “handle” is defined by each implementation guide, but the flag always shifts the burden onto the system rather than the data.
A worked example
Profile: USCorePatient
Base: Patient (via baseDefinition)
Constraints:
Patient.identifier 1..* (was 0..*) Must Support
Patient.name 1..* (was 0..*) Must Support
Patient.gender 1..1 (was 0..1) Must Support
extension[race] 0..1 bound to an
"OMB race categories" value set
Reading this: an instance is only a valid USCorePatient if it has at least one identifier and at least one name, and a conforming system must be able to handle those plus gender and the race extension. Yet because every line only tightens the base, the very same instance is still a perfectly valid Patient 1.
References
- HL7 FHIR Release 4 (R4), v4.0.1. HL7 International. 2019. verified Cited at: profiling.html; structuredefinition.html; extensibility.html.