XTech 2005: XML, the Web and beyond.

Implementing Web Services for Healthcare – Lessons & Pitfalls

Discuss this paper on the XTech wiki
View XML source for this paper

Keywords

Abstract

Healthcare in the Netherlands is moving rapidly towards XML data exchange over the Internet. This presentation discussion the application of web services, and specifically SOAP and WSDL to a large, complex, real-life environment.

Introduction

The author has been responsible for the web services parts of the NICTIZ specifications and is contributor to the "Web Services Profile" of the HL7 version 3 standard. This presentation will discuss the issues, lessons learned and pitfalls in applying web services to a large, complex, real-life environment. I will start with an introduction to NICTIZ and the Dutch healthcare situation, followed by a short introduction to HL7v3. Next I will discuss the various issues we encountered in specifying web services.

NICTIZ and Healthcare in the Netherlands

NICTIZ is the standardization authority for data exchange in healthcare for the Netherlands. One of the targets of NICTIZ is to have a national exchange of medication data operational in 2006: physicians and pharmacists will be able to see the medication history of patients online. Currently some 90.000 people (out of a population of 16 million) are hospitalized yearly due to wrong medication given to them, so the benefits of having online access to the medication history is extremely important. The Dutch government is dedicated to those developments as well: "The question is not whether the electronic medical file will become obligatory, but how", according to the Secretary of State of Healthcare, Mr. Hoogervorst (Signaal nr.4 NICTIZ 2004).

All NICTIZ messages are between a healthcare provider and an information broker (which sends to other healthcare parties). The information broker has a vital role in medication queries. All parties who have medication data on some patient, inform the broker. The information broker keeps a database of all medication data available. When a healthcare provider wants to retrieve the medication history of a given patient, it sends the patient id to the broker, who looks up all parties which have medical information on this patient, collects this information and sends it back to the requesting healthcare provider. The information broker thus does not store any medical information itself, but acts as a registry of sources where this information is available, and acts as an intelligent messaging intermediary, which can actively collect, sort and release information.

The information broker is considered trustworthy. This means security is a relatively easy subject: we use HTTPS for transport, and since all transport nodes are trustworthy, this is secure. Maybe at a later date more fine-grained security will be needed, especially the ability to encrypt or sign parts of a document.

Just enough HL7

The message format is HL7 version 3 (HL7v3), the international standard developed by Health Level Seven. HL7v3 is the first XML version of HL7. It is also a major overhaul over HL7v2 on other respects.

The HL7v3 Message Format itself consists of a payload of – in this case – medical data, along with several wrappers. Here only two are shown, the Transmission Wrapper and the Control Query Wrapper. The Transmission Wrapper contains constructs for message identification, addressing and reliability. As such it duplicates some of the features provided in the WS-* stack or ebXML Messaging. The HL7v3 message is wrapped in SOAP and transported over secure HTTP over the Internet. HL7v3 as such does not contain any message formats, just a toolbox for making message formats: a local authority such as NICTIZ has to provide a localized HL7v3 format with the actual XML Schema's which will be used.

Lesson: Do not adopt standards too soon

The first important choice was which standards to use. The basis of the protocol stack were obvious: HL7v3, XML 1.0 and XML Schema. On top of this stack there are several possibilities for advanced services such as reliable messaging, security and addressing. At the time the web services specification for NICTIZ started (mid 2003) the choices were limited. One option was ebXML Messaging (ebMS), a set of messaging standards developed jointly by OASIS and UN/CEFACT. A draft HL7 specification for the use of ebMS together with HL7v3 was already under construction. However, at the same time the entire set of ebXML specifications did not appear to have achieved the traction which was expected upon its release in 2001. Notably, Microsoft and IBM were not planning support for ebMS but working on their own stack of advanced services, such WS-ReliableMessaging. And on top of that, OASIS was developing a new WS-Reliability specification which seemed to compete with its own ebMS specification. Taken together, the future of advanced web services was unclear to say the least, and very messy at worst.

NICTIZ had opted for an exchange model in which every exchange is between an healthcare information system (HIS) and a healthcare information broker (HIB). Since the HIBs are considered trustworthy, the security context was relatively simple, making SOAP over HTTPS a viable solution. Since HL7 itself provides addressing as well as reliability features, none of the advanced features of web services were necessary.

