Category Archives: Profiling

Versioning and canonical urls

 

At last week’s FHIR Developer Days in Amsterdam, we had a highly enjoyable break-out session on the use of canonical urls when taking versioning into consideration.

The issue had been popping up more often recently, and we, as the core team, had been pushing off trying to solve it until we had better understanding of the problem. But here we were: we had a room full of knowledgeable people and enough experience to take a stab at a solution.

For those not present, I’d like to summarize what we discussed, seamlessly turning into what I think needs to happen next.

The problem

Let me start off by giving you an overview of the problem.
The two key players in the game are the canonical url and the canonical reference. The canonical url is a special identity present on most of our conformance resources (StructureDefinition, ValueSet and the like). This identity is used by a canonical reference, present in StructureDefinition (e.g. StructureDefinition.base) and FHIR instances (Resource.meta.profile), which is used to refer to other StructureDefinitions (or any other conformance resource). For example, the StructureDefinition for the localized version of Address in Germany starts like this:

<StructureDefinition>    
    <url value="http://fhir.de/StructureDefinition/address-de-basis" />
    <name value="address-de-basis" />
    <title value="Adresse, deutsches Basisprofil" />

Here, the <url> contains the canonical url of the address-de-basis StructureDefinition.

This canonical url can be referenced by other parts of your specification, as for example here at Patient.address in the StructureDefinition of the German version of Patient:

<element>
    <path value="Patient.address" />
    <short value="Adresse nach deutschem Profil" />
    <type>
        <code value="Address" />
        <profile value="http://fhir.de/StructureDefinition/address-de-basis" />
    </type>

This is quite comparable to what happens in your favorite programming language, and then referring to it when you are declaring an instance variable of class member:

public class GermanAddress
{
    ....
}

public class GermanPatient
{
    ....
    public GermanAddress Address { get; }
}

As a good member of the FHIR community, you have published all your profiles to a registry (these examples come from the German Basisprofil on Simplifier), and people are happily validating their Patient instances against it. Some of the instances may even have gotten tagged by a profile claim:

    <Patient>
        <meta>
            <profile value="http://fhir.de/StructureDefinition/address-de-basis">
        </meta>
        <!-- rest of the Patient's data -->
    </Patient>

Before long, this canonical reference gets baked into both instances in databases and software using your profile.

And then, the day comes that you are required to make changes to your profile. Breaking changes in fact. You are circling your brand new version of the profile amongst your colleagues, everyone is updating their test-instances and all works fine. Until you publish the new version on the registry. Once a copy of your StructureDefinition starts tickling down to servers around the country, previously correct instances of the German Patient will now start to fail validation. You realize you have broken the contract you had with your users: stuff that was valid before has now -without the end-users doing anything- become broken.

More subtly, if your breaking change was to the address-de-basis profile, this turns out to be a breaking change to all profiles depending on address-de-basis, which then proliferate up all the way to the instances.

A simple solution

It is easy to see what you should have done: you (and all your users) should have versioned both the canonical url and the canonical reference! So,

<StructureDefinition>    
    <url value="http://fhir.de/StructureDefinition/address-de-basis-v1" />

and

<type>
    <code value="Address" />
    <profile value="http://fhir.de/StructureDefinition/address-de-basis-v1" />
</type>

and finally

<meta>
    <profile value="http://fhir.de/StructureDefinition/address-de-basis-v1">
</meta>

This way, when we publish a new version we may change the canonical url and update it to end in v2, and all existing materials will remain untouched and valid.
Of course, minor changes are OK, so if we all agree to stick to semver, and only change our canonical url when a major version number changes, we’re doing fine. We formalized this approach in the STU3 version of the FHIR specification, by adding a <version> element to StructureDefinition:

<StructureDefinition>    
    <url value="http://fhir.de/StructureDefinition/address-de-basis-v1" />
    <version value="0.1" />

and then allowing you to tag canonical references with a | and a version number like so:

<type>
    <code value="Address" />
    <profile value="http://fhir.de/StructureDefinition/address-de-basis|0.1" />
