Article
· 8 hr ago 11m read

Implementing a FHIR project - ÚNICAS

Welcome, dear members of the Community!

In this article, we will present an example of a project implementing a FHIR-based solution. This project will be based on the national project (Spanish national project), known as ÚNICAS.

What is ÚNICAS?

In his own words:

A project whose objective is to create an ecosystem of partnerships to improve healthcare for pediatric patients with complex rare diseases (RMDs). This project is being implemented through the network within the National Health System (NHS) to improve the diagnosis and care of patients with rare diseases.

Technical characteristics of ÚNICAS

To summarize, the architecture designed for this project is composed of:

Central node

The objective is to enable the sharing of clinical data on patients affected by these rare diseases. This clinical data will be found in the regional nodes.

Autonomous node

Each autonomous community will have its own node (or more than one, depending on the volume of data). These nodes will be responsible for obtaining patients' clinical information from the various available data sources and providing it to the central node.

Interoperability model

The interoperability model chosen for this case is FHIR (R5), which provides a set of standardized resources for the exchange of clinical data.

Each autonomous community is free to choose how to implement this model, either by deploying a FHIR repository into which to upload clinical data for subsequent sharing or by using a FHIR facade that will transform clinical information into FHIR resources when a sharing request is processed.

FHIR resources in ÚNICAS

Since this project has very specific objectives, it has defined a set of FHIR resources to be used:

Mensajería estandardizada en ÚNICAS en FHIR

While for reports:

Mensajería existente de informes en HL7 CDA

Implementing ÚNICAS with Health Connect

After reviewing the technical aspects of the ÚNICAS project, it's time to examine how it fits with Health Connect and what possible adaptations would be necessary to facilitate implementation.

FHIR R5 Model

In order to implement the interoperability model in FHIR R5, the client has two options:

  • FHIR Facade.
  • FHIR Repository.

FHIR Facade

The FHIR facade will deploy the business components necessary for a REST API within our Health Connect instance that allows us to receive calls from third-party systems with the various FHIR resources. With this option, we can extract the resources in %DynamicObject format and transform them into our specific clinical data format.

Queries received against the FHIR facade must be built manually, since there is no FHIR repository linked below, so we must implement each feature ourselves. For ÚNICAS, this would not be the best option, since the query-level requirements would require us to implement the functionality we already have in the FHIR repository.

FHIR Repository

Health Connect permite (consultar condiciones de la licencia) desplegar un repositorio FHIR R5 de una forma sencilla desde un namespace con la opción de interoperabilidad habilitada desde el menú Health -> FHIR Server Management

Al pulsar sobre la opción de añadir nuevo servidor se nos mostrará una pantalla con las diversas opciones de configuración:

As you can see, we have the option to import Packages into our configuration. This will be useful for importing the ValueSets and Profiles defined by the ÚNICAS IG. We can perform this import at any time by running the following command:

// Rutas a modificar por el usuario
do ##class(HS.FHIRMeta.Load.NpmLoader).importPackages($lb("/iris-shared/packages/hl7.terminology.r5-6.5.0/package", "/iris-shared/packages/hl7.fhir.uv.extensions.r5-5.2.0/package","/iris-shared/packages/full-ig/package"))

In the ÚNICAS IG, there is a small discrepancy regarding the dependencies defined for the full-IG . The following dependencies are listed within the package:

"dependencies" : {
    "hl7.terminology.r5" : "6.5.0",
    "hl7.fhir.uv.extensions.r5" : "5.2.0",
    "hl7.fhir.uv.ips" : "1.1.0"
  }

Version 1.1.0 of  hl7.fhir.uv.ips  is only supported on FHIR R4, and attempting to import it onto an R5 server will result in an error, as it also has several R4 dependencies that will cause incompatibilities. For our implementation, we'll remove it from the dependency list and import only those related to R5...and let God take care of it.

Adapting FHIR repository to ÚNICAS

One of the most interesting features of the FHIR repository in Health Connect is the ability to use the interoperability engine to capture and transform every interaction with the FHIR repository to adapt it to our needs. To do this, we only need to include the business service  HS.FHIRServer.Interop.Service  and the business operation  HS.FHIRServer.Interop.Operation from the production environment linked to the namespace where we have deployed the FHIR repository. This will be the appearance of the production environment for the namespace containing our repository:

With these components in our production, we can specify in our repository configuration that all calls using the REST API will go through this service:

Perfect! Our FHIR R5 repository is ready to begin deploying our ÚNICAS autonomic node. Let's look at an example of how we can leverage interoperability capabilities to transform HL7 v2.5.1 messaging into FHIR R5 resources.

Converting HL7 to FHIR

A typical case to be solved for any FHIR repository implementer is the conversion of HL7 (v2 or v3) messaging into FHIR resources. This conversion is not at all straightforward for two reasons: 

  1. There is no direct equivalence between FHIR resources and HL7 events. While FHIR is used for the exchange of clinical concepts in the form of resources, HL7 represents events, which contain information that may or may not be equivalent to a clinical concept.
  2. The implementation of HL7 v2 or v3 messaging is far from uniform. Each organization adapts messages to its needs, and the same event in different organizations may contain completely different data.

