Section: 13

productshelflife.html

ProductShelfLife

Maturity Level: 3Standards Status:Trial Use

The ProductShelfLife structure defines TODO.

Note: the ProductShelfLife structure allows modifier extensions.

Content

Constraints

ProductShelfLife is used in the following places:

Interpretation of ProductShelfLife in different contexts


profilelist.html

Profiles defined as part of FHIR

FHIR Infrastructure Work GroupMaturity Level: N/AStandards Status: Informative

This specification is a common platform standard that must be adapted to particular use cases. Some particular use cases are common or important enough to be described as a part of the specification itself. These are published as groups of Structure Definitions (profiles or extensions), which are often found in implementation guides, along with Value Sets, newly defined search parameters and examples that are all defined with a common purpose. Additional profiles and extensions may be registered on the HL7 FHIR registry at http://registry.fhir.org


profiling-examples.html

Slicing and Discriminator Examples

Maturity Level: N/AStandards Status:Informative

Slicing Patient Telecom element

One common use of slicing is to describe different constraints on different kinds of patient contact details. In this example, Patient.telecom is defined as: ContactPoint [0..*] where the ContactPoint has system, value and use.

Consider the case where the profile should say:

An example of a patient resource that meets these rules:

<Patient>                         
... snip ...
  <telecom>                       
    <system value="phone" />      
    <use value="home" />          
    <value value="5551234567" />  
  </telecom>
  <telecom>                       
    <system value="email" />      
    <value value="someone@acme.org" />  
  </telecom>
... snip ...
</Patient>

To do this, the profile that implements these rules needs to do the following:

In a StructureDefinition, this will look like:

  <!-- setting up the slicing -->
  <element> 
    <path value="Patient.telecom"/>
    <slicing>
      <discriminator>
        <type value="value"/>
        <path value="system"/>
      </discriminator/>
      <discriminator value="system"/>
        <type value="value"/>
        <path value="use"/>
      </discriminator/>
      <rules value="closed"/>
    </slicing>
    <!-- net cardinality rules -->
    <min value="1"/>
    <max value="3"/>
  </element>
  
  <!-- first slice: home phone -->
  <element> 
    <path value="Patient.telecom"/>
    <name value="HomePhone"/> <!-- mandatory - gives the slice a name -->
    <min value="1"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.system"/>
    <min value="1"/>
    <fixedCode value="phone"/>
  </element>
  <element> 
    <path value="Patient.telecom.value"/>
    <min value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.use"/>
    <min value="1"/>
    <fixedCode value="home"/>
  </element>
  
  <!-- second slice: work phone -->
  <element> 
    <path value="Patient.telecom"/>
    <name value="WorkPhone"/> <!-- mandatory - gives the slice a name -->
    <min value="0"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.system"/>
    <min value="1"/>
    <fixedCode value="phone"/>
  </element>
  <element> 
    <path value="Patient.telecom.value"/>
    <min value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.use"/>
    <min value="1"/>
    <fixedCode value="work"/>
  </element>
  
  <!-- third slice: email -->
  <element> 
    <path value="Patient.telecom"/>
    <name value="Email"/> <!-- mandatory - gives the slice a name -->
    <min value="0"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.system"/>
    <min value="1"/>
    <fixedCode value="email"/>
  </element>
  <element> 
    <path value="Patient.telecom.value"/>
    <min value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.use"/>
    <max value="0"/>
  </element>

Note: much of the definition detail has been left out, and only the parts relevant to the pattern are shown. Also, providing a fixed value makes the minimum cardinality irrelevant, but it is shown here for completeness.

This table illustrates the relationship between the instance and the ElementDefinitions:

Path Name Min Max Fixed
<Patient> Patient
Patient.telecom 1 3
<telecom> Patient.telecom HomePhone 1 1
 <system value="phone" /> Patient.telecom.system 1 1 phone
 <value value="5551234567" /> Patient.telecom.value 1 1
 <use value="home" /> Patient.telecom.use 1 1 home
</telecom>
Patient.telecom WorkPhone 0 1
<telecom> Patient.telecom Email 0 1
 <system value="email" /> Patient.telecom.system 1 1 email
 <value value="someone@acme.org" /> Patient.telecom.value 1 1
 </telecom> Patient.telecom.use

Fixed Order Slicing

A variant to this that looks simpler but turns out to be deceptively complicated is to fix the order and make all slices mandatory. In this case the profile says:

An example of a patient resource that meets these rules:

{
  "resourceType" : "Patient",
  "telecom" : [
    {
      "system" : "phone",
      "value" : "5551234567",
      "use" : "home"
    },
    {
      "system" : "phone",
      "value" : "5551234567",
      "use" : "work"
    },
    {
      "system" : "email",
      "value" : "someone@acme.org"
    }
  ]
}

In a StructureDefinition, this will look like:

  <!-- setting up the slicing -->
  <element> 
    <path value="Patient.telecom"/>
    <slicing>
      <!-- invariant: there SHALL be discriminator or description -->
      <description value="No discriminator needed since offsets are fixed"/>
      <rules value="closed"/>
      <ordered value="true"/>
    </slicing>
    <!-- cardinality rules -->
    <min value="3"/>
    <max value="3"/>
  </element>
  
  <!-- first slice: home phone -->
  <element> 
    <path value="Patient.telecom"/>
    <name value="HomePhone"/> <!-- mandatory - gives the slice a name -->
    <min value="1"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.system"/>
    <min value="1"/>
    <fixedCode value="phone"/>
  </element>
  <element> 
    <path value="Patient.telecom.value"/>
    <min value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.use"/>
    <min value="1"/>
    <fixedCode value="home"/>
  </element>
  
  <!-- second slice: work phone -->
  <element> 
    <path value="Patient.telecom"/>
    <name value="WorkPhone"/> <!-- mandatory - gives the slice a name -->
    <min value="0"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.system"/>
    <min value="1"/>
    <fixedCode value="phone"/>
  </element>
  <element> 
    <path value="Patient.telecom.value"/>
    <min value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.use"/>
    <min value="1"/>
    <fixedCode value="work"/>
  </element>
  
  <!-- third slice: email -->
  <element> 
    <path value="Patient.telecom"/>
    <name value="Email"/> <!-- mandatory - gives the slice a name -->
    <min value="0"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.system"/>
    <min value="1"/>
    <fixedCode value="email"/>
  </element>
  <element> 
    <path value="Patient.telecom.value"/>
    <min value="1"/>
  </element>
  <element> 
    <path value="Patient.telecom.use"/>
    <max value="0"/>
  </element>

A simple content model like this is attractive because it's both easier to specify and to work with. There's no need to think about the values of system and use - implementers can just grab first telecom value etc.

The problem with this approach is that such simple requirements are rarely valid in production healthcare systems, and even if they are valid during implementation, they rarely stay that way. Implementing multiple applications based on simple offsets will make the overall eco-system fragile against change, either internally, or as the scope of the eco-system grows.

Since the discriminator could be system+use in this case, it's best for future compatibility to specify it anyway.

Blood Pressure Example

Another use of Slicing is for Blood Pressure Measurements, where the profile says:

An example of an observation resource that conforms such constrained StructureDefinitions looks like this:

<Observation>                         
  ... 
  <component>                       
    <code>                       
      <coding>                       
        <system value="http://loinc.org" />      
        <code value="8480-6" />  
        <display value="Systolic blood pressure" />  
      </coding>                       
    </code>                       
    <valueQuantity ... />                       
  </component>
  <component>                       
    <code>                       
      <coding>                       
        <system value="http://loinc.org" />      
        <code value="8462-4" />  
        <display value="Diastolic blood pressure" />  
      </coding>                       
    </code>                       
    <valueQuantity .../>                       
  </component>
</Patient>

To do this, the profile that implements these rules needs to do the following:

In a StructureDefinition, this will look like:

  <!-- setting up the slicing -->
  <element> 
    <path value="Observation.component"/>
    <slicing>
      <discriminator value="system"/>
        <type value="value"/>
        <path value="code"/>
      </discriminator/>
    </slicing>
    <!-- net cardinality rules -->
    <min value="2"/>
    <max value="*"/>
  </element>
  
  <!-- first slice: systolic -->
  <element> 
    <path value="Observation.component"/>
    <name value="systolic"/> <!-- mandatory - gives the slice a name -->
    <min value="1"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Observation.component.code"/>
    <min value="1"/>
    <fixedCodeableConcept>
      <coding>                       
        <system value="http://loinc.org" />      
        <code value="8480-6" />  
        <display value="Systolic blood pressure" />  
      </coding>                       
    </fixedCodeableConcept>
  </element>
  <element> 
    <path value="Observation.component.valueQuantity"/>
    <min value="1"/>
  </element>
  
  <!-- second slice: diastolic -->
  <element> 
    <path value="Observation.component"/>
    <name value="diastolic"/> <!-- mandatory - gives the slice a name -->
    <min value="1"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Observation.component.code"/>
    <min value="1"/>
    <fixedCodeableConcept>
      <coding>                       
        <system value="http://loinc.org" />      
        <code value="8462-4" />  
        <display value="Diastolic blood pressure" />  
      </coding>                       
    </fixedCodeableConcept>
  </element>
  <element> 
    <path value="Observation.component.valueQuantity"/>
    <min value="1"/>
  </element>

