Translation between FHIR and HL7 v2 message models

IPF provides utilities for translation between FHIR and HL7v2, thus giving the possibility to implement FHIR-based IHE transactions on top of their HL7 v2 counterparts and to avoid redundancy in that way.

Currently supported transaction pairs are

Dependencies

In a Maven-based environment, the following dependencies should be registered in pom.xml:

<dependency>
    <groupId>org.openehealth.ipf.commons</groupId>
    <artifactId>ipf-commons-spring</artifactId>
    <version>${ipf-version}</version>
</dependency>
<dependency>
    <groupId>org.openehealth.ipf.platform-camel</groupId>
    <artifactId>ipf-platform-camel-ihe-fhir-r4-pixpdq</artifactId>
    <version>${ipf-version}</version>
</dependency>

<!-- when using FHIR STU3 -->
<!--
<dependency>
    <groupId>org.openehealth.ipf.platform-camel</groupId>
    <artifactId>ipf-platform-camel-ihe-fhir-stu3-pixpdq</artifactId>
    <version>${ipf-version}</version>
</dependency>
-->

This depends transitively on the required module:

<dependency>
    <groupId>org.openehealth.ipf.commons</groupId>
    <artifactId>ipf-commons-ihe-fhir-r4-pixpdq</artifactId>
    <version>${ipf-version}</version>
</dependency>

Configuring the URI Mapper

For translation of FHIR messages, an instance of UriMapper is required in order to map FHIR URIs into OIDs and vice versa. IPF provides an implementation (NamingSystemUriMapper) that uses an instance of NamingSystemService under the hood.

The default implementation is DefaultNamingSystemServiceImpl, which expects a Bundle of FHIR NamingSystem resources. In addition, for code system mapping, a Mapping Service bean must be available. Here is a snippet of the required Spring XML configuration:


    <bean id="fhirContext" class="ca.uhn.fhir.context.FhirContext" factory-method="forR4"/>

    <bean id="mappingService" class="org.openehealth.ipf.commons.spring.map.SpringBidiMappingService">
        <property name="mappingResources">
            <list>
                 <value>classpath:META-INF/map/fhir-hl7v2-translation.map</value>
            </list>
        </property>
    </bean>

    <!-- Use NamingSystemService for the URI Mapper -->

    <bean id="namingSystemService" class="org.openehealth.ipf.commons.ihe.fhir.DefaultNamingSystemServiceImpl">
        <constructor-arg ref="fhirContext"/>
        <property name="namingSystemsFromXml" value="classpath:identifiers.xml"/>
    </bean>

    <bean id="uriMapper" class="org.openehealth.ipf.commons.ihe.fhir.translation.NamingSystemUriMapper">
        <constructor-arg ref="namingSystemService"/>
        <constructor-arg value="identifiers"/>
    </bean>
    

Note that Spring Boot applications can depend on ipf-fhir-spring-boot-starter, which already auto-configures these beans for you.

An example for a bundle of NamingSystem resources (referenced to be contained in the identifiers.xml file in the example above) looks like this:


<Bundle xmlns="http://hl7.org/fhir" >
    <id value="identifiers"/>
    <type value="collection"/>

    <entry>
        <resource>
            <NamingSystem>
                <id value="fhir1"/>
                <name value="FHIR1 Patient Identifier Namespace"/>
                <status value="active"/>
                <kind value="identifier"/>
                <date value="2015-07-31"/>
                <type>
                    <coding>
                        <system value="http://hl7.org/fhir/identifier-type"/>
                        <code value="PI"/>
                    </coding>
                </type>
                <uniqueId>
                    <type value="oid"/>
                    <value value="1.2.3.4"/>
                </uniqueId>
                <uniqueId>
                    <type value="other"/>
                    <value value="fhir1"/>
                </uniqueId>
                <uniqueId>
                    <type value="uri"/>
                    <value value="http://org.openehealth/ipf/commons/ihe/fhir/1"/>
                    <preferred value="true"/>
                </uniqueId>
            </NamingSystem>
        </resource>
    </entry>

    <entry>
        <resource>
            <NamingSystem>
                <id value="fhir2"/>
                <name value="FHIR2 Patient Identifier Namespace"/>
                <status value="active"/>
                <kind value="identifier"/>
                <date value="2015-07-31"/>
                <type>
                    <coding>
                        <system value="http://hl7.org/fhir/identifier-type"/>
                        <code value="PI"/>
                    </coding>
                </type>
                <uniqueId>
                    <type value="oid"/>
                    <value value="1.2.3.4.5.6"/>
                </uniqueId>
                <uniqueId>
                    <type value="other"/>
                    <value value="fhir2"/>
                </uniqueId>
                <uniqueId>
                    <type value="uri"/>
                    <value value="http://org.openehealth/ipf/commons/ihe/fhir/2"/>
                    <preferred value="true"/>
                </uniqueId>
            </NamingSystem>
        </resource>
    </entry>

