Artigo
· 4 hr atrás 12min de leitura

Implementando um projeto FHIR - ÚNICAS

Boas-vindas, estimados membros da Comunidade!


Neste artigo, apresentaremos um exemplo de um projeto que implementa uma solução baseada em FHIR. Este projeto se baseará no projeto nacional (espanhol), conhecido como ÚNICA

O que é ÚNICAS?

Em suas próprias palavras:

Um projeto cujo objetivo é criar um ecossistema de colaborações para melhorar a atenção à saúde de pacientes pediátricos com Doenças Raras Complexas (DRCs). Este projeto está sendo implementado por meio da rede dentro do Sistema Nacional de Saúde (SNS) para melhorar o diagnóstico e o cuidado de pacientes com doenças raras.

Características Técnicas de ÚNICAS

Em resumo, a arquitetura projetada para este projeto é composta por:

Nó Central 

O objetivo é permitir o compartilhamento de dados clínicos de pacientes afetados por essas doenças raras. Esses dados clínicos serão encontrados nos nós regionais.

Nó Autônomo

Cada comunidade autônoma terá seu próprio nó (ou mais de um, dependendo do volume de dados). Esses nós serão responsáveis por obter as informações clínicas dos pacientes das diversas fontes de dados disponíveis e fornecê-las ao nó central.

Modelo de Interoperabilidade

O modelo de interoperabilidade escolhido para este caso é o FHIR (R5), que fornece um conjunto de recursos padronizados para a troca de dados clínicos.

Cada comunidade autônoma é livre para escolher como implementar este modelo, seja implantando um repositório FHIR no qual carregar os dados clínicos para posterior compartilhamento, ou utilizando uma fachada FHIR (FHIR facade) que transformará a informação clínica em recursos FHIR quando um pedido de partilha for processado.

Recursos FHIR em ÚNICAS

Visto que este projeto tem objetivos muito específicos, ele definiu um conjunto de recursos FHIR a serem utilizados:

Mensajería estandardizada en ÚNICAS en FHIR

Enquanto para relatórios:

Mensajería existente de informes en HL7 CDA

Implementando ÚNICAS com Health Connect

Após revisar os aspectos técnicos do projeto ÚNICAS, é hora de examinar como ele se encaixa com o Health Connect e quais possíveis adaptações seriam necessárias para facilitar a implementação.

Modelo FHIR R5 

Para implementar o modelo de interoperabilidade em FHIR R5, o cliente tem duas opções:

  • FHIR Facade.
  • FHIR Repository.

FHIR Facade

O FHIR facade implantará os componentes de negócio necessários para uma API REST dentro de nossa instância do Health Connect que nos permitirá receber chamadas de sistemas de terceiros com os vários recursos FHIR. Com esta opção, podemos extrair os recursos no formato %DynamicObject e transformá-los em nosso formato específico de dados clínicos.

As consultas recebidas contra a fachada FHIR devem ser construídas manualmente, uma vez que não há um repositório FHIR vinculado abaixo, então devemos implementar cada recurso nós mesmos. Para o ÚNICAS, esta não seria a melhor opção, visto que os requisitos em nível de consulta nos exigiriam implementar a funcionalidade que já temos no repositório FHIR.

FHIR Repository

O Health Connect permite (consultar condições de licença) implantar um repositório FHIR R5 de uma forma simples a partir de um namespace com a opção de interoperabilidade habilitada a partir do menu Health -> FHIR Server Management

Ao clicar na opção de adicionar novo servidor, será mostrada uma tela com as diversas opções de configuração:

Como você pode ver, temos a opção de importar Pacotes (Packages) em nossa configuração. Isso será útil para importar os Conjuntos de Valores (ValueSets) e Perfis (Profiles) definidos pela ÚNICAS IG. Podemos fazer essa importação a qualquer momento rodando o seguinte comando:

// 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"))

 

No ÚNICAS IG, existe uma pequena discrepância acerca das dependências definidas para o full-IG (IG completo). As seguintes dependências estão listadas no pacote:

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

A versão 1.1.0 de hl7.fhir.uv.ips é suportada apenas no FHIR R4, e tentar importá-la para um servidor R5 resultará em um erro, pois também possui várias dependências R4 que causarão incompatibilidades. Para nossa implementação, vamos removê-la da lista de dependências e importar apenas aquelas relacionadas ao R5... e que Deus cuide disso.

Adaptando o repositório FHIR ao ÚNICAS

Uma das características mais interessantes do repositório FHIR no Health Connect é a capacidade de usar o motor de interoperabilidade para capturar e transformar cada interação com o repositório FHIR para adaptá-la às nossas necessidades. Para fazer isso, só precisamos incluir o business service HS.FHIRServer.Interop.Service e a business operation HS.FHIRServer.Interop.Operation do ambiente de produção vinculado ao namespace onde implantamos o repositório FHIR. Esta será a aparência do ambiente de produção para o namespace que contém nosso repositório:

Com estes componentes em nossa produção, podemos especificar em nossa configuração de repositório que todas as chamadas usando a API REST passarão por este serviço:

Perfeito! Nosso repositório FHIR R5 está pronto para começar a implantar nosso nó autônomo ÚNICAS. Vamos ver um exemplo de como podemos aproveitar as capacidades de interoperabilidade para transformar mensagens HL7 v2.5.1 em recursos FHIR R5.

Convertendo HL7 para FHIR