Note: much of the definition detail has been left out, and only the parts relevant to the pattern are shown. E.g. A real blood pressure profile would fix unit, an overall Observation code etc.

Extensions

For another example, consider slicing extensions. The base extension on every element is defined as a list (0..*) of extensions, and each extension has a url that identifies it, and a value. Consider an example where a profile defines that for a particular element (named Patient), there are two extensions, with URLs http://acme.com/a and http://acme.com/b. In addition, the profile allows other extensions to be used.

Technically, the profile achieves this by "slicing" the extension list, into two slices, and saying that the slicing is "open" - that there can be other slices introduced. Here are the relevant parts of the Profile on patient:


<StructureDefinition xmlns="http://hl7.org/fhir">
  <!-- snip -->
    <baseType value="Patient" />
    <baseDefinition value="http://hl7.org.fhir/StructureDefinition/Patient" />
    <derivation value="constraint" />
    <snapshot>
      <element>
        <path value="Patient"/>
        <!-- snip -->
      </element>
      <element>
        <path value="Patient.extension"/>
        <!-- this first element defines the slicing, and carries the base definition forward -->
        <slicing>
          <!-- Extensions are always discriminated by URL -->
          <discriminator value="system"/>
            <type value="value"/>
            <path value="url"/>
          </discriminator/>
          <ordered value="false"/>     <!-- we don't care what order they appear in -->
          <rules value="open"/>        <!-- other extensions can be used -->
        </slicing>
        <!-- -- snip definition -->
      </element>
      <!-- first extension -->
      <element>
        <path value="Patient.extension"/>
        <!-- snip most of definition -->
        <type>
         <code value="Extension"/>
         <!-- the profile for an extension is a reference to the extension definition itself -
           this implies a profile, and happens to fix the @url value to the desired URL -->
         <profile value="http://acme.com/a"/>
        </type>
      </element>
      <!-- second extension -->
      <element>
        <path value="Patient.extension"/>
        <!-- snip most of definition -->
        <type>
         <code value="Extension"/>
         <!-- the profile for an extension is a reference to the extension definition itself -
           this implies a profile, and happens to fix the @url value to the desired URL -->
         <profile value="http://acme.com/b"/>
        </type>
      </element>
      <!-- snip rest of profile -->
    </snapshot>
</StructureDefinition>

Here is a patient example that conforms to this profile:

<Patient xmlns="http://hl7.org/fhir">
  <!-- two extensions, the order doesn't matter -->
  <extension url="http://acme.com/b">
    <!-- this has the right url, and so matches the second slice -->
    <!-- snip whatever value extension would have -->
  </extension>
  <extension url="http://acme.com/a">
    <!-- this has the right url, and so matches the first slice -->
    <!-- snip whatever value extension would have -->
  </extension>
  <!-- the rest of patient -->
</Patient>

Diagnostic Report & Observation

In this example, a profile on a diagnostic report says that it must have four observations, each with a different LOINC code (e.g. a classic lab panel). In this case (taken from the Example Lipid Profile), the structure that applies to DiagnosticReport will say that there are four slices on DiagnosticReport.result, each conforming to a different structure, which are also contained in the same profile. Each of those structures will constrain the LOINC code in the observation.

<!-- first structure, the DiagnosticReport -->
<StructureDefinition xmlns="http://hl7.org/fhir">
  <!-- snip -->
  <url value="http://acme.org/fhir/StructureDefinition/lipid-report"/>
  <name value="LipidProfile"/>
  <baseType value="DiagnosticReport"/>
  <baseDefinition value="http://hl7.org/fhir/StructureDefinition/DiagnosticReport"/>
  <derivation value="constraint" />

  <!-- snip -->
  <snapshot>
    <!-- snip elements -->
    <element>
      <!-- first definition for result -->
      <path value="DiagnosticReport.result"/>
      <slicing>
        <!-- this is sliced by the code value of the target of the reference -->
        <discriminator value="system"/>
          <type value="value"/>
          <path value="reference.code"/>
        </discriminator/>
        <!-- have to be in the specified order -->
        <ordered value="true"/>
        <!-- this profile says, no other observations allowed -->
        <rules value="closed"/>
      </slicing>
      <!-- snip definition -->
    </element>
    <!-- first slice: Cholesterol -->
    <element>
      <path value="DiagnosticReport.result"/>
      <name value="Cholesterol"/>
      <!-- snip definition parts -->
      <type>
        <code value="Reference"/>
        <!-- this element must conform to the "Cholesterol" structure -->
        <profile value="http://acme.org/fhir/StructureDefinition/Cholesterol"/>
      </type>
    </element>
    <!-- next 3 slices all the same, but different names for profile -->
    <element>
      <path value="DiagnosticReport.result"/>
      <name value="Triglyceride"/>
      <!-- snip definition parts -->
      <type>
        <code value="Reference"/>
        <!-- this element must conform to the "Triglyceride" structure -->
        <profile value="http://acme.org/fhir/StructureDefinition/Triglyceride"/>
      </type>
    </element>
    <element>
      <path value="DiagnosticReport.result"/>
      <name value="LDLCholesterol"/>
      <!-- snip definition parts -->
      <type>
        <code value="Reference"/>
        <!-- this element must conform to the "LDLCholesterol" structure -->
        <profile value="http://acme.org/fhir/StructureDefinition/LDLCholesterol"/>
      </type>
    </element>
    <element>
      <path value="DiagnosticReport.result"/>
      <name value="HDLCholesterol"/>
      <!-- snip definition parts -->
      <type>
        <code value="Reference"/>
        <!-- this element must conform to the "HDLCholesterol" structure -->
        <profile value="http://acme.org/fhir/StructureDefinition/HDLCholesterol"/>
      </type>
    </element>
    <!-- snip elements -->
  </snapshot>
</StructureDefinition>

<!-- now, the second structure, for the Cholesterol profile -->
<StructureDefinition>
  <url value="http://acme.org/fhir/StructureDefinition/Cholesterol"/>
  <name value="Cholesterol"/> 
  <baseType value="Observation"/>
  <baseDefinition value="http://hl7.org/fhir/StructureDefinition/Observation"/>
  <derivation value="constraint" />
  <snapshot>
    <!-- snip elements -->
    <element>
      <!-- this the element definition for name. Because of the
        slicing / discriminator rules in the LipidReport profile
        that references it, it is required to fix the value of
        the name element -->
      <path value="Observation.code"/>
      <definition>
         <!-- there are actually 3 ways to fix a CodeableConcept
         to a single fixed value. Here, we used the simplest one -->
         <valueCodeableConcept>
           <!-- just fix the value to the right code -->
           <coding>
             <system value="http://loinc.org"/>
             <code value="35200-5"/>
             <display value="Cholesterol"/>
           </coding>
         </valueCodeableConcept>
      </definition>
    </element>
    <!-- snip elements -->
  </snapshot>
</StructureDefinition>

<!-- Triglyceride profile -->
<StructureDefinition>
  <url value="http://acme.org/fhir/StructureDefinition/Triglyceride"/>
  <name value="Triglyceride"/> 
  <baseType value="Observation"/>
  <baseDefinition value="http://hl7.org/fhir/StructureDefinition/Observation"/>
  <derivation value="constraint" />
  <snapshot>
    <!-- snip elements -->
    <element>
      <!-- this the element definition for name. Because of the
        slicing / discriminator rules in the LipidReport profile
        that references it, it is required to fix the value of
        the name element -->
      <path value="Observation.code"/>
      <definition>
         <!-- there's actually 3 ways to fix a CodeableConcept
         to a single fixed value. Here, we used the simplest one -->
         <valueCodeableConcept>
           <!-- just fix the value to the right code -->
           <coding>
             <system value="http://loinc.org"/>
             <code value="35217-9"/>
             <display value="Triglyceride"/>
           </coding>
         </valueCodeableConcept>
      </definition>
    </element>
    <!-- snip elements -->
  </snapshot>
</StructureDefinition>