What tools does Health Connect provide us?

Health Connect has a series of predefined transformations that use the SDA3 data model as a gateway, very similar to CDAs. These transformations are currently developed for the R4 version, but it is not very complicated to adapt them to transform R4 resources generated in the R5 version.

So the steps to follow will be HL7 v2.5.1 -> SDA3 -> FHIR R4 -> FHIR R5. 

HL7 v2.5.1 Transformation  -> SDA3

For this first step, we'll use the  HS.Gateway.HL7.HL7ToSDA3 class  and its  GetSDA method  (you can see an example in the documentation here ). This class will transform our HL7 message into a nice SDA.

Transformation SDA3 -> FHIR R4

Once we have obtained our SDA with the clinical information extracted from the HL7 message, we will use the  HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR class  and its TransformStream  method , which will transform the SDA3 in Stream format obtained in the previous step into a %DynamicObject with a FHIR R4 Bundle resource (more information here ).

Below you can see an example of the code needed to transform an HL7 message into a FHIR R4 bundle in %DynamicObject format:

 do ##class(HS.Gateway.HL7.HL7ToSDA3).GetSDA(request, .SDAMessageTemp)
 set SDAToFHIR = #class(HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR).TransformStream(SDAMessageTemp,"HS.SDA3.Container","R4")

What about my FHIR R5?

So far we have seen that Health Connect allows for a very simple conversion from HL7 to FHIR R4 (the conversion may not fit 100% of your needs, but it will be easier to edit a resource than to build it from scratch), great, but...WHERE IS MY R5 RESOURCE?

Don't worry, because... the Developer Community is coming to the rescue!

You'll see that attached to this article is an associated OpenExchange application. This application contains the code necessary to transform FHIR R4 resources into the R5 resources used in the ÚNICAS project. To install this code, you can use IPM:

set version="latest" s r=##class(%Net.HttpRequest).%New(),r.Server="pm.community.intersystems.com",r.SSLConfiguration="ISC.FeatureTracker.SSL.Config" d r.Get("/packages/zpm/"_version_"/installer"),$system.OBJ.LoadStream(r.HttpResponse.Data,"c")
zpm "enable -community"
zpm "install spain-unicas"

Once installed, you'll see a package called Spain  appear in the namespace where you installed it. What can you find in this package?

  • Spain.BP : BPL with example of transformation HL7 -> SDA -> FHIR R4 -> FHIR R5.
  • Spain.FHIR.DTL.vR4 : R4 to R5 transformations of the resources used by ÚNICAS.
  • Spain.FHIR.DTL.vR5 : ObjectScript classes that model ÚNICAS R5 resources.
  • Spain.Gateway.HL7 : Minor fix for HL7 to SDA3 transformation.

Transforming R4 to R5

With the tools available in the Spain package  , we can now transform our R4 resources into R5 resources. To do this, we will use the transformations for each resource type as follows:

set obj = ##class(HS.FHIR.DTL.vR4.Model.Resource.Bundle).FromJSONHelper(context.FHIRMessage, "vR4")

for i=1:1:obj.entry.Count() {
    set item = obj.entry.GetAt(i)
    if ($CLASSNAME(item.resource) = "HS.FHIR.DTL.vR4.Model.Resource.Immunization")
    {
      Set status = ##class(Spain.FHIR.DTL.vR4.vR5.Immunization).Transform(item.resource, .immunizationR5)            
      Set item.resource = immunizationR5
      Do obj.entry.SetAt(item, i)            
    }
    elseif ($CLASSNAME(item.resource) = "HS.FHIR.DTL.vR4.Model.Resource.AllergyIntolerance")
    {
      Set status = ##class(Spain.FHIR.DTL.vR4.vR5.Allergy.AllergyIntolerance).Transform(item.resource, .allergyIntoleranceR5)            
      Set item.resource = allergyIntoleranceR5
      Do obj.entry.SetAt(item, i)            
    }
    ...
 }

Remember that we had a %DynamicObject with a bundle of R4 resources, the first step we will take will be to map said %DynamicObject to a  HS.FHIR.DTL.vR4.Model.Resource.Bundle class using the FromJSONHelper method , the objective of this transformation is to be able to later recognize the type of resource present in the bundle by the name of its class.

With the bundle mapped to its ObjectScript class, we will simply go through each entry, identifying the resource. If it corresponds to one of the resources used in ÚNICAS, we proceed to invoke a specific DTL to perform the transformation from R4 to R5.

With the resource already transformed, we reintroduce it into the Bundle replacing the previous R4 resource.

Finally, we will only have to transform the Bundle into a Stream (in this case called QuickStream) and send it within a message of type HS.FHIRServer.Interop.Request  to the business operation HS.FHIRServer.Interop.Operation

 Set context.FHIRRequest.Request.RequestMethod = "POST"
 Set context.FHIRRequest.Request.RequestPath = ""
 Set context.FHIRRequest.Request.RequestFormatCode= "JSON"
 Set context.FHIRRequest.Request.SessionApplication = "/csp/healthshare/fhirserver/fhir/r5"
 Set context.FHIRRequest.Request.IsRecursive = 0
 set qs=##class(HS.SDA3.QuickStream).%New()
 set context.FHIRRequest.QuickStreamId = qs.%Id()
 do qs.CopyFrom(context.FHIRStream)
 