Given the insecurity on the future of web services in general, NICTIZ decided to adopt a little as possible of web services standards, thus avoiding "standard lock-in". This would make transition to another set of specifications, be it ebMS or the WS-* stack or something else, much easier. So on top of the basic stack of HL7v3 and XML, SOAP 1.1 and WSDL 1.0 were adopted. SOAP was a safe choice: all of the advanced services, ebMS as well as WS-* use SOAP, so adding SOAP to the NICTIZ stack would actually ease incorporation of those standards at a later date. WSDL was added to provide a description of the NICTIZ web services. The use of SOAP and WSDL led to the incorporation of the "Web Services Profile", co-authored by the presenter, in HL7v3.

Lesson: Do not count on code generation

Early prototypes of WSDL for HL7v3 medical queries, together with HL7v3 XML Schema's revealed several problems with tooling. One of the goals of the initial effort was to demonstrate the exchange of HL7v3 XML messages using SOAP in an environment with mixed development and runtime tools. As a first effort, WSDL was made for medical queries using the HL7v3 XML Schemas, which in turn was used to generate code from the WSDL. Visual Studio.NET was the first environment to try this approach, which failed on several accounts. First, the code generated from the WSDL exposed several errors. For example, HL7v3 defines a type in XML Schema called "ON", which is a reserved word in Visual Basic, and consequently the generated code would not even compile. Several errors like this had to be fixed. After that, the real problems surfaced. The HL7v3 XML Schemas are very complex. Each upper level schema is comprised of several included schemas, which in turn again include schemas. One of the lowest level components are the HL7v3 vocabulary and datatype schemas. Generating code from WSDL will make programmatic constructs – in this case Visual Basic constructs – for everything included in the schemas.

This is not likely to be the desired result. First, not all of the HL7v3 vocabulary or datatype is actually used, so the generated code contains too much useless clutter. Second, the generated code is hardly human-readable anymore. The plethora of generated programming constructs obscures what is happening in the code. Third, and most important, the generated code is unlikely to provide the needed functions. HL7 itself is a layered message format, so it is natural to process only part of the message in any particular application. For instance, processing of the HL7 Transmission Wrapper could occur in a HL7 Messaging Adapter, which passes the contents of this wrapper on to the next layer. Extracting the contents of the Transmission Wrapper as XML, and pass it on as XML to the next application is an obvious choice. The idea of using generated code from WSDL was abandoned in favor of hand-written code, and using WSDL only for documentative purposes. The use of Visual Studio .NET for such low-level programming of web services proved cumbersome. The free PocketSOAP library did allow easy manipulation of SOAP on each desired level (WSDL, SOAP or HTTP). Exchange of HL7v3 messages was demonstrated using a Visual Basic / PocketSOAP client and a Python / ZSI server. When things get more complex than Fahrenheit-to-Celsius web services, generated code stubs will not be much use.

Lesson: Consider generic WSDL

WSDL design leaves plenty of implementation choices. For HL7v3 messages, the basic choice was between two models. The first is simple: an HIS sends a generic HL7 message to a HIB, and optionally gets a generic HL7 reply. The schemas in the WSDL will only describe the outer HL7 message wrappers (which are transport- and context-oriented), the real healthcare content will be described as "any" content in the schemas. A web services layer will forward the content to an HL7-aware layer, which will validate HL7 content, and hide all HL7 complexity from the web services layer (and vice versa). The second option is making "real" WSDL descriptions for the actual healthcare interactions, for instance a HIS sends medication query, and gets a medication response from an HIB. This describes the actual web services provided much better, and might seem the obvious choice. It does however not allow for neat separation of layers: the web services layer has to be aware of HL7 content, whereas the first solution does allow a neat separation of layers, in line with established web development patterns. We've chosen the second option.

Specific WSDL describes the actual messages which are going back and forth, with associated schemas (WSDL schema references not shown):

  <portType name="Medical_Query_PortType">
    <operation name="Medical_Query">
      <input message=" Medical_Query_Request_Message"/>
      <output message=" Medical_Query_Response_Message"/>
    </operation>
  </portType>

