Background
This is meant to help guide a developer who needs to map source data to the FHIR intermediate data model. One simple example of this is in the src/custom/dbt/dbt_transform_defacto_to_fhir folder and src/custom/dags/dag_import_and_transform_defacto_health_to_fhir.py DAG. Because of the simplicity of this data set (it is only a few resources, with only a few fields populated for each resource), some extra detail may be necessary to properly map data to FHIR.
Many of the tips in this page will also be helpful for writing a query from V12 to a FHIR resource. While the tips will be geared towards getting data into the intermediate FHIR model, a lot of the core FHIR concepts will be the same for V12 queries.
Important Concepts
We will go over some important concepts related to understanding FHIR. In particular, this page will cover
- Profiles VS Resources
- CodeableConcepts
Profiles VS Resources
A FHIR Resource is the base data model used by any system that produces or consumes FHIR. FHIR Resources have versioned definitions, for example, https://hl7.org/fhir/organizationaffiliation.html is the current (v5.0) version of the OrganizationAffiliation Resource and https://hl7.org/fhir/R4/organizationaffiliation.html is the older v4.0.1 version of the OrganizationAffiliation Resource.
A Profile is a modification to a particular version of a FHIR Resource Type. If a particular instance of a Resource Type adheres to all of the modifications, then we say that the Resource supports the Profile. For Monarch's FHIR servers, we support the Profiles defined as part of the PlanNet specification - https://build.fhir.org/ig/HL7/davinci-pdex-plan-net/artifacts.html#structures-resource-profiles
There are 3 primary modifications a Profile can make on a Resource.
Must SupportFlags- Changing Cardinality
- Extensions
Must Support Flags
Profiles may mark certain fields with a Must Support flag indicated by a red S. Fields that are a part of the original FHIR Resource that are not in the profile with a Must Support flag do not need to be supported for the Resource to support the Profile.
If a field in a Profile is marked with the Must Support flag, and the generated FHIR Resource does not have data in this field, then that must be interpreted as the source system not having any data for that field. If the source system does have data to populate a Must Support field, and the field is not populated then this is an error in the FHIR generation. If a field is marked as Must Support, but it is a complex field (a field with one or more sub-elements), then not all sub-elements necessarily need to be supported. If a sub-element is marked as Must Support then that sub-element must be supported.
The Monarch FHIR data model defined in src/custom/fhir_module/schema/object_meta.json currently has fields for all elements marked as Must Support. When mapping to this data model (or from V12 to a FHIR Resource), take care to ensure each field is populated if possible based on the source data.
For more information on the Must Support flag, reference https://build.fhir.org/ig/HL7/US-Core/must-support.html.
Changing Cardinality
Resources have predefined cardinalities on fields. Cardinalities can be in one of 4 forms:
- 0..* - This field is a list of elements, but can be missing from the Resource if not applicable
- n.._ - This field is a list of elements, and must have at least n entries in the list. Most commonly, this is 1.._
- 0..1 - This field is a single element, but can be missing from the Resource
- 1..1 - This field is a single required element
A Profile can modify the default cardinality of a FHIR Resource, with some restrictions.
- A Profile can never take a required field (1.._ or 1..1) and change it to optional (0.._ or 0..1)
- If a Profile changes a list of elements (0..*) to a single element (0..1), the generated resource must still put that single element into a list
- A Profile can never take a single element (0..1 or 1..1) and change it into a list (0.._ or n.._)
For the PlanNet specification Profiles, the most common change to cardinality is to make optional fields required (i.e. a Practitioner Resource has cardinality of 0..1 on the active field, but the PlanNet Practitioner Profile has a cardinality of 1..1 on the active field). On occasion, the PlanNet spec also limits elements with cardinality 0..* to 0..1.
The Monarch FHIR queries defined in src/custom/fhir_module/queries currently use array_agg, jsonb_agg, and array[] calls to make sure cardinality is set properly. Cardinality is also enforced after a FHIR Resource is generated based on JSON validation using the schemas in src/custom/fhir_module/response_schema. If modifications are needed to these queries, take care to ensure that the cardinality matches what is expected by both the PlanNet Profiles and the base FHIR Resources.
For more information on Cardinality, refer to https://build.fhir.org/profiling.html#cardinality
Extensions
Profiles also defined expected extensions that should be present on a generated FHIR Resource. These are marked as Must Support. Each Profile will provide information about the structure of each Extension that must be supported.
Extensions must be formatted in a very particular way. https://build.fhir.org/ig/HL7/davinci-pdex-plan-net/StructureDefinition-newpatients.html links to a definition of an extension used by the PlanNet spec and defines the expected format for the extension.
Each extension must have a URL that links to the StructureDefinition. If custom extensions are requested, a StructureDefinition will need to be created and hosted somewhere for the FHIR Resource to link to. Currently, the Monarch FHIR servers only support extensions defined by the PlanNet spec.
Each extension then has one or more elements that should be supported. In the case of a single element, a value{Datatype} field (valueDecimal or valueBoolean for example) should be present along with the url to the StructureDefinition. If there are multiple elements, then an extension field should be present along with the url. This extension field is a list of further extension objects, each with their own defined url or value{Datatype} fields.
Using the linked newpatients extension StructureDefinition as an example, we can see there are 3 expected fields for this extension - acceptingPatients, fromNetwork and characteristics. Not all of these fields are required. Based on the cardinality, only acceptingPatients is required. The resulting extension field in the final resource could then look similar to the following:
{
extension: [{
url: 'http://hl7.org/fhir/us/davinci-pdex-plan-net/StructureDefinition/newpatients',
extension: [{
url: 'acceptingPatients',
valueCodeableConcept: {
coding: [{code: 'nopt'}]
}
}, {
url: 'fromNetwork',
valueReference: {
display: 'Name of some Network'
}
}]
}]
}
If more than one extension is present on the resource, then there would be another entry at the top level extension field, with a new URL to point to the StructureDefinition of that extension. The Monarch FHIR queries currently generate extensions based on fields defined in the Monarch FHIR data model.
For more information on Extensions, refer to https://build.fhir.org/extensibility.html
CodeableConcepts
Some elements on a Resource may be of the type CodeableConcepts. CodeableConcepts often have some restrictions on what values can be present in the various fields on the CodeableConcept datatype. These restrictions come from various bindings to ValueSets, and different CodeableConcept fields can have different levels of bindings than others.
When mapping from a source system to the FHIR data model (or from V12 to a FHIR query), be careful to check the binding of any CodeableConcept fields on the Profile. In the case of extensible bindings, or required bindings with multiple possible system values, the codes are typically kept in separate fields to indicate that they come from separate systems. For an example of this, refer to OrganizationAffiliations fhir_roles, plannet_roles, and other_roles fields.
Bindings
- required - The value for the CodeableConcept
codeMUST come from the bound ValueSet and thesystemmust be the system defined in the bound ValueSet. Often the bound ValueSet will have a placeholder value for 'other' or 'unknown' that can be used if no other appropriate mapping exists - extensible - The value for the CodeableConcept
codeSHOULD come from the bound ValueSet if possible. If it does, then thesystemmust also come from the bound ValueSet. If there are no values in the bound ValueSet that are appropriate for the source data set, then a newcode/systempair can be used or the value can be directly used as the CodeableConcepttextfield. It would be considered not conformant to use thetextfield or a newcode/systempair if there is an appropriate mapping in the bound ValueSet - preferred - The value for the CodeableConcept
codeis encouraged to come from the bound ValueSet, but there is no requirement to use the bound ValueSet to be conformant. If there is an appropriate mapping in the bound ValueSet but thetextfield or a newcode/systempair are used, the instance is still considered to be conformant. - example - The value for the CodeableConcept
codedoes not matter. The bound ValueSet is just meant to serve as a possible example of values that might be put in this field.
For more information on CodeableConcepts, refer to https://hl7.org/fhir/datatypes.html#CodeableConcept.
For more information on Bindings, refer to https://hl7.org/fhir/terminologies.html#strength