In an earlier blog post FHIR R4 at Firely, my colleague Ardon outlined our product roadmap for 2019. In this post, we will explore the changes that FHIR R4 introduces in the profiling and conformance layer and how this affects Forge, the official FHIR profile editor.
The profiling layer has seen some drastic changes throughout the different draft releases of the FHIR standard, driven by feedback from the FHIR community and experiences from HL7 Connectathons. The initial FHIR DSTU1 release introduced a conformance resource called Profile for capturing type definitions and constraints. However, the original resource title generated a lot of confusion as the term profiling is heavily overloaded within the HL7 technology space. Subsequently, in FHIR release DSTU2, the community decided to rename the resource to the unambiguous StructureDefinition. The term profile has since been deprecated to informal use, colloquially referring to a set of constraints on a FHIR resource, datatype or other profile (versus a core resource/datatype definition or a logical model).
In FHIR STU3, the conformance layer matured and stabilized. The community further improved the StructureDefinition resource in order to allow certain element type and slicing constraints to be expressed unambiguously. Profiling tools such as Forge were slowly (…) maturing and around the world, early adopters started to publish FHIR implementation guides and deploy FHIR profiles into production environments.
The FHIR STU3 conformance layer has been extensively and thoroughly tested by the community and proved to be powerful, flexible and mostly well defined. FHIR R4 brings a couple of further tweaks to profiling, but the community did not see a need to introduce any drastic changes. Nonetheless, these few “tweaks” in R4 do represent breaking changes that require existing profiling tools to be updated. Let’s take a look at some relevant breaking changes in further detail.
Element type constraints
A FHIR profile element specifies a list of supported element types, uniquely identified by the associated type code. By default, a profile element conforms to the core FHIR datatype with the specified type code. A profile element can also introduce an optional element type profile constraint. If a profile element specifies an optional type profile, then the constraints defined by the referenced type profile override the constraints of the underlying core datatype. This approach allows authors to define and manage common, shared type constraints in a centralized location and reference these constraints from many other profiles.
Profile element type constraints are captured by the ElementDefinition.type component. The ElementDefinition.type.profile field allows an author to specify the canonical url of an optional custom type profile. Additionally, FHIR also defines a field ElementDefinition.type.targetProfile. The targetProfile field applies only to resource reference elements (type.code=Reference) and specifies optional custom constraints on the target of the reference.
The definition of the ElementDefinition.type component has changed from FHIR STU3 to R4. FHIR STU3 defines the ElementDefinition.type.profile field with cardinality 0…1, as shown below. This implies that each element type can optionally specify a single custom type profile. A FHIR STU3 profile element also allows multiple element type constraints for a common type code. Instance data should conform to (at least) one of the specified type constraints (logical OR).
The following screenshot shows how element type constraints are represented in Forge for STU3. In this example, an Observation profile defines multiple type constraints for the value[x] child element. Each custom type constraint repeats the same type code (Range) and specifies a different custom type profile. Instance data should conform to one of the specified custom Range profiles.
In FHIR R4, the ElementDefinition.type.code field values must be unique. Elements are not allowed to specify repeated type codes. On the other hand, the cardinality of the ElementDefinition.type.profile field (and also of the .targetProfile and .aggregation fields) has changed from 0…1 to 0…* . Instance data should conform to (at least) one of the specified type codes (logical OR). If an element type constraint specifies multiple type profiles, then instance data should conform to (at least) one of the specified profiles (logical OR).
Basically, in FHIR R4, element type constraints are grouped by unique type code, whereas in FHIR STU3 type constraints are represented as a flat unordered list. The grouping on type code in R4 ensures that constraints for a specific type code only need to be specified once. This improvement removes a bit of redundancy, as the STU3 profile syntax requires common type constraints to be repeatedly defined by all element type components with a matching type code.
The user interface of Forge for R4 must be updated accordingly to reflect this breaking change. Preferably, whenever changes in the FHIR standard require us to update the user interface, we also try to improve the user experience. However, as we maintain to provide support older Forge releases for FHIR DSTU2 and STU3, we try to keep the different branches in sync as much as possible. This breaking change forces a redesign of relevant user interface components and requires us to strike a balance between improving usability versus limiting the maintenance burden of diverging code branches.
Choice type constraints
A choice type element is a polymorphic field that supports multiple datatypes. Choice type elements are indicated by an element name that ends with suffix [x]. FHIR R4 improves the expressiveness of constraints on polymorphic choice type elements and allows a simplified, less noisy syntax.
FHIR STU3 defines the following rules for constraints on choice types:
- If a profile constrains a choice type to a single type code, then the element is renamed, replacing the [x] suffix with the element type (e.g. value[x] to valueString).
- If a profile allows multiple type options for a choice type element, then elements are not renamed.
- A profile must slice the choice type element in order to define multiple constraints for different type options. The slice introduction element defines the list of allowed type codes. The list of element types in a derived profile must be a subset of the list of element types allowed by the base profile.
- Each named slice defines additional constraints for individual element type options. Named slices maintain the original element name with [x] suffix (no renaming).
Example 1: STU3 choice type element constrained to a single type
|Observation.valueString||Limited to single type|
Example 2: STU3 choice type element constraints with multiple type options
|Observation.value[x]:valueString||Constraints on type string|
|Observation.value[x]:valueBoolean||Constraints on type string|
In FHIR R4 introduces a simplified and improved syntax for defining choice type constraints:
- A profile can introduce element constraints for a choice type element with suffix [x]. Initial constraints defined on the polymorphic [x] element represent common, generic constraints that apply to all allowed type options.
- A profile can constrain the type component of the original choice type element name with suffix [x] to limit the list of allowed type options. The list of element types in a derived profile must be a subset of the list of element types allowed by the base profile. Only explicitly included type options are allowed; all other types are disallowed.
- A profile can also introduce one or more type-specific element constraints on a choice type, without having to specify a slicing introduction. Slicing is still allowed, but no longer required. Each element constraint applies to another type option.
- Choice type constraints that apply to a single type option always trigger an element rename (e.g. value[x] to valueString). A profile can introduce multiple renamed element constraints (e.g. define valueString and also valueBoolean).
- Renamed choice type constraints do NOT limit the list of allowed element types; this is strictly determined by the initial constraints on the original chioce type element with [x] suffix.
The new syntax for choice types in R4 is less verbose and hopefully more intuitive to end-users. A slicing introduction is usually redundant and no longer mandatory, although still allowed to cover advanced scenario’s. Element constraints for individual type options are always renamed, improving the expressiveness and readability of the profile. And with the introduction of support for common constraints on the [x] element, R4 profiles no longer need to repeatedly define similar constraints on different choice types.
Example 3: R4 choice type element constraints with multiple type options
|Observation.value[x]||Limit types (string, boolean)|
|Observation.valueString||Constraints on string|
|Observation.valueBoolean||Constraints on boolean|
User interface improvements
Since early Forge releases, the application has provided support for authoring element type profile constraints. However, the user interface remains quite basic and requires the user to manually input the full canonical url of a target profile:
In 2018, we have introduced a new Project Explorer in Forge that provides access to a set of related profiles located in a project folder structure on disk. Subsequently, we have also integrated a FHIR NPM package manager that allows users to import and manage external dependencies, such as extensions published on the official HL7 FHIR registry or on Simplifier. The Folder Explorer integrates imported packages as virtual subfolders of the selected project folder, allowing easy access to all external references. Forge considers imported external references as “read-only”; opening an external reference will automatically create a new duplicate, in order to prevent modifications to the original.
Recently, we have updated the user interface for managing profile extensions. The command to add a new extension element to the current profile now displays a selection dialog based on the folder explorer. The extension selection dialog displays a filtered view of all compatible extensions in scope (from the working folder and all referenced packages), by matching the extension context to the selected profile element. When the user selects a compatible extension definition from the list, Forge will copy the associated canonical url to the receiving target profile.
In an upcoming release, we will also update the user interface for editing type profile constraints, similar to extension definitions. Forge will allow users to open a type profile selection dialog based on the folder explorer. The selection dialog will display a filtered view of all compatible profiles in scope (from the working folder and all referenced packages), matching the type of the selected profile element. When the user selects a compatible type profile from the list, Forge will copy the associated canonical url to the receiving target profile.
Ultimately, we plan to completely remove the need for the error-prone and tedious manual input of canonical url’s, by integrating the Project Explorer into all profile attributes that represent a (canonical url-based) reference to an external conformance resource. For example, this would allow users to edit a terminology binding by selecting a target ValueSet resource from the current project scope.
Forge, like all our Firely tools, is based on the open source FHIR .NET API library on github. Most of the FHIR-specific logic that Forge relies on is provided by the API, including the fundamental snapshot generator logic that is at the heart of FHIR profiling. We’ve updated most of the API logic to R4. However some advanced scenarios, such as the new behavior for choice type constraints, have not been implemented yet. We are now working to cover the remaining few implementation gaps. As soon as the relevant API logic has been updated to R4 and tested, we will start working on a new Forge branch for R4. Finally, we will have to redesign some UI components to conform to breaking changes in the FHIR R4 spec. We aim to publish a new Forge R4 release in time for the next HL7 WGM in May 2019.
Just like Forge for DSTU2 and STU3, we will also publish Forge R4 as a separate install package with a separate application identity. This ensures that you can install several different Forge releases for different FHIR versions on the same machine without any conflicts.