Generic WSDL provides an operation with "HL7 Message In" and "HL7 Message Out". This portType can be used for any HL7v3 Message.

  <portType name="portHl7Message">
    <operation name="opHl7Message">
      <input message="ns:msgHl7MessageIn"/>
      <output message="ns:msgHl7MessageOut"/>
    </operation>
  </portType>

A generic schema is used with content "Any" for the HL7 message payload. Alternatively (and more sensibly) one could make a schema which does describe the outer HL7 wrappers and passes the medical payload to another HL7 application.

  <xs:schema targetNamespace="urn:hl7-org:v3">
    <xs:element name="hl7Message">
      <xs:complexType>
        <xs:sequence>
          <xs:any/>
        </xs:sequence>
      </xs:complexType>
    </xs:element>
  </xs:schema>

From a WSDL point of view, these two are completely different. From a SOAP point of view, they are however nearly the same, only the name of the top-level element is different. If we were to include a top-level wrapper as described in the previous section, the SOAP could even be the same.

At the time I thought specific WSDL much better: it actually describes the web service, i.e. a medication query. Given the discussions above, I doubt this decision. Generic WSDL fits much better with separation of layers: a web services layer will just cover basic web connectivity, and does not need to know anything about HL7 content. The procession of HL7 content is delegated to a separate HL7-aware layer. Lastly, WSDL is often used to generate code stubs. Given the complexity of HL7 content, it is not likely code generated on the basis of complete schemas is much use. Simple code which does web connectivity at this layer is sufficient, and generic WSDL would fit this approach.

This would mean the WSDL-generated stub would need objects to handle SOAP, security, reliability etc. and just pass the content of the SOAP Body to the HL7-aware next layer. Such a model actually fits well with generic WSDL. If one uses a tool like VS.NET to generate code, one gets just one layer which should handle everything - or one has to completely overhaul the generated stub and restructure it to resemble something like the above layered model. Current developments within HL7 are in line with this approach. HL7v3 probably will define non-HL7-aware transport layers in future versions.

Pitfalls: Reliability Issues

As mentioned before, reliability was an important requirement of NICTIZ, which could be provided by ebMS, HL7v3 reliability mechanisms or similar provisions in WS-Reliability or WS-ReliableMessaging. All mechanism work similar. The HL7v3 mechanism was chosen: in essence, after sending a message, an Accept Acknowledgement message is returned with the message id of the original message. The sender of the original message thus knows it has been received. If no Accept Acknowledgement is forthcoming, the message can be resent until an Accept Acknowledgement is received. HL7 Accept Acks are controlled by an attribute in the sent message: it can require the Receiver to send Accept Acks always, never, for errors only or for success only.

This yields two options for reliability: HL7 Accept Acks, and a SOAP binding where a HTTP request is followed by a HTTP response which contains the application response.

Example 1, reliability delegated to HTTP:
HL7 level:
Sender sends Query to Receiver
Receiver send Response to Sender
WSDL level:
<operation> with:
<input> = Query
<output> = Response
HTTP level:
HTTP Request with Query
HTTP Response (status 200:OK) with Response

This is inherently reliable: in the absence of HTTP 200 response, Sender knows communication has failed and can try again. No Accept Acks are needed (in fact, we can regard the Response as implicit Accept Ack). There is an alternative, namely sending an Accept Ack for all HL7 messages.

Example 2, reliability delegated to Accept Ack:
WSDL level:
<operation1> with:
<input> = Query
<output> = Accept Ack for Query

<operation2> with:
<input> = Response
<output> = Accept Ack for Response
HTTP level:
HTTP Request with Query
HTTP Response (status 200:OK) with Accept Ack for Query

HTTP Request with Response
HTTP Response (status 200:OK) with Accept Ack for Response

In the current HL7 Web Services Profile an example is given like above Example 1, the HL7 Web Services Profile also makes an implicit choice for delegating reliability to HTTP at least in some cases. However, there are no guidelines in the HL7 Web Services Profile when to use which mechanism.

There are three different solutions to the reliability problems:

Bind immediate response to HTTP