<!-- LDLCholesterol profile -->
<StructureDefinition>
  <url value="http://acme.org/fhir/StructureDefinition/LDLCholesterol"/>
  <name value="LDLCholesterol"/> 
  <baseType value="Observation"/>
  <baseDefinition value="http://hl7.org/fhir/StructureDefinition/Observation"/>
  <derivation value="constraint" />
  <snapshot>
    <!-- snip elements -->
    <element>
      <!-- this the element definition for name. Because of the
        slicing / discriminator rules in the LipidReport profile
        that references it, it is required to fix the value of
        the name element -->
      <path value="Observation.code"/>
      <definition>
         <!-- because of the way that LDL cholesterol measurements works
           (well, in this context- it varies), there's 2 different LOINC
           codes for either measured or calculated. So here, we bind to
           a value set -->
       <binding>
        <conformance value="required"/> <!-- must be required if this is a discriminator -->
        <!-- snip the actual value set reference, but it refers to a value
             set with two LOINC codes, one for each kind of LDL, which in
             this case are LOINC codes 18262-6 and 13457-7 -->
       </binding>
     </definition>
    </element>
    <!-- snip elements -->
  </snapshot>
</StructureDefinition>

<!-- HDLCholesterol profile -->
<StructureDefinition>
  <url value="http://acme.org/fhir/StructureDefinition/HDLCholesterol"/>
  <name value="HDLCholesterol"/> 
  <baseType value="Observation"/>
  <baseDefinition value="http://hl7.org/fhir/StructureDefinition/Observation"/>
  <derivation value="constraint" />
  <snapshot>
    <!-- snip elements -->
    <element>
      <!-- this the element definition for name. Because of the
        slicing / discriminator rules in the LipidReport profile
        that references it, it is required to fix the value of
        the name element -->
      <path value="Observation.code"/>
      <definition>
         <!-- there's actually 3 ways to fix a CodeableConcept
         to a single fixed value. Here, we used the simplest one -->
         <valueCodeableConcept>
           <!-- just fix the value to the right code -->
           <coding>
             <system value="http://loinc.org"/>
             <code value="2085-9"/>
             <display value="LDL Cholesterol"/>
           </coding>
         </valueCodeableConcept>
      </definition>
    </element>
    <!-- snip elements -->
  </snapshot>
</StructureDefinition>

Here is an instance that meets the rules for this profile:

<!-- first, the diagnostic report -->
<DiagnosticReport xmlns="http://hl7.org/fhir">
  <!-- snip -->
  <!-- here's the set of results. We don't know what
    slices they are or anything until we go off, find
    the references, and look in them -->
  <result>
    <reference value="Observation/cholesterol"/>
  </result>
  <result>
    <reference value="Observation/triglyceride"/>
  </result>
  <result>
    <reference value="Observation/ldlcholesterol"/>
  </result>
  <result>
    <reference value="Observation/hdlcholesterol"/>
  </result>
  <!-- snip -->
</DiagnosticReport>

<!-- Observation, id = cholesterol -->
<Observation xmlns="http://hl7.org/fhir">
  <!-- the observation starts with the name, as specified
    by the profile for the first slice  -->
  <code>
    <coding>
      <system value="http://loinc.org"/>
      <code value="35200-5"/>
      <display value="Cholesterol"/>
    </coding>
  </code>
  <!-- snip -->
</Observation>

<!-- Observation, id = triglyceride -->
<!-- this code matches the second slice. good -->
<Observation xmlns="http://hl7.org/fhir">
  <code>
    <coding>
      <system value="http://loinc.org"/>
      <code value="35217-9"/>
      <display value="Triglyceride"/>
    </coding>
  </code>
  <!-- snip -->
</Observation>

<!-- Observation, id = hdlcholesterol -->
<!-- this code matches the fourth slice. good -->
<Observation xmlns="http://hl7.org/fhir">
  <code>
    <coding>
      <system value="http://loinc.org"/>
      <code value="2085-9"/>
      <display value="HDL Cholesterol"/>
    </coding>
  </code>
  <!-- snip -->
</Observation>

<!-- Observation, id = ldlcholesterol -->
<!-- this code matches the third slice. good -->
<Observation id="ldlcholesterol">
  <code>
    <coding>
      <system value="http://loinc.org"/>
      <code value="13457-7"/>
      <display value="LDL Chol. (Calc)"/>
    </coding>
  </code>
  <!-- snip -->
</Observation>

Note that this version is not valid, because the slices are not in the correct order:

<!-- first, the diagnostic report -->
<DiagnosticReport xmlns="http://hl7.org/fhir">
  <!-- snip -->
  <!-- here's the set of results. We don't know what
    slices they are or anything until we go off, find
    the references, and look in them -->
  <result>
    <reference value="Observation/cholesterol"/>
  </result>
  <result>
    <reference value="Observation/triglyceride"/>
  </result>
    <!-- Oops the last two are out of order -->
  <result>
    <reference value="Observation/hdlcholesterol"/>
  </result>
  <result>
    <reference value="Observation/ldlcholesterol"/>
  </result>
  <!-- snip -->
</DiagnosticReport>

Composition Sections

Most uses of Composition involve conformance to a profile that specifies which sections will exist, and what their contents will be. This is yet another example of slicing. A typical document content profile might specify a section structure something like this:

Real profiles will contain lots of detail about the sections, but these are omitted here in the interests of clarity.

An example of a Composition that meets these rules:

<Composition>                         
... snip ...
  <section>                       
    <code>                       
      <coding>                       
        <system value="http://loinc.org" />      
        <use value="29299-5" />          
        <value value="Reason for visit Narrative" />  
      </coding>                       
    </code>                       
    ... snip ...
  </section>
  <section>                       
    <code>                       
      <coding>                       
        <system value="http://loinc.org" />      
        <use value="46057-6" />          
        <value value="Medications section" />  
      </coding>                       
    </code>                       
    ... snip ...
    <section>                       
      <code>                       
        <coding>                       
          <system value="http://loinc.org" />      
          <use value="66149-6" />          
          <value value="Prescribed medications" />  
        </coding>                       
      </code>                       
      ... snip ...
    </section>
    <section>                       
      <code>                       
        <coding>                       
          <system value="http://loinc.org" />      
          <use value="66150-4" />          
          <value value="O medications" />  
        </coding>                       
      </code>                       
      ... snip ...
    </section>
  </section>
  <section>                       
    <code>                       
      <coding>                       
        <system value="http://loinc.org" />      
        <use value="8716-3" />          
        <value value="Vital signs" />  
      </coding>                       
    </code>                       
    ... snip ...
  </section>
</Composition>

To do this, the profile that implements these rules needs to do the following:

In a StructureDefinition, this will look like:

  <!-- setting up the slicing on Composition.section -->
  <element> 
    <path value="Composition.section"/>
    <slicing>
      <discriminator value="system"/>
        <type value="value"/>
        <path value="code"/>
      </discriminator/>
      <ordered value="true"/>
      <rules value="closed"/>
    </slicing>
    <!-- net cardinality rules -->
    <min value="3"/>
    <max value="3"/>
  </element>
  
  <!-- first slice: reason for visit -->
  <element> 
    <path value="Composition.section"/>
    <name value="reason-for-visit"/> 
    <min value="1"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Composition.section.code"/>
    <min value="1"/>
    <fixedCodeableConcept>
      <coding>                       
        <system value="http://loinc.org" />      
        <use value="29299-5" />          
        <value value="Reason for visit Narrative" />  
      </coding>                       
    </fixedCodeableConcept>
  </element>
  
  <!-- second slice: medications -->
  <element> 
    <path value="Composition.section"/>
    <name value="medications"/> 
    <min value="1"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Composition.section.code"/>
    <min value="1"/>
    <fixedCodeableConcept>
      <coding>                       
        <system value="http://loinc.org" />      
        <use value="46057-6" />          
        <value value="Medications section" />  
      </coding>                       
    </fixedCodeableConcept>
  </element>

  <!-- setting up the inner slicing on medication Composition.section.section -->
  <element> 
    <path value="Composition.section.section"/>
    <slicing>
      <discriminator value="system"/>
        <type value="value"/>
        <path value="code"/>
      </discriminator/>
      <ordered value="true"/>
      <rules value="closed"/>
    </slicing>
    <!-- net cardinality rules -->
    <min value="1"/>
    <max value="2"/>
  </element>

  <!-- first inner slice: prescribed medications -->
  <element> 
    <path value="Composition.section.section"/>
    <name value="prescribed"/> 
    <min value="1"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Composition.section.section.code"/>
    <min value="1"/>
    <fixedCodeableConcept>
      <coding>                       
        <system value="http://loinc.org" />      
        <use value="66149-6" />          
        <value value="Prescribed medications" />  
      </coding>                       
    </fixedCodeableConcept>
  </element>
  
  <!-- second inner slice: over the counter medications -->
  <element> 
    <path value="Composition.section.section"/>
    <name value="otc"/> 
    <min value="0"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Composition.section.section.code"/>
    <min value="1"/>
    <fixedCodeableConcept>
      <coding>                       
        <system value="http://loinc.org" />      
        <use value="66150-4" />          
        <value value="Over the counter medications" />  
      </coding>                       
    </fixedCodeableConcept>
  </element>
  
  <!-- third slice: Vital Signs -->
  <element> 
    <path value="Composition.section"/>
    <name value="vital-signs"/> 
    <min value="1"/>
    <max value="1"/>
  </element>
  <element> 
    <path value="Composition.section.code"/>
    <min value="1"/>
    <fixedCodeableConcept>
      <coding>                       
        <system value="http://loinc.org" />      
        <use value="8716-3" />          
        <value value="Vital signs" />  
      </coding>                       
    </fixedCodeableConcept>
  </element>