Um caso típico a ser resolvido para qualquer implementador de repositório FHIR é a conversão de mensagens HL7 (v2 ou v3) em recursos FHIR. Esta conversão não é nada direta por duas razões:

  1. Não há equivalência direta entre recursos FHIR e eventos HL7. Enquanto o FHIR é usado para a troca de conceitos clínicos na forma de recursos, o HL7 representa eventos, que contêm informações que podem ou não ser equivalentes a um conceito clínico.
  2. A implementação de mensagens HL7 v2 ou v3 está longe de ser uniforme. Cada organização adapta as mensagens às suas necessidades, e o mesmo evento em diferentes organizações pode conter dados completamente diferentes.

Que ferramentas o Health Connect nos fornece?

O Health Connect possui uma série de transformações predefinidas que utilizam o modelo de dados SDA3 como gateway, muito semelhante aos CDAs. Estas transformações estão atualmente desenvolvidas para a versão R4, mas não é muito complicado adaptá-las para transformar recursos R4 gerados na versão R5.

Portanto, os passos a seguir serão HL7 v2.5.1 -> SDA3 -> FHIR R4 -> FHIR R5. 

Transformação HL7 v2.5.1 -> SDA3

Para este primeiro passo, usaremos a classe HS.Gateway.HL7.HL7ToSDA3 e seu método GetSDA (você pode ver um exemplo na documentação aqui ). Esta classe transformará nossa mensagem HL7 em um bom SDA.

Transformação SDA3 -> FHIR R4

Uma vez obtido nosso SDA com as informações clínicas extraídas da mensagem HL7, usaremos a classe HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR e seu método TransformStream, que transformará o SDA3 em formato Stream obtido na etapa anterior em um %DynamicObject com um recurso FHIR R4 Bundle (mais informações
 aqui ).

Abaixo você pode ver um exemplo do código necessário para transformar uma mensagem HL7 em um bundle FHIR R4 no formato %DynamicObject:

 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")

E quanto ao meu FHIR R5?

Até agora vimos que o Health Connect permite uma conversão muito simples de HL7 para FHIR R4 (a conversão pode não se adequar 100% às suas necessidades, mas será mais fácil editar um recurso do que construí-lo do zero), ótimo, mas... ONDE ESTÁ O MEU RECURSO R5?

Não se preocupe, porque... a Comunidade de Desenvolvedores está vindo ao resgate!

Você verá que anexado a este artigo está um aplicativo OpenExchange associado. Este aplicativo contém o código necessário para transformar recursos FHIR R4 nos recursos R5 usados no projeto ÚNICAS. Para instalar este código, você pode usar o 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"

Uma vez instalado, você verá um pacote chamado Spain aparecer no namespace onde você o instalou. O que você pode encontrar neste pacote?

  • Spain.BPBPL com exemplo de transformação HL7 -> SDA -> FHIR R4 -> FHIR R5.
  • Spain.FHIR.DTL.vR4Transformações de R4 para R5 dos recursos usados pelo ÚNICAS.
  • Spain.FHIR.DTL.vR5: Classes ObjectScript que modelam recursos ÚNICAS R5.
  • Spain.Gateway.HL7: Pequena correção para transformação HL7 para SDA3.

Transformando R4 para R5

Com as ferramentas disponíveis no pacote Spain, agora podemos transformar nossos recursos R4 em recursos R5. Para fazer isso, usaremos as transformações para cada tipo de recurso da seguinte forma:

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)            
    }
    ...
 }

Lembre-se de que tínhamos um %DynamicObject com um bundle de recursos R4, o primeiro passo que daremos será mapear dito %DynamicObject para uma classe HS.FHIR.DTL.vR4.Model.Resource.Bundle usando o método FromJSONHelper, o objetivo desta transformação é poder posteriormente reconhecer o tipo de recurso presente no bundle pelo nome de sua classe.

Com o bundle mapeado para sua classe ObjectScript, simplesmente percorreremos cada entrada, identificando o recurso. Se corresponder a um dos recursos utilizados no ÚNICAS, procedemos a invocar uma DTL específica para realizar a transformação de R4 para R5.

Com o recurso já transformado, o reintroduzimos no Bundle substituindo o recurso R4 anterior.

Finalmente, só teremos que transformar o Bundle em um Stream (neste caso chamado QuickStream) e enviá-lo dentro de uma mensagem do tipo HS.FHIRServer.Interop.Request para a 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)
 

Aqui teríamos a chamada:

Bem, é isso! Agora temos nosso pequeno exemplo de conversões de HL7 para R5 e seu registro em nosso repositório Health Connect. Vejamos em ação.

Exemplo de transformação de HL7 para R5

Vamos começar com uma mensagem HL7 v2.5.1

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

Aqui temos um simples ADT_A08 com informações relacionadas a um paciente e suas alergias, como implementadores diligentes que somos, sabemos que esta mensagem conterá (pelo menos) um recurso do tipo Patient (Paciente) e 3 do tipo AllergyIntolerance (Alergia e Intolerância).

Introduzimos um serviço de negócio em nossa produção para capturar arquivos HL7 e enviá-los ao nosso BPL:

Uma vez capturado, transformamos nosso HL7 para R4 através de SDA, vamos dar uma olhada no recurso AllergyIntolerance gerado em 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"
							}
						]
 
					}
				]
			}
		]
	}
}

E agora o mesmo recurso, mas adaptado à versão R5:

{
	"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"
								}
							]
						}
					}
				]
			}
		]
	}
}

Como você pode ver, a propriedade reaction foi modificada, adaptando-a à definição R5:

Conclusão

Como você viu, o Health Connect oferece infinitas possibilidades para se adaptar aos diversos projetos baseados em FHIR que podemos encontrar. Com o motor de interoperabilidade, podemos adaptar nossos dados da forma que precisarmos, levando em conta quaisquer requisitos que se desviem do comportamento padrão.

Espero que você ache útil!

Discussão (0)1
Entre ou crie uma conta para continuar