This will perform better as far as internet processing is concerned, since a HL7 request – response pair will map to single HTTP request – response pair. It is also easy to implement. The Sender will just have to make s single synchronous blocking call to its SOAP library, and get a single result message in return. These cases will get a WSDL portType like:

  <portType name="My_PortType_with_AcceptAck">
    <operation name="My_Request_with_AcceptAck">
      <input message="My_Request_Message"/>
      <output message="My_Response_Message"/>
    </operation>
  </portType>

This is described in the current HL7 Web Services Profile. This mechanism does not allow more than one reply to a single request (which HL7v3 by itself does allow). Also a Sender will only need SOAP client functionality, not a SOAP server.

In general the use of batched responses may mean responses will come slower (since the supplier will wait for a batch to fill). It will not be slower when a single source is queried, since the query has to run there anyway. In a NICTIZ context, where results from different suppliers are aggregated at an intermediary, it will take longer when results from several suppliers have to be integrated, which might cause the HIB to timeout the original request. The mechanism is also only applicable to idempotent requests: if the response to a request gets lost in transport, the Sender will have to resent the request. This means the second instance of the request must get the same response as the first. It turns out HL7v3 queries are not always idempotent: there are queries which are started by a request for an initial result set (say records 1- 20), and followed by requests for "the next" result set of the same query. If this next result set (say records 21 – 40) gets lost in transport, resending the request will be interpreted as a request for again the next result set (41 – 60), which means a part of the result set has gone missing. This may be considered a bug in the HL7v3 specifications, but nonetheless the HTTP-binding described above does not work in all HL7v3 cases.

Always use accept acks

This solution is very general and scalable. Design of the standard is easy. Each HL7 message is implemented as a WSDL portType with a single message as input and an acceptAck as output (or without the acceptAck, see discussion above). A single HL7 request – response pair will map to two HTTP request – response pairs, one for the HL7 request, the second for the HL7 response. Please note that a blocking HL7 application cannot meaningfully request an accept ack: this would unblock the application when it blocks to get the results, not the accept ack. Or stated otherwise, if Accept Acks are returned synchronously, the actual response is always sent asynchronously. The solution does also require each participant to act as both SOAP Server and SOAP client.

Lesson: WSDL does not fit well with dynamic response

HL7 message response type is co-determined by message request content. WSDL cannot handle this situation very well. HL7 has attributes whose value co-determines response type, such as responseModalityCode and acceptAckCode. Those are attributes whose value a Sender can set to different values, but whose value co-determines the Schema of a response. For example, the HL7 attribute acceptAckCode determines whether the response will be an Accept Acknowledgement or nothing.

Specifically, if a HL7 message request an AcceptAck this would result in the following WSDL portType:

  <portType name="My_PortType_with_AcceptAck">
    <operation name="My_Request_with_AcceptAck">
      <input message="My_Request_Message"/>
      <output message="My_AcceptAck"/>
    </operation>
  </portType>

If the same message requests no Accept Ack this would result in the following portType:

  <portType name="My_PortType_without_AcceptAck">
    <operation name="My_Request_without_AcceptAck">
      <input message="My_Request_Message"/>
    </operation>
  </portType>

Optionally both operations can be combined into a single portType. But in each case, the sending application has to look into the HL7 Transmission Wrapper to decide which operation to invoke. There is no neat separation of WSDL/SOAP functionality from HL7 Transmission functionality.

The attribute responseModalityCode determines whether the response will be a single result set or a batch of result sets – with a different Schema. WSDL requires a specific Schema for each message. Different schemas means, for WSDL, different messages. Different messages means different operations. This requires the WSDL operations to contain all possible combinations of request and response type. HL7 attributes like this make specific WSDL a) hard to write b) hard to understand. It already does if one uses acceptAckCode with values "AL" and "NE": every WSDL operation has to have two variants, one with and one without accept ack as output. If one adds responseModalityCode with values "B" and "R" there already are 4 variants of each WSDL operation.

Pitfall: Basic Profile Wire Signatures

NICTIZ chose to require compliance with the WS-I Basic Profile, which is a good set of guidelines for interoperability. The Basic Profile however has the following paragraph:

"R2710 The operations in a wsdl:binding in a DESCRIPTION MUST result in wire signatures that are different from one another.

The Profile defines the "wire signature" of an operation in a wsdl:binding to be the fully qualified name of the child element of the soap:Body of the SOAP input message it describes. ... In the document-literal case, ... the message signatures must be correctly designed so that they meet this requirement."

R2710 basically says each top element in an HL7 interaction must have a unique element name. In a HL7 query context, this means Query Continuation and Query Cancel must have different top level element names. However, whether an HL7 message is QueryContinuation or QueryCancel is decided by the value of the continuationQuantity element: if zero, it's a Cancel, if > zero, it's a Continuation. The catch is a Continuation and a Cancel get different responses, and therefore yield different operations in WSDL (in the NICTIZ case, a Continuation would have a Query Response with more records as output, the Cancel would have an Accept Ack as output of the operation).

This means, if we want to support R2710, an HL7 transport application must serialize a HL7 message to XML in a different way for Continuation and Cancel based on the value of one element. Only in this way could we have unique element names for Continuation and Cancel. This is very bad design: Continuation and Cancel are basically the same message, serialize them differently only because of R2710, no other reason, makes no sense. The XML serializer in an HL7 application would actually have to open the HL7 message and inspect the value of continuationQuantity to be able to serialize it correctly.

Take the following WSDL fragment for instance:

	<types>
		<xsd:schema ...
		...
			<xsd:include schemaLocation="../schemas/QUQI_IN000003.xsd"/>
		...
		</xsd:schema>
	</types>
	...
	<message name="QUQI_IN000003">
		<part name="body" element="hl7:QUQI_IN000003"/>
	</message>
	...
	<operation name="QURX_AR990120NL_ContinuationCancelResponse">
		<input message="hl7:QUQI_IN000003"/>
		<output message="hl7:QURX_IN990013NL"/>
	</operation>
	<operation name="QURX_AR990120NL_ContinuationCancelAccept">
		<input message="hl7:QUQI_IN000003"/>
		<output message="hl7:MCCI_MT000200"/>
	</operation>

NICTIZ has chosen not to require all of the Basic Profile for this reason. Unfortunately the Basic Profile itself does not allow for exceptions or individual recommendations: one can either claim compliance with the Basic Profile, or not. This means the NICTIZ interactions are no longer Basic Profile-compliant.

Lesson revisited: Do not adopt standards too soon

At the time this presentation had to be submitted, things were rapidly changing at NICTIZ. Due to external developments, the implementation of reliability was reconsidered. The most important external development was the adoption of ebXML Messaging by the British National Health Service for its medical infrastructure. Also some Dutch governmental units were adopting ebMS: the Dutch Justice Chain, as well as the semi-governmental BKWI, which handles messaging in the Employment, Benefits and Income Chain. All of a sudden ebMS was getting a lot of traction in highly relevant areas for NICTIZ. So at the time of writing, NICTIZ is moving towards adoption of ebMS. The issues with ebMS messaging are largely the same as the ones described in this paper. Details on the last developments will follow in the presentation.

Bibliography

[] HL7 Version 3 Standard, ballot #7
Health Level Seven Inc., 2004
[] Simple Object Access Protocol (SOAP) 1.1
World Wide Web Consortium (W3C), 2000
[] Web Services Description Language (WSDL) 1.1
World Wide Web Consortium (W3C), 2001
[] Basic Profile Version 1.0a
WS-I, 2004
[] Specificatie van de basisinfrastructuur in de zorg, versie 2.1
NICTIZ, 2004

Biography

Marc de Graauw

Independent Consultant, Marc de Graauw IT http://www.marcdegraauw.com

Marc de Graauw graduated cum laude in Philosophy in 1988 and has worked in the field of IT ever since, first for several large Dutch companies, and as an independent consultant since 1998. His main areas of interest are B2B exchange, ontologies and semantics. Marc has done extensive work as a Business Analyst/Information Analyst in the design of B2B exchanges the last years. Marc has frequently spoken on XML and B2B in the Netherlands and has published several articles in Dutch on those issues. He has given presentations at several international congresses and published in several magazines such as XML.COM. Marc lives and works in Amsterdam, the Netherlands.