Reslicing

For some use cases, it is necessary to further reslice in a derivative profile. This is when Profile A derives from Profile B, Profile A slices an element in Slice A and B, and Profile B further slices Slice A into A1 and A2.

As an example, assume that institution implementation guide specifies that a medication list is represented by a List resource, where the items into the list are split up into 3 slices, based on resource type:

These slices must come in order.

Now, a particular application creates an additional profile that specifies the following rules in addition to the base rules of the institution:

  1. the medicationRequests will be sliced into active and non-active, where all the active requests will come first, and then the inactive ones
  2. only active MedicationAdministrations will be encountered
  3. Medication statements are not supported

An example of a List resource that meets these rules:

<List>                         
... snip ...
  <entry>                       
    <item>  
      <reference value="MedicationRequest/ex-active-1"/>  
    </item>  
  </entry>
  <entry>                       
    <item>  
      <reference value="MedicationRequest/ex-active-2"/>  
    </item>  
  </entry>
  <entry>                       
    <item>  
      <reference value="MedicationRequest/ex-inactive-1"/>  
    </item>  
  </entry>
  <entry>                       
    <item>  
      <reference value="MedicationAdministration/ex-any-1"/>  
    </item>  
  </entry>

  ... snip ...
</List>

The StructureDefinition differential for the first profile sets up the slicing, and then defines 3 slices:

  <!-- setting up the slicing -->
  <element> 
    <path value="List.entry"/>
    <slicing>
      <discriminator>
        <type value="profile"/>
        <path value="entry.item.resolve()"/>
      </discriminator>
      <ordered value="true"/>
      <rules value="closed"/>
    </slicing>
  </element>
  
  <!-- first slice: medrequest -->
  <element> 
    <path value="List.entry"/>
    <name value="medrequest"/> <!-- mandatory - gives the slice a name -->
  </element>
  <element> 
    <path value="List.entry.item"/>
    <type>
      <code value="Reference"/>
      <targetProfile value="http://example.org/StructureDefinition/medrequest"/>
    </type>
  </element>
  
  <!-- second slice: medadmin -->
  <element> 
    <path value="List.entry"/>
    <name value="medadmin"/> <!-- mandatory - gives the slice a name -->
  </element>
  <element> 
    <path value="List.entry.item"/>
    <type>
      <code value="Reference"/>
      <targetProfile value="http://example.org/StructureDefinition/medadmin"/>
    </type>
  </element>
  
  <!-- third slice: medstmt -->
  <element> 
    <path value="List.entry"/>
    <name value="medstmt"/> <!-- mandatory - gives the slice a name -->
  </element>
  <element> 
    <path value="List.entry.item"/>
    <type>
      <code value="Reference"/>
      <targetProfile value="http://example.org/StructureDefinition/medstmt"/>
    </type>
  </element>

The derived StructureDefinition differential reslices the first slice into 2 slices, constrains the second slice, and prohibits the 3rd:

  <!-- setting up the slicing - this profile doesn't change the slicing 
    (though it could add new discriminators if it needed) -->
  <element> 
    <path value="List.entry"/>
    <slicing>
      <discriminator>
        <type value="profile"/>
        <path value="entry.item.resolve()"/>
      </discriminator>
      <ordered value="true"/>
      <rules value="closed"/>
    </slicing>
  </element>
  
  <!-- first slice: medrequest/active -->
  <element> 
    <path value="List.entry"/>
    <!-- name/sub-name: splitting an existing slice into new slices  -->
    <name value="medrequest/active"/> 
  </element>
  <element> 
    <path value="List.entry.item"/>
    <type>
      <code value="Reference"/>
      <targetProfile value="http://example.org/StructureDefinition/medrequest-active"/>
    </type>
  </element>
  
  <!-- second slice: medrequest/inactive -->
  <element> 
    <path value="List.entry"/>
    <name value="medrequest/inactive"/>
  </element>
  <element> 
    <path value="List.entry.item"/>
    <type>
      <code value="Reference"/>
      <targetProfile value="http://example.org/StructureDefinition/medrequest-active"/>
    </type>
  </element>
  
  <!-- third slice: medadmin (rule on the second slice in the parent profile) -->
  <element> 
    <path value="List.entry"/>
    <!-- use the same name - constraining the existing slice -->
    <name value="medadmin"/> 
  </element>
  <element> 
    <path value="List.entry.item"/>
    <type>
      <code value="Reference"/>
      <targetProfile value="http://example.org/StructureDefinition/medadmin-active"/>
    </type>
  </element>
  
  <!-- fourth slice: medstmt -->
  <element> 
    <path value="List.entry"/>
    <!-- use the same name - constraining the existing slice -->
    <name value="medstmt"/> 
    <max value="0"/>
  </element>

profiling.html

Profiling FHIR

Maturity Level: NormativeStandards Status:Normative

The base FHIR specification (this specification) describes a set of base resources, frameworks and APIs that are used in many different contexts in healthcare. However, there is wide variability between jurisdictions and across the healthcare ecosystem around practices, requirements, regulations, education and what actions are feasible and/or beneficial.

For this reason, the FHIR specification is a "platform specification" - it creates a common platform or foundation on which a variety of different solutions are implemented. As a consequence, this specification usually requires further adaptation to particular contexts of use. Typically, these adaptations specify:

Note that because of the nature of the healthcare ecosystem, there may be multiple overlapping sets of adaptations - by healthcare domain, by country, by institution, and/or by vendor/implementation.

Glossary

FHIR defines a cascade of artifacts for this purpose:

Artifact Description US Core example
Implementation Guide (IG) A coherent and bounded set of adaptations that are published as a single unit. Validation occurs within the context of the Implementation Guide US Core IG
Package A group of related adaptations that are published as a group within an Implementation Guide US Core Capability Statements
Conformance Resource A single resource in a package that makes rules about how an implementation works. These are described below DAF Problem Value Set
Profile A set of constraints on a resource represented as a structure definition with kind = constraint DAF Medication Request

The verb 'profile', or 'profiling', is used to describe the process of creating a profile.

Conformance Resources

Typically, Implementation Guides both restrict and extend APIs, resources and terminologies. FHIR provides a set of resources that can be used to represent and share the decisions that have been made, and allows implementers to build useful services from them. These resources are known as the conformance resources. These conformance resources allow implementers to:

These resources need to be used as discussed below, and also following the basic concepts for extension that are described in "Extensibility". For implementer convenience, the specification itself publishes its base definitions using these same resources.

Two uses of Profiles

The CapabilityStatement resource describes two different uses for profiles on resources: Resource Profiles and Supported Profiles. Resource Profiles are specified using the CapabilityStatement.rest.resource.profile element and Supported Profiles are specified using the CapabilityStatement.rest.resource.supportedProfile element.

CapabilityStatement.rest.resource.profile

These profiles describe the general features that are supported by the system for each kind of resource. Typically, this is the superset of all the different use-cases implemented by the system. This is a resource-level perspective of a system's functionality.

CapabilityStatement.rest.resource.supportedProfile

These profiles describe the information handled/produced by the system on a per use case basis. Some examples of the uses for these kind of profiles:

These profiles represent different use cases leading to handling resources of the type indicated by the CapabilityStatement.rest.resource.type differently. For instance:

For a producer system and a consumer system to exchange data successfully based on one of these supported profiles, it is not enough to know that the systems happen to have profiles that overlap for the use case of interest; the consumer must be able to filter the total set of resources made available by the producer system and deal only with the ones relevant to the use case.

As an example, consider a laboratory system generating thousands of reports a day. 1% of those reports are a particular endocrine report that a decision support system knows how to process. Both systems declare that they support the particular endocrine report profile, but how does the decision support system actually find the endocrine reports that it knows how to process?

One possible option is for the decision support system to receive every single report coming from the lab system, check whether it conforms to the profile or not, and then decide whether to process it. Checking whether a resource conforms to a particular profile or not is a straight forward operation (one option is to use the provided tools for this), but this is a very inefficient way - the decision support system has to receive and process 100 times as many resources as it uses. To help a consumer find the correct set of reports for a use-case, a producer of resources also SHALL, for any profile declared in CapabilityStatement.rest.resource.supportedProfile:

  1. Mark resources with profile assertions documenting the profile(s) they conform to (this enables indexing by the profile)
  2. (if a server) support searching by the _profile parameter for the declared profiles

Beyond these requirements, a producer of resources SHOULD ensure that any resource instance that would reasonably be expected to conform to the declared profiles SHOULD be published in this form.