Here we would have the call:

Well, that's it! We now have our small example of HL7 to R5 conversions and its registration in our Health Connect repository. Let's see it in action.

Example of transformation from HL7 to R5

Let's start with an HL7 v2.5.1 message

MSH|^~\&|CLINIC_SYS|HOSPITAL123|EHR_SYSTEM|REGION1|20250826143000||ADT^A08^ADT_A01|MSG00002|P|2.5.1
EVN|A08|20250826143000
PID|1||123456^^^HOSPITAL123^MR||GOMEZ^MARIA^LUISA||19750523|F|||AVDA UNIVERSIDAD 45^^MADRID^^28040^ESP||(555)1234567|||S||12345678Z^^^ESP^NI
AL1|1|DA|70618^Penicillin^RXNORM|SV|Rash|20200115|Clinician noted severe rash after administration
AL1|2|FA|256349002^Peanut protein^SCT|SE|Anaphylaxis|20181201|Reported after accidental exposure
AL1|3|MA|300916003^Latex^SCT|MO|Skin irritation|20191010|Observed during procedure with latex gloves

Here we have a simple ADT_A08 with information related to a patient and their allergies, as diligent implementers that we are, we know that this message will contain (at least) one resource of type Patient and 3 of type AllergyIntolerance. 

We introduced a business service into our production to capture HL7 files and send them to our BPL:

Once captured, we transform our HL7 to R4 through SDA, let's take a look at the AlleryIntolerance resource generated in R4:

{
                                                              
	"request": {
		"method": "POST",
		"url": "AllergyIntolerance"
	},
	"fullUrl": "urn:uuid:4b9f7b2e-9dd0-11f0-870e-c6b593068383",
	"resource": {
		"resourceType": "AllergyIntolerance",
		"category": [
			"food"
		],
		"clinicalStatus": {
			"coding": [
				{
					"code": "active",
					"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical"
				}
			]
		},
		"code": {
			"coding": [
				{
					"code": "256349002",
					"display": "Peanut protein",
					"system": "http://snomed.info/sct"
				}
			]
		},
		"extension": [
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-discovery-time",
				"valueDateTime": "2018-12-01T00:00:00+00:00"
			},
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-entered-at",
				"valueReference": {
					"reference": "urn:uuid:4b9e97f6-9dd0-11f0-870e-c6b593068383"
				}
			}
		],
		"onsetDateTime": "2018-12-01T00:00:00+00:00",
		"patient": {
			"reference": "urn:uuid:4b9edb58-9dd0-11f0-870e-c6b593068383"
		},
		"reaction": [
			{
				"extension": [
					{
						"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-severity",
						"valueCodeableConcept": {
							"coding": [
								{
									"code": "SE"
								}
							]
						}
					}
				],
				"manifestation": [
					{
			
						"coding": [
							{
								"code": "Anaphylaxis"
							}
						]
 
					}
				]
			}
		]
	}
}

And now the same resource but adapted to the R5 version:

{
	"fullUrl": "urn:uuid:4b9f7b2e-9dd0-11f0-870e-c6b593068383",
	"request": {
		"method": "POST",
		"url": "AllergyIntolerance"
	},
													  
	"resource": {
		"resourceType": "AllergyIntolerance",
		"category": [
			"food"
		],
		"clinicalStatus": {
			"coding": [
				{
					"code": "active",
					"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical"
				}
			]
		},
		"code": {
			"coding": [
				{
					"code": "256349002",
					"display": "Peanut protein",
					"system": "http://snomed.info/sct"
				}
			]
		},
		"extension": [
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-discovery-time",
				"valueDateTime": "2018-12-01T00:00:00+00:00"
			},
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-entered-at",
				"valueReference": {
					"reference": "urn:uuid:4b9e97f6-9dd0-11f0-870e-c6b593068383"
				}
			}
		],
		"onsetDateTime": "2018-12-01T00:00:00+00:00",
		"patient": {
			"reference": "urn:uuid:4b9edb58-9dd0-11f0-870e-c6b593068383"
		},
		"reaction": [
			{
				"extension": [
					{
						"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-severity",
						"valueCodeableConcept": {
							"coding": [
								{
									"code": "SE"
								}
							]
						}
					}
				],
				"manifestation": [
					{
						"concept": {
							"coding": [
								{
									"code": "Anaphylaxis"
								}
							]
						}
					}
				]
			}
		]
	}
}

As you can see, the reaction property has been modified, adapting it to the R5 definition:

Conclusion

As you've seen, Health Connect offers endless possibilities for adapting to the various FHIR-based projects we may encounter. With the interoperability engine, we can adapt our data however we need, taking into account any requirements that deviate from standard behavior.

I hope you find it useful!

Discussion (0)1
Log in or sign up to continue