</Bundle>

Of course you are free to include your own implementations of UriMapper and/or MappingService.

Translators

The package org.openehealth.ipf.commons.ihe.fhir.translation... contains the set of translators that is able to translate between corresponding IHE transactions.

From a Patient identity Cross Reference Manager ‘s perspective, there are inbound translators:

FHIR transaction FHIR-to-HL7v2 request HL7v2-Transaction HL7v2-to-FHIR response
PDQm ITI-78 iti78.PdqmRequestToPdqQueryTranslator PDQ ITI-21 iti78.PdqResponseToPdqmResponseTranslator
PIXm ITI-83 iti83.PixmRequestToPixQueryTranslator PIX Query ITI-9 iti83.PixQueryResponseToPixmResponseTranslator

Each translator has a set of configurable properties. Their descriptions can be taken from javadoc of the corresponding classes. Below there’s an example of a Spring application context defining translator beans:


<!-- Example for PIXm Query -->

<bean name="pixmRequestTranslator"
      class="org.openehealth.ipf.commons.ihe.fhir.translation.iti83.PixmRequestToPixQueryTranslator">
    <property name="uriMapper" ref="uriMapper" />
</bean>

<bean name="pixmResponseTranslator"
      class="org.openehealth.ipf.commons.ihe.fhir.translation.iti83.PixQueryResponseToPixmResponseTranslator">
    <property name="uriMapper" ref="uriMapper" />
</bean>

<!-- Example for PDQm -->

<bean name="pdqmRequestTranslator"
      class="org.openehealth.ipf.commons.ihe.fhir.translation.iti78.PdqmRequestToPdqQueryTranslator">
    <property name="uriMapper" ref="uriMapper" />
</bean>

<bean name="pdqmResponseTranslator"
      class="org.openehealth.ipf.commons.ihe.fhir.translation.iti78.PdqResponseToPdqmResponseTranslator">
    <property name="uriMapper" ref="uriMapper" />
</bean>

Using the translators

A translator instance can be used two ways:

  • directly from a Java or Groovy application (not discussed here)
  • from a Camel route using ´.process()`

The module ipf-platform-camel-ihe-fhir-pixpdq, being the basis for the PIXm/PDQm FHIR transactions’ implementation, provides processors that can be used to embed HL7 translation functionality into a Camel route.

There are two processor implementations, each taking a translator instance as parameter for the desired translation:

  • FhirCamelTranslators.translatorFhirToHL7v2(translator)
  • FhirCamelTranslators.translatorHL7v2ToFhir(translator)

Example

Here is a sample Camel route that bridges PIXm requests (ITI-83) to an HL7 v2-based Patient Identifier Cross-Reference Manager (ITI-9), and does the same in reverse direction for responses.


import org.apache.camel.builder.RouteBuilder;
import org.openehealth.ipf.commons.ihe.fhir.translation.FhirTranslator;
import org.openehealth.ipf.commons.ihe.fhir.translation.ToFhirTranslator;

import static org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirCamelTranslators.translateFhir;
import static org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirCamelTranslators.translateToFhir;

public class Iti83TestRouteBuilder extends RouteBuilder {

    private final FhirTranslator<Message> requestTranslator;
    private final ToFhirTranslator<Message> responseTranslator;

    public Iti83TestRouteBuilder(FhirTranslator<Message> requestTranslator, 
                                 ToFhirTranslator<Message> responseTranslator) {
        super();
        this.requestTranslator = requestTranslator;
        this.responseTranslator = responseTranslator;
    }

    @Override
    public void configure() throws Exception {
        from("pixm-iti83:translation?audit=true")
                // Translate into ITI-9
                .process(translatorFhir(requestTranslator))
                        // Create some static response
                .to("pix-iti9://${pixManagerUri}")
                        // Translate back into FHIR
                .process(translatorToFhir(responseTranslator, Message.class));
    }
}