There are many uninvestigated issues associated with this use of profiles. HL7 is actively seeking feedback from users who experiment in this area, and users should be prepared for changes to features and obligations in this area in the future.

Feedback is welcome here.

Extending and Restricting the API

A CapabilityStatement resource lists the REST interactions (read, update, search, etc.) that a server provides or that a client uses, along with some supporting information for each. It can also be used to define a set of desired behaviors (e.g. as part of a specification or a Request for Proposal). The only interaction that servers are required to support is the capabilities interaction itself - to retrieve the server's CapabilityStatement. Beyond that, servers and clients support and use whichever API calls are relevant to their use case.

In addition to the operations that FHIR provides, servers may provide additional operations that are not part of the FHIR specification. Implementers can safely do this by appending a custom operation name prefixed with '$' to an existing FHIR URL, as the Operations framework does. The Conformance resource supports defining what OperationDefinitions make use of particular names on an end-point. If services are defined that are not declared using OperationDefinition, it may be appropriate to use longer names, reducing the chance of collision (and confusion) with services declared by other interfaces. The base specification will never define operation names with a "." (period) in them, so implementers are recommended to use some appropriate prefix in their names (such as "ihe.someService") to reduce the likelihood of name conflicts.

Implementations are encouraged, but not required, to define operations using the standard FHIR operations framework - that is, to declare the operations using the OperationDefinition resource, but some operations may involve formats that can't be described that way.

Implementations are also able to extend the FHIR API using additional content types. For instance, it might be useful to read or update the appointment resources using a vCard based format. vCard defines its own mime type, and these additional mime types can safely be used in addition to those defined in this specification.

Extending and Restricting Resources

Extending and restricting resources (collectively known as 'profiling a resource') is done with a "StructureDefinition" resource, which is a statement of rules about how the elements in a resource are used, and where extensions are used in a resource.

Changing Cardinality

One key function of profiles is to change the cardinality of an element. A profile can restrict the cardinality of an element within the limits of the base structure it is constraining. This table summarizes what types of restrictions are allowed:

derived (across)
base (down)
0..0
(Not used)
0..1
(optional)
0..n
(optional, many)
1..1
(required)
1..n
(at least 1)
0..1 yes yes no yes no
0..* yes yes yes yes yes
1..1 no no no yes no
1..* no no no yes yes

When a profile is constraining another profile where there are more cardinality options (e.g. low is not just 0 or 1, and high is not just 1 or *), the same principles still apply: the constraining profile can only allow what the base profile allows.

Note that though a profile can constrain an element from x..* to x..1, this doesn't make any difference to the representation in the JSON format - the element will still be represented in an array. As an example, take Patient.name which has a cardinality of 0..*. In an unprofiled Patient, this will be represented as:

{
  "resourceType" : "Patient",
	"name" : [{
	   "text" : "Peter James"
	}]
}

Even if a profile is created on the resource that narrows tha cardinality to 1..1, applications will still process the resource without knowledge of the profile. For this reason the representation will still be the same.

Limitations of Use

What StructureDefinitions can do when they are constraining existing resources and datatypes is limited in some respects:

The consequence of this is that if a profile mandates extended behavior that cannot be ignored, it must also mandate the use of a modifier extension. Another way of saying this is that knowledge must be explicit in the instance, not implicit in the profile.

As an example, if a profile wished to describe that a Procedure resource was being negated (e.g. asserting that it never happened), it could not simply say in the profile itself that this is what the resource means; instead, the profile must say that the resource must have an extension that represents this knowledge.

There is a facility to mark resources to indicate that they can only be safely understood by a process that is aware of and understands a set of published rules. For more information, see Restricted Understanding of Resources.

Using StructureDefinitions

A "constraint" StructureDefinition specifies a set of restrictions on the content of a FHIR resource or data type, or an additional set of constraints on an existing profile. A given structure definition is identified by its canonical URL, which SHOULD be the URL at which it is published. The following kinds of statements can be made about how an element is used, using a series of Element Definitions:

Any changed definitions SHALL be restrictions that are consistent with the rules defined in the resource in the FHIR Specification from which the profile is derived. Note that some of these restrictions can be enforced by tooling (and are by the FHIR tooling), but others (e.g. alignment of changes to descriptive text) cannot be automatically enforced.

Note that structure definitions cannot 'remove' mappings and constraints that are defined in the base structure, but for purposes of clarity, they can refrain from repeating them.

A structure definition contains a linear list of element definitions. The inherent nested structure of the elements is derived from the path value of each element. For instance, a sequence of the element paths like this:

defines the following structure:

 <Root>
   <childA>
     <grandChild1/>
   </childA>
   <childB/>
 </Root>

or its JSON equivalent. The structure is coherent - children are never implied, and the path statements are always in order. The element list is a linear list rather than being explicitly nested because element definitions are frequently re-used in multiple places within a single definition, and this re-use is easier with a flat structure.

Differential vs Snapshot

StructureDefinitions may contain a differential statement, a snapshot statement or both.

Differential statements describe only the differences that they make relative to the structure definition the constrain (which is most often the base FHIR resource or data type). For example, a profile may make a single element mandatory (cardinality 1..1). In the example of a differential structure, it will contain a single element with the path of the element being made mandatory, and a cardinality statement. Nothing else is stated - all the rest of the structural information is implied (note that this means that a differential profile can be sparse and only mention the elements that are changed, without having to list the full structure. This rule includes the root element - it is not needed in a sparse differential).

Note that a differential can choose not to constrain elements. Doing so means that the profile will be more flexible in terms of compatibility with other profiles, but will require more work to support from implementing systems. Alternatively, a profile can constrain all optional elements to be not present (max cardinality = 0) - this closes the content, which makes implementation easier, but also reduces its usefulness.

In order to properly understand a differential structure, it must be applied to the structure definition on which it is based. In order to save tools from needing to support this operation (which is computationally intensive - and impossible if the base structure is not available), a StructureDefinition can also carry a "snapshot" - a fully calculated form of the structure that is not dependent on any other structure. The FHIR project provides tools for the common platforms that can populate a snapshot from a differential (note that the tools generate complete verbose snapshots; they do not support suppressing mappings or constraints).

StructureDefinitions can contain both a differential and a snapshot view. In fact, this is the most useful form - the differential form serves the authoring process, while the snapshot serves the implementation tooling. StructureDefinition resources used in operational systems should always have the snapshot view populated.

Slicing

One common feature of constraining StructureDefinitions is to take an element that may occur more than once (e.g. in a list), and then split the list into a series of sublists, each with different restrictions on the elements in the sublist with associated additional meaning. In FHIR, this operation is known as "Slicing" a list. It is common to "slice" a list into sub-lists with each containing just one element, effectively putting constraints on each element in the list.

Here is an example to illustrate the process:

Slicing diagram

In this example, the base structure definition for the resource Observation defines the "component" element which contains a nested code and a value for observations that have multiple values. A classic example of this kind of observation is a blood pressure measurement - it contains 2 values, one for systolic, and one for diastolic (example).

This diagram shows the conceptual process of 'slicing' the component list into systolic and diastolic slices (note that to avoid clutter, the "name" attribute of Observation is shown as just a code not a full CodeableConcept).

The structure definition for Blood Pressure splits the component list into two sublists of one element each: a systolic element, and a diastolic element. Each of these elements has a fixed value for the code element (a fixed LOINC code for the name), and both have a value of type Quantity. This process is known as "slicing" and the Systolic and Diastolic elements are called "slices".

Note that when the resource is exchanged, the serialization format that is exchanged is not altered by the constraining definition. This means that the item profile names defined in the structure definition ("systolic", etc. in this example) are never exchanged. A resource instance looks like this:

 <Observation>
   ...
   <component>
     <code {LOINC="8480-6"}/>
     <value ...>
   </component>
   <component>
     <code {LOINC="8462-4"}/>
     <value ...>
   </component>
 </Observation>

In order to determine that the first related item corresponds to "Systolic" in the structure definition, so that it can then determine to which additional constraints for a sub-list the item conforms, the system checks the values of the elements. In this case, the "code" element in the target resource can be used to determine which slice that target refers to. This element is called the "discriminator".

Discriminator

In the general case, systems processing resources using a structure definition that slices a list can determine the slice corresponding to an item in the list by checking whether the item's content meets the rules specified for the slice. This would require a processor to be able to check all the rules applied in the slice and to do so speculatively in a depth-first fashion. Both of these requirements are inappropriately difficult for an operational system, and particularly for generated code (e.g. software that is automatically produced based on the StructureDefinition). Thus, to provide a better way to distinguish slices, a sliced element can designate a field or set of fields that act as a "discriminator" used to tell the slices apart.

When a discriminator is provided, the composite of the values of the elements designated in the discriminator is unique and distinct for each possible slice and applications can easily determine which slice an item in a list is. The intention is that this can be done in generated code, e.g. using a switch/case statement.

When a constraining structure designates one or more discriminators, it SHALL ensure that the possible values for each slice are different and non-overlapping, so that the slices can easily be distinguished.