</type>

Ok. Done. We published STU3 and hoped the problem was solved.

But it wasn’t. Well, technically, it was – but there are a few practical complications:

  • We had not followed this practice ourselves in the specification (leaving http://hl7.org/StructureDefinition/Patient untouched for years across STUs), and neither did the most prominent early Implementation Guides (like those from Argonaut). We set bad examples that turned out to be the path of least resistance as well. Guess what happens.
  • It just feels wrong to hard-wire version numbers in references within a set of StructureDefinitions and Implementation Guides you author. If you publish them as a version-managed, coherent set of StructureDefinitions, it is obvious that you’d like to reference the version of the StructureDefinition published at the same time as the referring StructureDefinition in that same set.
  • If you need to bump the version in a canonical url in the set that you publish, you need to be really sure you update all references to it in that set. And then (as we saw above) update the canonical url of all referring StructureDefinitions, and so on and so on. If you fail to do this, you end up in a situation where part of your definitions are using one version and another part another version. Granted, we could find someone who thinks that’s a feature, but I am sure most would disagree.

Better tooling support for authors could ease this job and help sticking to versioned references, but I kept having the nagging feeling something was not right. This was strengthened by the fact that this is not how we commonly version in other development environments:
continuing our parrallel with programming concepts, versioning the canonical url would be comparable to versioning our class names:

public class GermanAddress2 { ... }
public class GermanName4 { ... }

public class GermanPatient2
{
    ....
    public GermanAddress2 Address { get; }
    public GermanName4 Name { get; }
}

It is not like we’ve never seen this before, but that’s really only done if you want to keep incompatible versions of the same class within the same codebase, because you still need access to both (e.g. to do mapping).

At the same time, we were looking at how users of Simplifier and authors of implementation guides organized their projects and how they wanted versioning to work. It turns out that StructureDefinitions simply do not live on their own, much like Java classes are not shared and distributed in isolation. They are authored and shipped in sets (like implementation guides), and are versioned in those sets. Of course, they may still use canonical references to materials outside the set, and you’d need to tightly version those references, but inside their set, they simply mean to point to each other.

You don’t need to look around long at how other parts of the industry have solved this to realize that we need the concept of a “package”, much like you would package up your classes and Javascript files into zips, jars or whatever and ship them as npm, maven or NuGet packages.

Packages to the rescue

If you are not familiar with these packaging mechanism, I’ll call out a few properties of packages, to see how they solve our problems:

  • Packages contain artifacts that are managed by a small group of authors, who prepare them as a consistent set and publish them as an indivisible unit under the same version moniker.
  • A package is published on a registry and has a name and a version, the combination of which is enforced to be unique within that registry. You cannot overwrite a package once it has become published.
  • Packages explicitly state on which version of which other packages they depend, and contain configuration information on how to handle version upgrades and mismatches. Additionally, the user of a packages may override how version dependencies are resolved, even enforcing the use of an older or newer version of the dependency when desired.

Packages are usually just normal zip archives with all artifacts you like to publish, with an additional “configuration file” added to the zip:

{
  "name": "http://fhir.org/Package/package-de-basis",
  "version": 1.0.4,
  "dependencies": 
  {
    "http://fhir.org/Package/some-external-package": ">3.1.4"
  }
}

This has considerable advantages for the users:

  • When a user downloads a package, it contains “everything you need” to start working with StructureDefinitions within it, instead of having to download individual artifacts one by one from a registry.
  • The user also has the confidence that these artifacts are meant to be used together, which is not obvious if you are looking at a huge list of StructureDefinition on the registry.
  • A package manager will ensure that if you download one package for use in your authoring environment or machine, all dependencies will also be retrieved, and you would not encounter errors due to missing references.

As an author of a package of conformance resources this means that you no longer need to version your canonical references: they are interpreted to mean to refer to the resources inside your package. This is even true for canonical references to StructureDefinitions and ValueSets outside your package, since you as an author explicitly declare these dependencies and version them at the package level and no longer at each individual reference. Upgrading to a new version of a dependency is now completely trivial.

It might even solve another long standing wish expressed by authors: you could configure “type aliases” within your package, stating that every reference to a certain core type (or profile) within the package is translated to a reference to another type. If you wonder why that is useful, consider the poor German authors who defined a German Address (address-de-basis) and then needed to go over all of their StructureDefinitions to replace references to the “standard” core Address with their localized version. It’s pretty comparable to redirecting conformance references to a specific version of a StructureDefinition, so we can solve this in one go.

Concrete steps

Looking at the situation today, I suggest we do the following:

  • Remove <version> from StructureDefinition, and obsolete the use of | in references (though hard version references might still have a use).
  • Decide what we need as a syntax to define packages and declare dependencies. We could leverage existing mechanisms (package.json being a prime candidate) or integrate it into the existing ImplementationGuide resource (which would enable existing registries to simply use a FHIR endpoint to list the packages).
  • Enhance the authoring workflow in the current tools to allow the authors to create packages when they want to publish their IGs, fixing the external dependencies to specific versions.
  • Create (or re-use) package managers to enable our users to download packages and their dependencies (and turn make our registry compatible with those tools).

Are we done? No. There are two more design decisions (at least) to make. Let’s start with the easiest one:

It is apparent that there is a relationship to our concept of an “Implementation Guide” and a package. And we have to figure out exactly what that relationship is. I feel an Implementation Guide is a kind of package, containing the conformance resources, html files, images, etcetera that form the IG. But this also means there will be packages that are not ImplementationGuides. If we decide that the ImplementationGuide resource is the home of the “configuration part” of a package, we will need to rename ImplementationGuide to reflect its new scope.

But I saved the most impactful consequence for last:

You can no longer interpret canonical references present in conformance resources or instances outside of the context of a package.

Let me reiterate that: any authoring tool, validator, renderer or whatever system that uses StructureDefinitions to do its work will need to know this context. Those who have carefully studied the current ImplementationGuide resource (especially ImplementationGuide.global) realize this is already the case now, but most are blissfully unaware of this hidden feature.

For systems working with conformance resources (like instance validators), it’s likely they have this context: if you’re dealing with a StructureDefinition, you probably got it from a package in the first place (it becomes a different matter entirely if a resource can be distributed in different packages. Well, let’s not diverge.)

For servers exchanging instances however – we’d need to assume they know the context out of band. But this won’t do for partners exchanging outside of such a controlled environment. For this let me suggest to summon an old friend, hidden in a dark corner of the FHIR specification: Resource.implicitRules

The specification states:

Resource.implicitRules is a reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content

that sounds about right, however, it continues:

Wherever possible, implementers and/or specification writers should avoid using this element.

And sparingly lists the reasons why we shouldn’t. I suggest we take a fresh look at this element and see whether the element we thought up so many years ago may find use for it after all.

Finally

We’re not done yet, and I admit I am not sure I’ve dealt with all the consequences of this versioning solution here, but it has one thing going for it: we’re profiting from solutions thought up by the rest of the industry who have dealt with this before. But is an exchange standard and it’s artifacts completely comparable to a software package? Will we be able to fix the problem of communicating package context?

I don’t know yet, but the more I think about the uses of packages and how it eases the life of authors and users of implementation guides, the more I think we should pursue this a bit in online discussions. Hope they are as engaging as the one at FHIR DevDays!

Tune in to our new Profiling Academy and become a profiling expert yourself!

By Lilian Minne – At the DevDays 2017, we launched our new product: the Profiling Academy. The Profiling Academy is available for all Simplifier users (free to join) and is meant for anyone willing to learn more about FHIR profiling. We aim to share our knowledge in a way that is easy to digest for all levels of FHIR users: beginner, moderate and advanced.

The Profiling Academy offers short, digestible modules covering one topic each. Each module offers reading material, real-life examples and exercises.  At this moment the following modules are available: Start Profiling, Extensions, Slicing and Best-Practices. More modules will be added in the near future. If you want to follow a module, just click on its name or use the menu in the upper navigation bar.

Profiling academy

Don’t forget to visit the other pages as well:

  • Feature movies: Watch interesting movies explaining some of the features of our products.
  • Helpful links: This page contains useful links when working with FHIR.
  • Meet our team: Get to know our Profiling Team members who are happy to introduce themselves.
  • About FHI: Learn more about our company and how to get in touch.

We are curious to know how you feel about the Profiling Academy. As the Profiling Academy was built using the IG-editor in Simplifier (pretty cool, right?), please leave your comments in the Issue Tracker of the project.

Element Identifiers in FHIR

In this article, we’ll take a closer look at element identifiers in FHIR, the relevant changes introduced by STU3 and the reasons that motivated that change.

FHIR has supported element identifiers since DSTU1. They are intended to specify unique identifiers for the elements within an individual resource. The official definition from the STU3 specification states: “The id property of the element is defined to allow implementers to build implementation functionality that makes use of internal references inside the resource. This specification does not use the internal id on the element in any way.

Element ids are particularly convenient to identify the individual ElementDefinitions inside of a StructureDefinition. In a basic StructureDefinition without slicing, each element definition has a unique path. However when a profile introduces slicing constraints, element paths are no longer unique, as the following example demonstrates:

Element name Slice name Element path
Patient Patient
identifier Patient.identifier
identifier ssn Patient.identifier
system Patient.identifier.system
identifier ehr Patient.identifier
system Patient.identifier.system

Clearly, the element path itself is not sufficient to uniquely identify individual element definitions, as we also require information about slice name(s). But if we would also include the slice name(s) in the expression, then the resulting value is no longer ambiguous and becomes a unique identifier:

Element name Slice name Element identifier
Patient Patient
identifier Patient.identifier
identifier ssn Patient.identifier:ssn
system Patient.identifier:ssn.system
identifier ehr Patient.identifier:ehr
system Patient.identifier:ehr.system

In order to support this, FHIR STU3 introduces some changes in the definition of element ids. The following table compares the specification of element ids in different FHIR versions:

FHIR version data type max length
DSTU 1 id 36 characters
DSTU 2 id 64 characters
STU 3 string 1MB

In STU3, the element identifier datatype has changed from id to string, effectively removing the maximum length constraint. Also, STU3 allows any string value that does not contain spaces, whereas in earlier versions, the set of valid characters was limited to A-Z | a-z| 0-9 | - | .

FHIR does not specify a mandatory format for element identifiers. In STU3, any unique non-empty string value without spaces is considered to be a valid identifier. However STU3 does introduce a preferred format for the identifiers of ElementDefinitions in a StructureDefinition resource:

"elementName[:sliceName].elementName[:sliceName]..."

Similar to the element path, the preferred identifier format specifies a number of path segments, separated by a dot “.” character. Each segment represents an individual element in the hierarchy and starts with the element name, optionally followed by a semicolon “:” character and the associated slice name (if not empty).

The preferred element identifier value only depends on the position of the element in the element tree. Different available representations of a specific ElementDefinition in both the differential and the snapshot component share the exact same identifier value. As the astute reader may have already noticed, this implies that element identifiers in the preferred format are actually not fully unique within the context of the containing StructureDefinition resource. Also, if we include multiple StructureDefinitions in a Bundle resource, then ElementDefinition identifiers of are not guaranteed to be unique within the context of the containing Bundle resource.

Note: When a FHIR resource is serialized to the XML representation, FHIR element identifiers are expressed as xml:id attributes. According to the W3C specification, “the [identifier] value is unique within the XML document”. So in fact, the FHIR specification violates the W3C XML specification… However in practical situations, this idiosyncrasy of FHIR shouldn’t pose an issue.

In general, software cannot assume that FHIR element identifiers follow the preferred format. The FHIR specification itself does not use the internal id on the element in any way (1). For ElementDefinitions contained in a StructureDefinition resource, the element name and the slice name remain to be the leading identifying attributes for processing logic to act on. This also implies that a sparse differential component should always include parent elements with a non-empty slice name, even if they are unconstrained. In theory, processing logic could reconstruct the parent element hierarchy by parsing the element identifiers in the differential component, provided that all identifiers are specified in the preferred format. However as the preferred identifier format is not required, generic logic cannot rely on this information.

Nonetheless, the standard open source Java and the .NET libraries for FHIR STU3 both provide implementations of a snapshot generator that can generate element ids in the preferred format. So within a clearly defined use context that guarantees standardized element identifiers to be present, e.g. because all snapshots are always (re-)generated by a standard FHIR library, it becomes possible to implement processing logic that acts on the standard identifiers.

Forge, the FHIR profile editor, introduced preliminary support for element identifiers as part of the initial STU3 release from May 2017. Initially, Forge allowed users to specify custom identifiers, however this feature has been deprecated since. As of release 16.4 for STU3, Forge will automatically generate element identifiers in the preferred format on all ElementDefinitions in both the differential and snapshot components. Users can not manually edit the generated identifiers.

1. ^ As Grahame points out in his comment, the FHIR standard actually does use element id’s as the target of content references. However these references do not rely on the format of the identifier.

Introducing Forge for STU3

DutchKingWedding

Royal tears of joy at the official STU3 release ceremony

Today marks the community release of Forge for FHIR STU3. A joyful moment, right after our national Dutch King’s day and just in time for the upcoming HL7 WGM in Madrid.

Hoera! Hoera! Hoera!

You can download Forge, the FHIR profile editor, for free from the FHIR profile registry at simplifier.net. In this article we’ll introduce you to the new release and try to answer some common questions about migration to STU3.

Side by side installation

The new Forge for STU3 ClickOnce installer package has a new application identity that allows side by side installation. This means that you can install and use Forge for STU3 next to an existing installation of Forge for DSTU2 on the same machine. Each release will use it’s own version of the FHIR core resource definitions and manage it’s own application configuration settings separately.

dutch-king-willem-alexander-with-wife-maxima-and-animal-pelt

Side by side installation

As of now, future application updates will target the Forge STU3 release. You can still use Forge for DSTU2 and remain to keep using it for as long as you need. But from now on, the Forge DSTU2 branch will no longer be actively developed as our development efforts are now focused on the STU3 branch. We will remain to provide support for the final Forge DSTU2 release to our commercial customers until they have migrated to STU3.

Profiling changes in STU3

FHIR STU3 introduces a lot of updated and new resources and datatypes. Changes to the conformance resources have the largest impact on profiling. Fortunately, the main FHIR conformance resources are relatively mature and haven’t changed drastically, with one notable exception in the terminology space: CodeSystem has been separated from ValueSet. However this change has no impact on Forge.

You will notice some relatively minor changes to the StructureDefinition resource and the ElementDefinition datatype. Some properties have been renamed and there are a couple of new properties, e.g. to define FHIRPath expressions.

One of the more notable changes involves ElementDefinition.type.profile. In DSTU2, an element type can define multiple profiles. However there are some complications with this approach:

  1. Multiple type profiles pose a problem for dynamic code generation, UI input form generation etc.
    Note that although Forge for DSTU2 (secretly…) supports specifying multiple profiles per type, all known FHIR DSTU2 implementations actually only consider the primary type profile. Practical use of this technique has been actively discouraged by the FHIR infrastructure group. We are not aware of any customers to rely on profiles having multiple type profiles.
  2. When an element has type ResourceReference, we would like to be able to express constraints on the reference target. However ElementDefinition.type.profile expresses constraints on the ResourceReference itself.

FHIR STU3 solves these problems by:

  1. Introducing ElementDefinition.type.targetprofile wich applies when the type code equals ResourceReference and expresses a constraint on the reference target. As before, you can use ElementDefinition.type.profile to constrain the ResourceReference datatype itself.
  2. Limiting ElementDefinition.type.profile max cardinality to 1. This takes away any ambiguity for e.g. code/UI generation.

FHIR STU3 also introduces a lot of changes to the other (clinical) resources and data types. Forge for STU3 includes a new STU3-specific version of specification.zip that contains updated definitions for all the existing and newly introduced resources and data types in STU3.

Migration to STU3

As FHIR STU3 introduces breaking changes to StructureDefinition and ElementDefinition, Forge for STU3 is not compatible with existing DSTU2 profiles.

It is possible to automatically convert existing DSTU2 StructureDefinition resources to the new STU3 format. However an automatic conversion tool can only update the meta information in the profile. It cannot safely convert the actual profiling constraints. Because STU3 introduces changes to many of the resources, some of the constraints in a DSTU2 profile will no longer be valid when converted to STU3. For example, when a DSTU2 profile constrains an element that has been renamed/removed in the corresponding STU3 resource. Therefore migrating profiles from DSTU2 to STU3 will usually require manual review and corrections.

We have a team of experienced consultants with a track record in FHIR profiling who can assist and support you with smooth profile conversion to STU3. Feel free to contact us for a quote or if you want to learn more about our professional FHIR consultancy services.

King-Willem-Alexander-Queen-Maxima-Princesses-Catharina-Amalia

Dutch royal family is said to be “super excited” about FHIR STU3

Looking back

We’ve come a long way since the first release of Forge. The initial Forge DSTU1 release attempted to provide a reference implementation of the FHIR standard. Our team tried to demonstrate that the basic FHIR (profiling) principles could indeed be implemented and put to use. Forge was originally designed to hide the user from the complexity of the underlying XML/JSON wire representation. The novel UI design quickly drew a small but growing user base and succeeded to stimulate both the adoption as well as the development of the FHIR standard itself.

After updating Forge to DSTU2, we focused our efforts on improving the quality of the generated differential and snapshot components. Implementing a proper and fully capable snapshot generator in the .NET FHIR API library proved to be a (royal pain in the ***) challenging and time consuming undertaking that claimed a great deal of development time during 2016. But those efforts eventually drove the implementation of some very powerful profiling features in Forge, such as support for derived profiles, deep inline constraints on complex extensions and dynamic expansion of choice type elements and properties. It took a fair bit of time and effort to stabilize some of the more complex features. But thanks to feedback from our users and from the FHIR profiling community, we managed to solve most of the reported issues.

Moving from DSTU2 to STU3, we have noticed a clear trend in Forge issue reports where they slowly but steadily involve more and more complex and rare profiling use cases. This indicates that basic FHIR profiling features have indeed matured and stabilized and that our users are starting to apply more advanced profiling techniques. And while these newly reported issues may not always be easy to solve, generally they do seem to confirm that FHIR profiling is proving to be effective and productive!

Looking forward

Now that we have updated our full tooling stack to STU3 (The official FHIR .NET API library; Forge, the FHIR profile editor; simplifier.net, the FHIR profile registry; and last but not least Vonk, our new enterprise-grade FHIR server), we are looking forward to unleash them onto the FHIR community, starting at the upcoming HL7 WGM in Madrid.

Our simplifier.net platform now supports STU3, but will also remain to serve existing DSTU2 profiles. We will cooperate with our Simplifier users in order to provide migration strategies to update existing DSTU2 projects to STU3. Under the hood, our team has updated the simplifier.net environment to the new Microsoft .NET Core platform, which brings a significant performance increase, improved response times and higher scalability. We are experimenting with the new Microsoft Orleans platform, an exciting new cloud-based technology, in order to implement scalable validation support and initial results are quite promising. This will allow our simplifier.net platform to provide computationally intensive features to an ever increasing and demanding user base.

As Forge has been updated to STU3 and most of the underlying complex profiling logic is finished and maturing, we can now focus our development efforts towards improving the user experience, file management, online simplifier integration and profiling workflow support. We have received a lot of great suggestions from the community about possible improvements and new features that will allow us to further improve the application. Your feedback remains very valuable to us, so please don’t hesitate to submit issue reports and/or feature requests! Should you have a need for commercial support, then you may benefit from one of our simplifier.net account plans.

Last but not least, our brand new Vonk server is now ready for STU3. Vonk is a modular and highly scalable FHIR server based on the new Microsoft .NET core platform. Aside from performance improvements, the new platform also enables some surprising new applications, as recently one of our bright engineers successfully managed to spin up a Docker image of Vonk server running .Net core on Linux. The new flexible options for host operating systems ensure that our Vonk server will smoothly integrate into any existing customer infrastructure.

Final thoughts

Kings day

Millions join the Forge STU3 release party, Amsterdam 2017

We hope that you enjoy the new Forge STU3 release, as well as all our other FHIR tools. The Furore FHIR team celebrated this important milestone in Amsterdam on our King’s birthday, which traditionally involves orange outfits, DJ-powered canal boat rides and abundant substance abuse. And now that the orange smoke has cleared up, we’re back making FHIR simple.

 

Download Forge for STU3 for free from simplifier.net

Happy profiling and long live the King!

 

Zo waerlijck helpe my Forge almechtig

 

Forge

Forging Profiles on Profiles

Introduction

Recently (Friday November 25th) we published a new Forge release, Forge 14.4 for DSTU2 – FHIR DevDays 2016 edition. You can download the new Forge release for free from Simplifier.net. The previous Forge release (13.2) dates from June 10th, so clearly it’s been a while since we’ve published an update. In this blog post I’ll elaborate on the most important new feature: support for profiles on profiles, also known as derived profiles.

What is a derived profile?

Out of the box FHIR resources are designed to cover 80% of the common use cases. In order to cover the remaining 20%, FHIR introduces profiles. A FHIR profile defines a set of constraints on a standard FHIR resource or datatype. This allows you to define a specialized variant of a standard resource for a specific use case. And because profiles are based on common FHIR resources, they provide a certain basic level of global interoperability with other (external) FHIR systems.

Obviously, as we limit the context to a specific country, region or organization, we can probably harmonize more aspects and provide increased interoperability within that limited context. FHIR supports this common use case by allowing you to define so-called profiles on profiles a.k.a. derived profiles. A derived profile defines a set of constraints on another existing profile, the so-called base profile. The derived profile implicitly inherits all the existing constraints from the associated base profile. A derived profile can only further constrain the base profile, similar to profiles on core resources.

Example:

  • The HL7 affiliate for the Netherlands publishes a national profile for a Dutch patient. The profile is based on the standard FHIR Patient resource and defines a number of additional constraints, e.g. in order to identify Dutch patients by their unique national social security number (BSN). The profile also constrains the generic Patient.careProvider reference to a specific Dutch practitioner profile.
  • A Dutch hospital authors a derived Patient profile for use within the organization. The organizational Patient profile is based on the national Dutch patient profile and defines some additional constraints that only apply within the organization. The hospital’s custom Patient profile defines additional constraints for the unique Patient identifier that is assigned by the hospital EHR system. The profile also prohibits the Patient.animal element, as it does not apply.
Hierarchy of Profiles

Hierarchy of profiles

As the example demonstrates, this allows you to create a hierarchy of profiles, e.g. on a national, regional, organizational and/or departmental level. As we navigate deeper down the profile hierarchy, we encounter more specific profiles for more limited use contexts.

Authoring a derived profile in Forge

New Derived ProfileUsing the new Forge 14.4 release, you can now easily author a derived profile. Forge 14.4 introduces a new command “New Derived Profile“. You can find the new command directly on the start screen, in the File menu and also in the Session Explorer toolbar. The new command resembles the existing “New Profile” command, but with one important difference: you can select an existing profile as the underlying base profile. Forge will display a File Open dialog window to select a suitable base profile from disk. After selecting the base profile, Forge will create and display the new derived profile.

For example, let’s assume that Mayo Clinic wants to create their own derived Patient profile based on the US DAF Patient profile which defines a set of national constraints for the US Patient. Below, we can see the original DAF Patient base profile loaded in Forge. All the constraints defined by the base profile are clearly indicated by the yellow pen icon:

DAF Patient Profile

Notes:

  • The blue exclamation icon indicates a “must support” element
  • The blue paperclip icon indicates extension elements
  • The yellow pen icon indicates a constrained element

Now Mayo Clinics creates a new derived Patient profile based on the DAF Patient profile. The new derived Patient profile inherits all constraints that are defined by the base DAF Patient profile. Initially, the new derived profile does not introduce any new constraints, as is clearly visible in the Forge Element Tree:

Mayo Clinic Patient Profile

The screenshot demonstrates that the derived Patient profile indeed inherits all the constraints defined by the base DAF Patient profile (e.g. you can see the inherited extensions such as ethnicity). Because the implicitly inherited constraints are not defined by the derived profile itself, they are not considered to be part of it. This is clearly visible in the Forge element tree, as none of the elements in the new derived profile show a yellow pen icon (except on the root element, because the derived profile has a new name & url). In other words, the new derived profile shown above is (almost) empty.

Now Mayo Clinic can define additional specific constraints in the derived profile. Any newly defined constraints will actually be part of the derived profile and consequently they will be indicated by a yellow pen in Forge. For example, the screenshot below shows how the derived profile defines an additional constraint on the Patient.photo element:

Mayo Clinic Patient Profile with constraint on photo

Below you can see the serialized XML of the derived Patient profile. We can clearly see the newly introduced constraint on the Patient.photo element. Note that the root element definition is mandatory in DSTU2, even if the element is not constrained. This requirement will be deprecated in STU3. Also note the (mandatory) reference to the underlying DAF Patient base profile.

<?xml version="1.0" encoding="utf-8"?>
<StructureDefinition xmlns="http://hl7.org/fhir">
  <meta>
    <lastUpdated value="2016-12-01T16:47:22.41+01:00" />
  </meta>
  <url value="StructureDefinition/MayClinicPatientProfile" />
  <name value="Mayo Clinic Patient Profile" />
  <status value="draft" />
  <date value="2016-12-01T16:28:17+01:00" />
  <fhirVersion value="1.0.2" />
  <kind value="resource" />
  <constrainedType value="Patient" />
  <abstract value="false" />
  <base value="http://hl7.org/fhir/StructureDefinition/daf-patient" />
  <differential>
    <element>
      <path value="Patient" />
    </element>
    <element>
      <path value="Patient.photo" />
      <max value="0" />
    </element>
  </differential>
</StructureDefinition>

Conclusion

This concludes our introduction of the new support for derived profiles in Forge. The example demonstrates that the authoring process is actually quite easy for end users and quite similar to creating profiles on core resources. After all, in FHIR we try to move complexity away to the developers of tools and servers.

As simple as this all seems to the end user, the actual implementation entailed a long and winding road straight into the bowels of the FHIR specification. In a separate blog post, I will provide some more in-depth background information about the technical challenges we faced when trying to implement support for derived profiles, and how we addressed these.

If you have questions concerning (derived) profiles or need a bit of assistance, then you are strongly encouraged to join the online FHIR discussion groups. The FHIR community is very active, friendly and helpful and usually you quickly receive helpful responses from FHIR users all over the world. The conformance stream is specifically intended to discuss FHIR profiling and related tools including Forge. Also if you’ve implemented FHIR in a production environment, then please inform the FHIR community and share your story in the implementers stream!

Two final notes:

  1. Today we have published a minor hotfix release (14.4.1) with a couple of bugfixes. Should you encounter any issues while trying to repeat the exercise above, then please make sure that you have upgraded to the most recent version. Users of release 14.4 will receive an auto-update notification. Users of earlier releases should manually download and install the new version.
  2. The new Forge release does not yet support base profiles containing slices. If you select a base profile containing slices, snapshot generation will fail and generate a runtime exception. We will introduce support for sliced base profiles in a future application update.

Happy profiling!

Michel Rutten