Each discriminator is a pair of values: a type that indicates how the field is processed when evaluating the discriminator, and a FHIRPath expression that identifies the element in which the discriminator is found. There are five different processing types for discriminators:

The FHIRPath statement that allows for the selection of the element on which the discriminator is based is a restricted FHIRPath statement that is allowed to include:

Further notes about the use of the different discriminator types:

value This is the most commonly used discriminator type: to decide based on the value of an element. Elements used like this are mostly primitive types- code, uri. Typical example: slice on the value of Patient.telecom.system, for values phone, email etc.
pattern This is mostly used with elements of type CodeableConcept where the elements are distinguished by the presence of a particular code but other codes are expected to be present, and are irrelevant for the slice matching process. Typical example: slice on the value of Observation.code, for values LOINC codes 1234-5, 4235-8 etc
exists This is not used commonly - it only has 2 values, so not much discrimination power. It's mainly used as an adjunct slicing criteria along with other discriminators. Elements used like this are mostly complex backbone elements. Typical example: slice on the pattern of Observation.code and the presence of Observation.component.
type Used to match slices based on the type of the item. While it can be used with polymorphic elements such as Observation.value[x], mostly it is used with Resource types on references, to apply different profiles based on the different resource type. Typical example: slice on the type of List.item.resolve() for the types Patient, RelatedPerson.
profile Used to match slices based on the whether the item conforms to the specified profile. This provides the most power, since the full range of profiling capabilities are available, but it is also the hardest to implement, and requires the most processing (>1000-fold compared to the others). Implementers should use this only where absolutely required. Typical example: slice on the type of Composition.section.entry() for the profiles Current-Clinical-Condition, Past-Medical-Event, etc

Each slice must use the element definition for the element(s) in the discriminator(s) to ensure that the slices are clearly differentiated by assigning an appropriate value domain, depending on the discriminator type. If the type is value, or pattern, then the element definition must use either:

It is the composite (combined) values of the discriminators that are unique, not each discriminator alone. For example, a slice on a list of items that are references to other resources could designate fields from different resources, where each resource only has one of the designated elements, as long as they are distinct across slices.

A structure definition is not required to designate any discriminator at all for a slice, but those that don't identify discriminators are describing content that is very difficult to process, and so this is discouraged.

Within a structure definition, a slice is defined using multiple element entries that share a path but have distinct names. These entries together form a "slice group" that is:

  1. Initiated by a "slicing entry" That is, the first element in a slice group must contain a slicing property that defines the discriminator for all members of the group. It also contains the unconstrained definition of the element that is sliced, potentially including children of the unconstrained element, if there are any
  2. Mutually exclusive. This means that each element in a slice group SHALL describe a distinct set of values for the group's discriminators. Because of this constraint, an element in a resource instance will never match more than one element in a given slice group. If no discriminators are named, it SHOULD still be possible to differentiate the slices based on their properties, though it may be substantially harder to do so.
  3. Serialized as a group. The entries in a slice group must be adjacent in a serialized structure definition, or, if there are any intervening elements, those elements must be "compatible with" the group. Concretely, this means that any intervening elements must have a path that starts with the slice groups's path. For example, an element with a path of Observation.name.extension would be compatible with (and thus, would not "break up") a slice group whose path was Observation.name

Some examples of discriminators:

Context Discriminator Type Discriminator Path Interpretation
List.entry value item.resolve().name Entries are differentiated by the name element on the target resource - probably an observation, which could be determined by other information in the profile
List.entry type item.resolve() Entries are differentiated by the type of the target element that the reference points to
List.entry profile item.resolve() Entries are differentiated by a profile tag on the target of the reference, as specified by a structure definition in the profile
List.entry value item.extension("http://acme.org/extensions/test").value Entries are differentiated by the value of the code element in the extension with the designated URL
List.entry.extension value url Extensions are differentiated by the value of their url property (usually how extensions are sliced)
List.entry type, value item.resolve(), item.resolve().value Extensions are differentiated by the combination of the type of the referenced resource, and, if it has one, the code element of that resource. This would be appropriate for where a List might be composed of a Condition, and set of observations, each differentiated by its name - the condition has no name, so that is evaluated as a null in the discriminator set
Observation.value[x] type $this Different constraints (e.g. "must support", usage notes, vocabulary bindings, etc.) are asserted for different supported types for the multi-typed element Observation.value[x]

Note that discriminator types of type and profile can also be used where a repeating element contains a resource directly (e.g. DomainResource.contained, Bundle.entry, Parameters.parameter.resource).

The examples of slicing and discriminators show exactly how this and other typical uses of slicing are represented in profiles.

Note that extensions are always sliced by the url element, though they may be resliced on additional elements where required.

Slice Cardinality

When an element of a fixed cardinality m..n is sliced, the following rules apply:

Default Slice

There is a special slice, called the default slice. This allows a profile to describe a set of specific slices, and then make a set of rules that apply to all of the remaining content that is not in one of the defined slices. Some rules about the default slice:

One use of a default slice would be the case where the profile slices an identifier element to require a set of known identifiers, where the type element is prohibited (since they are known identifiers) but requires type on all other identifiers if any are present. In this case, the default slice makes no rules about the identifier.system (which is the slicing discriminator), but fixes the cardinality of type to 1..1 in the @default slice.

Re-profiling and Re-slicing

Profiles can be based on other profiles, and can apply further constraints to those already specified. This is a useful technique, but implementers should be wary of over-use - humans have trouble understanding the implications of deep stacks of constraining profiles.

When a profile constrains another profile, it can make additional constraints, including extending the discriminator, adding new slices (if the slices are not already closed), and slicing inside the existing slices.

The rules for constraining ElementDefinition.slicing are as follows::

It's sometimes necessary to slice data that has already been sliced in the base profile - that is, create new slices within the existing slices. This is called "Re-slicing". The rules for re-slicing are as follows:

When you slice, you define a name for each new slice. The name has to be unique across the set of slices in the profile. So if profile A defines an element X with cardinality 0..*, and profile B is derived from profile A, then profile B can either:

  1. make a constraint on X with no ElementDefinition.sliceName - in which case the profile is adding constraints to all slices of X; or
  2. make a constraint on X with an ElementDefinition.sliceName - in which case the profile is describing a specific slice on X, and the constraints only apply to that slice; or
  3. it can do both

Then, profile C derives from profile B. Profile C can do the following:

  1. make a constraint on X with no ElementDefinition.sliceName - in which case the profile is constraining all slices of X; or
  2. make a constraint on X with a different ElementDefinition.sliceName from that used in profile B - in which case the profile is describing a specific new slice on X, and the constraints only apply to that slice; or
  3. make a constraint on X with the same ElementDefinition.sliceName as that used in profile B - in which case the profile is making new constraints on the slice defined in profile B; or
  4. some combination of the above options

Note that it is possible for Profile C to make rules that are incompatible with profile B, in which case there is no set of instances that can be valid against profile C

In addition to the above, there are times when Profile C will need to further slice a slice defined in B. In this case, there's a need to reference both the ElementDefinition.sliceName of the original slice from Profile B as well as to define an ElementDefinition.sliceName for the slice defined within Profile C. This is done by separating the names using "/". For example, if Profile B defines the slice "example", and profile C defines the slice "example/example1", then this is deemed to be "example1" slice of the example slice. This process can continue indefinitely by separating each layer of slicing names with the "/" character. This pattern applies to @default too: @default/@default.

Extension Definitions

An extension definition defines the URL that identifies the extension and which is used to refer to the extension definition when it is used in a resource.

The extension definition also defines the context where the extension can be used (usually a particular path or a data type) and then defines the extension element using the same details used to profile the structural elements that are part of resources. This means that a single extension can be defined once and used on different resources and/or datatypes, e.g. one would only have to define an extension for "hair color" once, and then specify that it can be used on both Patient and Practitioner.

For further discussion of defining and using extensions, along with some examples, see Extensibility.

Using Extensions in Profiles

Once defined, an extension can be used in an instance of a resource without any Profile declaring that it can, should or must be, but Profiles can be used to describe how an extension is used.

To actually prescribe the use of an extension in an instance, the extension list on the resource needs to be sliced. This is shown in the extensibility examples

Note that the minimum cardinality of an extension SHALL be a valid restriction on the minimum cardinality in the definition of the extension. if the minimum cardinality of the extension is 1 when it is defined, it can only be mandatory when it is added to a profile. This is not recommended - the minimum cardinality of an extension should usually be 0.

Binding Definitions

Coded elements have bindings that link from the element to a definition of the set of possible codes that the element may contain. The binding identifies the definition of the set of possible codes and controls how tightly the set of the possible codes is interpreted.

The set of possible codes is either a formal reference to a ValueSet resource, which may be version specific, or a general reference to some web content that defines a set of codes. The second is most appropriate where a set of values is defined by some external standard (such as mime types). Alternatively, where the binding is incomplete (e.g. under development) just a text description of the possible codes can be provided.

Bindings have a property that defines the degree of flexibility associated with the use of the codes in the value set. See Binding Strength for further information.

Mixing Custom and Standard Terminologies

CodeSystem resources can be used to carry definitions of local codes (Example) and ValueSets can mix a combination of local codes and standard codes (e.g. LOINC, SNOMED), or just to choose a particular set of standard codes (examples: LOINC, SNOMED, RxNorm). Profiles can bind to these value sets instead of the ones defined in the base specification, following these rules:

Binding Strength in base specification Customization Rules in Profiles
required The value set can only contain codes contained in the value set specified by the FHIR specification
extensible The value set can contain codes not found in the base value set. These additional codes SHOULD NOT have the same meaning as existing codes in the base value set
preferred or example The value set can contain whatever is appropriate for local use

Note that local codes are not as interoperable as standard published code systems (e.g. LOINC, SNOMED CT), so it is preferable to use standard code systems.

Changing Binding Strength in Profiles

A profile can change the terminology binding of an element - both strength and value set - within the limits of the base structure it is constraining. This table summarizes the changes that can be made to the binding strength:

derived (across)
base (down)
required extensible preferred example
required yes no no no
extensible yes yes no no
preferred yes yes yes no
example yes yes yes yes

Note that a constraining profile may leave the binding strength the same and change the value set instead. Whatever the constraining profile does, it cannot make codes valid that are invalid in the base profile.

Must Support

One of the properties that can be declared on profiles but not on resource or data type definitions is 'mustSupport'. This is a boolean property. If true, it means that systems claiming to conform to a given profile must "support" the element. This is distinct from cardinality. It is possible to have an element with a minimum cardinality of "0", but still expect systems to support the element.

The meaning of "support" is not defined by the base FHIR specification, but can be set to true in a profile. When a profile does this, it SHALL also make clear exactly what kind of "support" is required. Examples include:

The specific meaning of "Must Support" for the purposes of a particular profile SHALL be described in the ElementDefinition.definition, the general StructureDefinition.description or in other documentation for the implementation guide that includes the profile.

If creating a profile based on another profile, Must Support can be changed from false to true, but cannot be changed from true to false. Note that an element that has the property IsModifier is not necessarily a "key" element (e.g. one of the important elements to make use of the resource), nor is it automatically mustSupport - however both of these things are more likely to be true for IsModifier elements than for other elements.

Search Criteria

Implementations can define search criteria in addition to those defined in the specification itself. Search criteria fall into one of four categories:

  1. Enabling search on core elements that don't have standard search criteria defined (e.g. searching Observation by normal range)
  2. Enabling search on elements that already have a standard search criteria defined, but with custom matching rules; e.g. a sounds-like search on Practitioner name
  3. Enabling search on a particular extension
  4. Enabling search that doesn't correspond to a single element but rather a combination of elements or computation on an element; e.g. searching for patients by age

Additional Search Parameters can be defined using the SearchParameter resource.

Presenting Profiles

When this specification describes a profile, the profile is presented in 5 different forms:

Text SummaryThis presents a short summary human readable summary of the profile - a combination of the author's summary, and some automatically generated summary content
Differential TableThis is a view of the differential statement (see above). For context, additional information not in the differential is also shown partially transparent
Snapshot TableThis is a view of the snapshot produced by the profile (see above). The information is a comprehensive view of what the profile means
XML TemplateAn example of what the profile looks like in XML format
JSON TemplateAn example of what the profile looks like in JSON format

Supporting Multiple Profiles

Applications may be required to support more than one profile at a time. A typical example might be an EHR application that is required to support a general purpose data sharing profile (such as DAF), and also must support specific profiles for decision support using the same interface.

The impact of supporting two sets of profiles depends on whether resources are being created or consumed. When an application is creating content, it must create content that conforms to both sets of profiles - that is, the intersection of the profiles. When an application is consuming information, then it must be able to consume content that conforms to either set of profiles - that is, the union of the profiles.

Since applications generally consume and produce resources at the same time, conforming to more than one profile might not be possible, unless the profiles are designed to make statements at different levels - and the case above is one such case, where one profile is focused on data access, provenance, and availability, the other profile is focused on clinical content.

Accordingly, profiles can relate to each other in four different ways. Each profile can be thought of in terms of the set of instances that conform to the profile:

  1. Non-overlapping: there no instances that conform to profiles A & B (technically, the intersection of profiles A & B is an empty set)
  2. Partly overlapping: some instances conform to both A & B, but others only conform to A or B
  3. One set contained in the other: all resources that conform to A conform to B, but only some of the ones that conform to B conform to A (or vice versa)
  4. Identical sets: the set of resources that conform to A is the same as the set of resource that conform to B and the set of resources that don't conform to A is the same as the set of resources that don't conform to B

Profiles can be compared to determine their compatibility. One such comparison can be found (no - todo: bring this into the build) between DAF and QICore. Note that this comparison is generated by tooling under ongoing development, and is purely draft content to demonstrate the idea of profile comparison.


pushpull.html

Maturity Level: N/AStandards Status:Informative

Managing Push and Pull

The primary focus of FHIR is the exchange of healthcare data between systems. Whenever one system has information that another needs or wants, one question that automatically arises is whether the source system will push the data, or whether the destination system will pull the data.

Push As new information becomes available, or whenever is deemed appropriate, the source system sends the information to the destination. It expects the destination system to maintain and index the information on receipt. The source system must trust the destination system to manage access/security appropriately
Pull The source system maintains and indexes data. When the destination system wants or needs the information, it must retrieve it from the source system. Either the source or the destination (or both) manages access/security

Note that it is possible to mix these modes by adding an interface repository into the mix. For example, to connect a push-based source with a pull-based destination, simply have the source push information to the repository, and the destination can pull data from it as required. If the source is pull-based, the interface repository can query it for new data on a regular basis, and when it finds any, it can pull it into the repository and/or push it to the destination.

Which combination of push, pull and interface repositories is appropriate for any given context depends on a host of factors around deployment architecture, network and business topology, security policies, etc. However, it is often constrained by the capabilities of the various systems and the standards used in the exchange.

The FHIR specification supports and enables both push and pull:

Push and Pull on a RESTful interface


qa.html

FHIR QA Report

See also the Work Group List Page


r2maps.html

Maturity Level: N/AStandards Status:Informative

Transforms between DSTU 2 and STU 3

In addition to a list of all differences between DSTU 2 and STU 3, this specification also provides transforms between DSTU and STU 3 for most resources that were in DSTU 2. These transforms are expressed using the FHIR Mapping Language, and both serve as formal documentation of the relationship between the resource structures in R2 and R3, and can also be used to convert between the formats automatically.

The following table summarizes the state of the R2:R3 maps for each resource type.

Note the following:

Resource # Tests % Execute OK % RoundTrip Ok % R3 Valid R3 Error Count

r3maps.html

Maturity Level: N/AStandards Status:Informative

Transforms between DSTU 3 and STU 4

In addition to a list of all differences between DSTU 3 and STU 4, this specification also provides transforms between R3 and R4 for most resources that were in R3. These transforms are expressed using the FHIR Mapping Language, and both serve as formal documentation of the relationship between the resource structures in R3 and R4, and can also be used to convert between the formats automatically.

The following table summarizes the state of the R3:R4 maps for each resource type.

Note the following:

Resource # Tests % Execute OK % RoundTrip Ok % R4 Valid R4 Error Count

rdf.html

Resource Description Framework (RDF) Representation

Maturity Level: 2Standards Status:Trial Use

This page and the RDF forms are jointly maintained by the HL7 FHIR project and the W3C Semantic Web Health Care and Life Sciences Interest Group.

FHIR resources can be represented as an RDF graph serialized in the Turtle format. The RDF format is defined to assist the process of bridging between operational data exchange and formal knowledge processing systems. While the RDF form offers a fully functional representation of FHIR resources, it has different operational characteristics from the JSON and XML representations, and would be implemented for different reasons. Systems focused on operational exchange of data would not generally choose to use RDF.

This page describes:

Most of the major pieces of FHIR/RDF are now available in a complete enough form for developers to start trying them and providing feedback. Work still needed includes:

Relationship between the RDF, FHIR, and ontologies

This page documents an RDF format that can be used to exchange FHIR data, on the basis that RDF is a universal information representation. Using RDF enables FHIR data to be used with RDF-aware applications to support inference, shared semantics across multiple standards and data formats, data integration, semantic data validation, compliance enforcement, SPARQL queries and other uses. Implementers using FHIR in this fashion should be aware of the relationship between the RDF format the the wider use of ontologies.

The RDF format is based on the same abstract information model as the XML and JSON formats and carries the same information content. Resources are losslessly round-trippable between XML, JSON and RDF formats and data expressed in the RDF format corresponds closely to the XML and JSON formats in its look and feel. However, there are a few additional terms that may appear in the RDF format, which are explained below: fhir:nodeRole, fhir:treeRoot, fhir:value, fhir:index and fhir:concept. ShEx schemas are available that describe the RDF format.

In addition to defining the RDF format, this specification provides an associated ontology that provides formal definitions for the relationships that appear in the RDF format. Ontologies that were designed independently almost always have some impedance mismatch when attempting to use them together. Many of the ontologies in the medical and life sciences domain are designed to capture facts about the world for research, such as the fact that the mitral valve is a kind of heart valve. But FHIR was designed to support the day-to-day operations of healthcare providers exchanging electronic health records (EHRs), and in this context, the orientation has historically been different. When using FHIR/RDF with other ontologies, impedence differences are likely to show up in two main ways:

For both of these reasons, to maintain monotonicity in RDF, FHIR/RDF should not be directly interpreted as stating facts; transformations are required to remove or isolate non-monotonic elements and reconcile the records across time and perspective.

Application developers should also be aware that some FHIR data attributes have a major impact on the interpretation of the enclosing data element: the meaning of the enclosing element cannot be determined in isolation. For example, a status of 'entered-in-error' means that the resource was created accidentally and should be ignored for most purposes.

Turtle Format for Resources

A FHIR resource is represented by a series of RDF triples. The Turtle representation for a resource is described using this format:

Turtle Template

[ a fhir:Observation; fhir:nodeRole fhir:treeRoot;
  # from Resource: id; meta; implicitRules; and language
  # from DomainResource: text; contained; extension; and modifierExtension
  fhir:Obervation.identifier [ Identifier ]; # 0..* Unique Id for this particular observation
  fhir:Obervation.status [ fhir:value "<code>" ]; # R!  registered | preliminary | final | amended +
  fhir:Obervation.code [ CodeableConcept ]; # 1..1 R!  Type of observation (code / type)
  fhir:Obervation.subject [ fhir:reference [ Patient|Group|Device|Location ] ]; # 0..1 Who and/or what this is about
  fhir:Obervation.encounter [ fhir:reference [ Encounter ] ]; # 0..1 Healthcare event during which this observation is made
  # effective[x]: 0..1 Clinically relevant time/time-period for observation. One of these 2:
    fhir:Obervation.effectiveDateTime [ fhir:value "<dateTime>" ];
    fhir:Obervation.effectivePeriod [ Period ];
]

Using this format:

Representing resources

Each resource is represented as a set of RDF triples represented using the Turtle syntax. When a resource has a persistent identity (e.g. it can be found at a particular URL - usually a FHIR RESTful server), then that URL is its identity. Resources with no persistent identity (e.g. bundles from search results) have the identity of the root document - "<>" in Turtle syntax.

Some resources can contain other resources. Given that the relationships can appear in any order, it cannot be assumed that the first encountered element represents the resource of interest that is being represented by the set of Turtle statements. The focal resource - where to start when parsing - is the resource with the relationship fhir:nodeRole to fhir:treeRoot. If there is more than one node labeled as a 'treeRoot' in a set of Turtle statements, it cannot be determined how to parse them as a single resource.

Representing Repeating Elements

Elements that can repeat are represented with a relationship

  fhir:index [n]

where [n] is a zero-based integer offset (i.e. the first element has an index of 0). Lists are never sparse; it is an error if there are missing items in the repeat sequence.

Note: this means that the rdf:list structure is not used.

Representing Primitive Elements

Primitive elements - elements with a primitive type - are represented as regular nodes so that the elements extensions can be represented. The actual value of the primitive type is represented using the fhir:value predicate:

  fhir:value "[value]"^^xs:type

The value has two parts: a literal string that contains the value, and, if applicable, one of the following schema types:

The choice is made based on the types as specified for the primitive type. Note that the correct schema type for a date/dateTime must be determined by inspecting the value of the date for precision.

The fhir:value property can never be empty. Either the relationship is absent, or it is present with at least one character of content. XHTML is represented as an escaped xs:string.

Representing References

A Reference element is represented using the same rules as above:

 fhir:Observation.subjectReference [
     fhir:Reference.reference [ fhir:value "Patient/example" ];
     fhir:Reference.display [ fhir:value "Example Patient" ];
  ];

This allows faithful round tripping of the resource between the Turtle format and the JSON and XML formats. However, it's very useful for an RDF processor if the RDF graph links to the target of the reference directly. This can be represented using the fhir:link property:

 fhir:Observation.subjectReference [
     fhir:link <http://hl7.org/fhir/Patient/example>;
     fhir:Reference.reference [ fhir:value "Patient/example" ]
  ];

The correct value for the fhir:link relationship must be determined by resolving the rules for resolving references for the various reference types to a literal URL that refers to the correct content in the local RDF context.

The fhir:link relationship can be added automatically as part of generating the resource representation, or it can be injected by a post-processor that knows how to convert the raw references into RDF-suitable references.

Representing Inline Resources

Inline resources - when a resource is contained directly in another element occur in the following places:

Inline resources are represented directly as anonymous nodes. This is an example for a contained Medication resource:

  fhir:DomainResource.contained [
     a fhir:Medication;
     fhir:index 0;
     # triples for the Medication
  ]

Note that DomainResource.contained has a cardinality of 0..*, so fhir:index is used to order the array.

Representing Concepts

The same logic applies to the Coding data type. These are represented directly in Turtle by serializing their properties as specified above:

  fhir:Observation.code [
     fhir:CodeableConcept.coding [
       fhir:index 0;
       fhir:Coding.system [ fhir:value "http://loinc.org" ];
       fhir:Coding.code [ fhir:value "29463-7" ];
       fhir:Coding.display [ fhir:value "Body Weight" ]
    ];
    fhir:CodeableConcept.coding [
       fhir:index 1;
       fhir:Coding.system [ fhir:value "http://snomed.info/sct" ];
       fhir:Coding.code [ fhir:value "27113001" ];
       fhir:Coding.display [ fhir:value "Body weight" ]
    ]
  ];

For reasoners using the RDF graph, it's very useful to make the implicit concept references in these Codings explicit using a rdf:type assertion ("a" in Turtle):

  fhir:Observation.code [
     fhir:CodeableConcept.coding [
       fhir:index 0;
       a loinc:29463-7;
       fhir:Coding.system [ fhir:value "http://loinc.org" ];
       fhir:Coding.code [ fhir:value "29463-7" ];
       fhir:Coding.display [ fhir:value "Body Weight" ]
     ];
     fhir:CodeableConcept.coding [
       fhir:index 2;
       a sct:27113001;
       fhir:Coding.system [ fhir:value "http://snomed.info/sct" ];
       fhir:Coding.code [ fhir:value "27113001" ];
       fhir:Coding.display [ fhir:value "Body weight" ]
    ]
  ];

These rdf:type assertions can be made by any agent that knows how to convert from the code system to the correct ontological representation on the RDF context. Note that a few code systems have standard ontological representations, but many don't. Again, these assertions can be made by the serializer, or injected by a post-processor.

Schema

FHIR uses ShEx for representing the Turtle schema. See fhir.shex for definitions.

RDF Representation of FHIR

In addition to the basic representation of FHIR resources in Turtle format, a Turtle representation of the FHIR infrastructure and definitions is also published, for the following purposes:

The RDF definitions are published as a series of Turtle files: HL7 v3 RIM (Reference Information Model) and FHIR.

Using RDF with the REST API

TODO

Using FHIR/RDF

Application developers wishing to use FHIR/RDF will often need to perform the following rough steps, though exact steps will depend on your application:

  1. Convert FHIR/XML or FHIR/JSON data to/from FHIR/RDF. Open source implementations that can perform this conversion include:
  2. Download the FHIR ontology.
  3. Download other ontologies that you wish to use with your application, such as the SNOMED-CT ontology. (Because of license restrictions, the process of obtaining the SNOMED-CT ontology is more complex than a simple download. Some guidance is provided here.)
  4. Create or obtain a bridge ontology that relates terms in the FHIR ontology to terms in your other ontologies.
  5. Load your FHIR/RDF instance data and your ontologies into a reasoner and/or a triplestore, and perform SPARQL queries and/or reasoning.
  6. If you are creating FHIR/RDF then you might also wish to validate it, using ShEx. If so, you can download the ShEx schema for FHIR/RDF

Slides by Eric Prud'hommeaux illustrate how inference can be used on FHIR/RDF data to perform a query for FHIR Observations of rheumatoid arthritis, using the SNOMED-CT ontology. Caveat: those slides show some terms in the fhir: namespace that do not actually exist in the fhir: namespace, such as fhir:Observation-of-Rheumatoid_arthritis-disorder and fhir:Coding-of-Rheumatoid_arthritis-disorder. A different namespace should have been used in the example.


redirect.html

See here: this link.


references-definitions.html

Maturity Level: NormativeStandards Status:Normative