Getting documents from a FHIR XDS infrastructure

So far in our little mini series on FHIR and XDS we’ve had an overview of how FHIR resources support a document sharing infrastructure, looked at how a document creator can create and submit documents to a repository and/or registry, and spent a bit of time talking about the DocumentReference resource.  Time to think about the consumer of these services.

There are 2 main scenarios that we want to support:

  • Getting a list of documents for a patient (with various search parameters – e.g. a date range, type of document etc.) and displaying the metadata in a list to the consumer
  • Retrieving a particular document to view

Getting the list of documents

Let’s think about the first scenario, and to keep it simple we’ll aim to retrieve all the documents for a patient. Given that the metadata about the document is represented by a DocumentReference resource hosted by the registry server we are using, then this is obviously going to be a query against the DocumentReference endpoint. There are a couple of ways we could to this, and the way we choose depends on whether the have the patients ID or their identity (in fact, it’s exactly the same issue as the document creator had – so refer to that post for more discussion on this).

If we know the patient ID (let’s say it’s 100), then our query will be something like:

GET  http://registryserver/DocumentReference?subject= 100

(assuming that the patient identity is also on the registry server – if not then the patient ID would need to be the absolute reference to the patient resource).

If we don’t have the patient ID, but we do have an identifier (e.g. PRP1660) then we have a couple of options (as always!):

  • Look up the patient ID by a query against the Patient endpoint first
  • Query the DocumentReference with the patient identifier using a chained query, like so:

GET http://registryserver/DocumentReference?subject.identifier= PRP1660

Note that the server is not obliged to support chained queries – check their conformance resource to see if they do. Also, I didn’t include the identifier system in the query – depending on the implementation I may need to do so. Chained queries make a clients life easier (but a servers life harder) – but as the FHIR philosophy is to make it as easy for clients as possible, it’s to be hoped that servers do support that capability.

In either case, the result will be a bundle of DocumentReference resources – the difference between the two being that if we specified the patient ID, then we know that all the DocumentReference resources are for the right person. If we used the identifier, then we are trusting that the identifier is unique (which means that we should probably not be lazy and include the system in the query).

But what if we want a more targeted search? Perhaps we’re only interested in documents created in the past year, or only outpatient summaries?

Well, checking the defined search parameters for DocumentReference we see a period parameter (date of the service) and a type parameter (document type) which would seem to suit our purpose, so (assuming we have the Patient ID):

GET  http://registryserver/DocumentReference?subject= 100&period= >= 2013-01-01

and

GET  http://registryserver/DocumentReference?subject= 100&type= http://loinc.org|34108-1

And of course we can mix and match parameters as we need to (provided the server supports them as documented in their conformance resource)

Retrieve a particular document.

So now we have a list of documents which we can display to our user, how do we retrieve a particular document for them? Well, hopefully the DocumentReference resource that describes the document we want has the location property. Then it’s just an HTTP call away. If the repository where the document is stored doesn’t support a direct GET, then you’ll need to drop back to the XDS ‘Retrieve Document’ profile, the access details for which should be on the DocumentReference.service property.

Assuming you can make a GET call, the repository should include the HTTP standard headers in the response like content-type (which is also recorded in the DocumentReference.mimeType property of course), so rendering by the viewer should be straightforward. Of course, it can get a lot more complex than that – especially in the case of a structured document like a CDA or FHIR-document, when specialized client-side renderers (like XSLT stylesheets) will be required. The metadata in the DocumentReference resource (and possibly HTTP headers)  should indicate what renderer is going to be needed, so all should be well.

Note that the GET call will return the document directly, whatever it is – eg a PDF or CDA document – not a FHIR resource of any sort.

A short (and grossly incomplete) comment on FHIR security.

FHIR itself doesn’t define security protocols – rather it provides the ‘hooks’ that a security framework can use when deciding whether to deliver up a particular document – or even whether to include the document metadata in the original list of documents.

There is an assumption that anyone accessing any of these services is authenticated (identified) and oAuth is the recommended (though not mandated) way to do this.

FHIR supports the concepts of tags – that can be applied to any resource, and allow a privacy policy framework to apply rules when determining whether to deliver up information p and those rules can become arbitrarily complex.  In addition, the DocumentReference resource has the specific confidentiality property that can also be used.

There is also the SecurityEvent resource (analogous to the IHE ATNA profile) that can be used to record significant events such as the release of a document to a viewer. This resource can then be used as the basis of functionality such as “Who has viewed my data?”, and can be created at any time that is appropriate in the overall workflow. The provenance resource indicates the context in which a resource was obtained – e.g. the person who created it.

So, it is the responsibility of the governing body for the document sharing infrastructure to make these policy decisions – and to enforce them! And the IHE ‘Affinity Domain’ concept is a good source of information.

Incidentally, for health security issues in general, check out John Moehrkes blog as a good source of inspiration!

We’re getting towards the end of our little mini series – one of the longer ones so far! We’ll wrap up the loose ends in the final post – in particular we need to think about the best approach when a document source needs to update a document it has already sent to a repository.

The FHIR DocumentReference resource

In the previous FHIR-XDS post we discussed how a document source actor (ie a system that creates a document) could create a document and DocumentReference resource and send them to the Repository server. (Actually, we described a couple of ways that this could be done – and there are a few subtleties yet to discuss about server behaviour – but we’ll hold that for later).

However, we didn’t have a lot of time to talk about the DocumentReference resource itself. This resource represents the metadata about the document and allows a consumer to search the registry (or any server hosting these resources) to retrieve matching documents. From these results, a consumer can then generate a list of documents so that a consumer can select one to view.

The documents themselves can be of any allowed type (CDA, PDF, FHIR document, text etc) and can be stored on any server accessible via HTTP – and they do not need to be FHIR servers. It is quite legitimate for a DocumentReference resource to refer to a static document on any server (or, for that matter, a URL that generates a document ‘on the fly’, though you’d want some way to persist the generated document for medico-legal reasons as described in the ‘IHE on-demand document‘ profile). And, if one is being particularly pedantic, then even HTTP is not really required – you could use FTP or any other supported protocol.

It is important to note that although the DocumentReference resource was developed to support some of the requirements of an XDS infrastructure, it is not restricted to that purpose and can be used in any situation where the business requirement is to create a ‘list of available documents’, from which one or more can be chosen for viewing.

In fact, the FHIR specification defines a specific XDS profile that describes how the DocumentReference resource is to be used in the context of an XDS installation – for example some fields that are optional in the resource, are required when acting as a proxy to XDS. The link also gives guidance for mapping between XDS and FHIR elements, including fields required for XDS that are not in the DocumentReference and so represented by extensions.

The following description refers to the generic DocumentReference resource. Note that there are a number of properties whose value should come from a ValueSet defined by the governance body that oversees the overall installation (an Affinity Domain in XDS-speak – though the affinity domain covers much more than just code sets). We will talk a bit more about ValueSet’s after this list.

  • The masterIdentifier is the identifier of the document as created by the source. It is version specific – i.e. a new one is required if the document is updated (We’ll talk about updating documents in a separate post).
  • There can be any number of other document identifiers that make sense. For example, there might be a version independent identifier here.
  • The subject is who the document is about. Generally this is a patient, but the specification also supports a Practitioner, a Group or a Device.
  • The type and class indicate what kind of document this is- (eg a discharge summary). A ValueSet should be defined for each. (Note that ‘class’ has been called ‘sub-type’ in some versions of this resource)
  • There is at least one author, which is a Practitioner.
  • There can be an Organization which is the custodian of the document – responsible for maintaining the document
  • There’s a policyManager link to a URL that describes the access policies to the document.
  • The authenticator of the document can be an Organization or a Practitioner.
  • The created date is when the document was created (not necessarily related to the period of the service that the document may describe – this is in the context.period element).
  • The indexed date which is when the DocumentReference resource was created.
  • The status allows minimal workflow of this DocumentReference resource (NOT the underlying document). Options are current, superseded or error. We’ll talk more about this status when we talk about updating a document in a later post.
  • The docStatus and supercedes allow a document to be updated (superseded) or otherwise have it’s status changed. This is a CodeableConcept, and so can accommodate a specific workflow defined by the affinity domain such as preliminary, final, amended or whatever, as documented in the ValueSet.
  • The description is the title of the document. If you’re displaying list of documents then this would be a good property to use.
  • The Confidentiality property allows a privacy policy to control the access to a document. The policy can also use the generic tags functionality in FHIR.
  • The PrimaryLanguage element indicates exactly that – no surprises here. It is a code, and is bound to the IETF Language tag.
  • mimeType and format indicate how the document is represented – eg xml, pdf, msword etc.
  • Size allows a client to estimate download time of the document, and the hash can ensure integrity.
  • The Location is a URI that indicates from where the document can be downloaded. This does not need to be a FHIR server. The client will (generally) not know what this is when creating the resource as it will be related to the ID that the server assigns. However, either it or the service property is required, so it may be necessary to create a ‘dummy’ resource using the cid: prefix so the server knows it is not real – or define a service. You would define a local policy to describe this, which could be identified in the policyManager link above.
  • The service is used if the location is not known. You would use this if you weren’t using simple FHIR REST, so for the moment we’ll ignore it.
  • The context allows you to specify the clinical context of the document – at least the context that is useful in searching or displaying the document list. The period that the document refers to and the facility type are present, but you can add codes that make sense here as well.

For reference, the following fields need to have their options defined by the governing body using a ValueSet. These can either be defined directly in the ValueSet or be a sub-set of an external Terminology like LOINC or SNOMED.

  • type
  • subtype
  • docStatus
  • confidentiality
  • mimeType
  • format
  • context.code
  • context.facilityType

The recommended way to document this is to create a Profile resource that describes the implementation, and refers to the appropriate ValueSets – with the whole lot being saved in FHIR server/s so that they can be easily located. The option below shows a minimal profile that sets the ValueSet for the confidentiality property.

<Profile xmlns="http://hl7.org/fhir">
     <text>
         <status value="additional"/>
         <div xmlns="http://www.w3.org/1999/xhtml">Minimal profile for confidentiality codes in DocumentReference</div>
     </text>
     <name value="Elbonian XDS Profile"/>
     <status value="draft"/>
     <structure>
         <type value="DocumentReference"/>
         <name value="XDSDocumentReference"/>
         <element>
             <path value="DocumentReference.confidentiality"/>
             <name value="ConfidentialityOptions"/>

             <definition>
                 <short value="Confidentiality Code options"/>
                 <formal value="Confidentiality Code options"/>
                 <min value="1"/>
                 <max value="1"/>
                 <isModifier value="false"/>
                 <binding>
                     <name value="List of confidentiality options"/>
                     <isExtensible value="false"/>
                     <referenceResource value="/ValueSet/ConfidentialityOptions"/>
                 </binding>
             </definition>
         </element>
     </structure>
 </Profile>

and the ValueSet that it references could look like:

<ValueSet xmlns="http://hl7.org/fhir">
     <text>
         <status value="additional"/>
         <div xmlns="http://www.w3.org/1999/xhtml">The value set for confidentiality codes in the elbonian document sharing
         project. The codes are defined directly by the valueset, rather than references to an external terminology</div>
     </text>
     <name value="ConfidentialityOptions"/>
     <description value="ConfidentialityOptions"/>
     <status value="draft"/>
     <!-- Define the  options in the resource rather than an external terminology-->
     <define>
         <system value="http://fhir.moh.elbonia.com/confidentiality"/>
         <concept>
             <code value='N'/>
             <display value="Normal"/>
             <definition value="That the document is of normal confidentiality - is able to be viewed for any authenticated user"/>
         </concept>
         <concept>
             <code value='H'/>
             <display value="High"/>
             <definition value="Then the document is Highly confidential - eg Mental health - and can only be viewed by people authorized to access documents at that level"/>
         </concept>
     </define>
 </ValueSet>

Finishing up…

In the previous post I promised that I would talk about the DocumentManifest resource. This is a new addition to the FHIR family, and has been introduced to support the ability for a document source to submit a collection of documents in a single batch as XDS does.

In truth, I’m not entirely clear how this resource should be used. It does have the recipient element, which provides the basis for a notification system, but beyond that the main value would seem to be that there can be more than a single document in a batch submission to the Repository server. I’ll update the post when I learn more.

So, we have saved a document in the repository and updated the registry. In the next post, we’ll switch our attention to the Document Consumer and discuss how to query the registry for documents, and to get the actual document itself.

FHIR Connectathon: The conformance resource for a client

The planning for the 5th FHIR connectathon is underway, with an announcement yesterday on the FHIR list server encouraging people to review the registration site, and to register as soon as the registration is open (which I absolutely encourage you to do). This will be the first connectathon that I won’t be at – which is kind of sad, but had to happen at some point.

Although I’m in the middle of a series of posts on FHIR & XDS (and a bit behind to tell the truth – work has got in the way of the fun stuff), I thought that it might be of interest to discuss some ‘connectathon-related’ topics – starting with the conformance resource. We’ve talked about this before in the context of a server using it to indicate to a potential consumer what it’s capabilities are (and been quite good in actually producing examples in our discussions), but it has also been designed for use by a client to indicates what it requires of a server.

In the case of connectathon, we encourage clients to create a Conformance resource to indicate what they need of a server as they fulfill the scenarios.

The conformance resource itself has some ‘header’ information (eg description, FHIR version and so forth) and then entries for REST, Document and Messaging. We will focus on the REST entry. There are entries that indicate:

  • Whether the conformance resource is describing a client or a server
  • Which resources the server supports or a client requires
  • Which profiles are supported/required
  • Security considerations
  • For each resource:
    • what operations are supported/required – CRUD
    • whether history is supported/required
    • the search parameters supported/required

A client will set the Conformance/rest/mode/@value  value to ‘client’ to indicate that it is specifying a requirement rather than a capability – and the other parts of the resource mean the same thing that they to for a server.

As an example, suppose that I want to complete scenario 1.4 of connectathon (search for a patient on name)  – the following conformance resource would indicate my requirements to a prospective server:


<Conformance xmlns="http://hl7.org/fhir">
     <text>
         <status value="generated"/>
         <div xmlns="http://www.w3.org/1999/xhtml">
             <p>A conformance statement detailing the client requirements for a sever in implementing scenario
             1.4 on connectathon#5 - search for a patient by name. Note that this only for 1.4 - we'd need
             to update if we were supporting any other scenario</p>
         </div>
     </text>
     <identifier value="68D043B5-9ECF-4559-A57A-396E0D452311"/>
     <version value="20130510"/>
     <name value="Connectathon Conformance requirements"/>
     <publisher value="HayCorp"/>
     <telecom>
         <system value="email"/>
         <value value="user@haycorp.com"/>
     </telecom>
     <description value="This is the FHIR conformance statement for the haycorp team at connectathon #5"/>
     <date value="2013-11-12"/>
     <software>
         <name value="HC-Client"/>
         <version value=".01"/>
     </software>
     <fhirVersion value="0.12-1911"/>

     <!--   doesn't really apply for a client, but is required   -->
     <acceptUnknown value="false"/>

     <!--   we only do XML -->
     <format value="xml"/>

     <rest>
         <mode value="client"/>
         <resource>
             <type value="Patient"/>
             <operation>
                 <code value="read"/>
             </operation>
             <searchParam>
                 <name value='name'/>
                 <type value="string"/>
                 <documentation value="Search by patient name"/>
             </searchParam>
         </resource>
         <operation>
             <code value="search-system"/>
         </operation>
     </rest>

 </Conformance>

A couple of notes:

  • The conformance resource is not – necessarily – used for machine processing, but rather as a uniform way that servers and clients can indicate and match capability & requirements. For example, they are very useful in the planning stages of a project when clients and servers are being located and/or designed.
  • As a FHIR resource, a conformance statement can be stored in a FHIR server, and has the same versioning capabilities as any other FHIR resource. This makes an excellent choice for locatable documentation once the systems are in operation.
  • And its resource nature also supports the concept of a searchable registry. This is more applicable to a server than a client, but does support the idea of providing UDDI like functionality…

FHIR and XDS – Submitting a document from a Document Source

This post will focus on the XDS Document Source actor and the mechanics behind submitting a document and its metadata to an XDS system. But before we do that, we need to review some aspects of the way that’s going to work in our RESTful context.

We’ll use the DocumentReference to record the metadata, but one of the tricky bits is going to be managing the ‘entity identifiers’ – particularly for patient, practitioner and organisation that XDS needs, vs the resource ID that FHIR requires for references.

A bit of background.

An Identifier is something that identifies an object or identity – like a patient. It has a number of properties, of which the most relevant to this discussion are the system and the value.

The system is the namespace within which the value is unique. A single entity/patient can potentially have many different identifiers – for example a patient might have:

  • An identity within a national identity service
  • An identity in their local doctors system
  • A drivers license
  • A library card

and so on. It is also possible that a single person could accidentally have multiple identifiers in the same system – for example when an administrator creates a new identifier for a patient without first checking whether they already have one. In fact one of the jobs of the identity service is to locate these duplicates and merge them, but I digress…

IHE recommends that the participants agree in advance on which identifier systems will be used in XDS, and provides a profile called the PIX profile (Patient Identity Cross reference) that describes how to manage and cross reference these identifiers – so that if you have one identifier you can get the matching ones for that patient. There is another query – PDQ – or Patient Demographic Query that describes how to query a registry using name, Date of Birth and other demographics and get back a list of patients – including known identifiers. Often, a single system will implement both of these profiles – and the term PIX/PDQ to describe them is not uncommon.

But when a Document Source submits a document to a repository using FHIR, it needs a resource reference to the patient – which is a URI to the patient resource – not an identifier.

This is a problem for a client that only has an identifier for a patient.

Reviewing what PIX/PDQ does, this all seems suspiciously like queries against the FHIR Patient resource. If we imagine that there is a server that exposes a /Patient endpoint and is backed up by all the identity matching and update functionality of an identity registry, then queries against this endpoint will perform much the same functionality as PIX/PDQ queries – for example:

  • GET /Patient?name=power&gender=M      (PDQ-ish)
  • GET /Patient?identifier=PRP1660                   (PIX-ish)

would both return a list of matching patients, each of which would contain all the identifiers known for a patient. We can even ask for previous versions of the resource if we need them, and Patient resources can link to merged resources using the link property. This is actually what we discussed in an earlier post, though we didn’t really talk about merging and linking patients (a topic for another post perhaps).

So – provided that we have such a server (which is kind of fulfilling the role of Identity Source in XDS) then we have our ‘identifier lookup’ sorted. But we’re still not quite there. Who should do this lookup?

There are a couple of options:

  • We could off load the problem to the repository – require it to be able to receive an identifier, query the /Patient server using that identifier and get the resource ID. There’s even a way of doing that using a bundle in FHIR (part of transaction processing)  – but this has the limitation that it only works on the same server as the search, which would mean duplicating patient data on all repositories. That doesn’t really sound right.
  • We could require that the Document Source perform that lookup, and include the full FHIR reference in the submission.

Although we are asking the Document Source to perform more work, this does place the responsibility for correctly identifying the patient on the Document Source, which feels like the right place. We will go with this solution, so the updated diagram now shows a line between the Document Source and the Identity Source, indicating that lookup.

Updated FHIR XDS

Similar issues exist for the Practitioner (author) and the Organisation (custodian), and we will adopt the same attitude – i.e. that it is the responsibility of the Document Source to locate the required resource ID. Fortunately, it’s a bit easier for these two as it’s not so critical to absolutely identify them (in a FHIR sense). If we can’t do that, then we can use FHIRs ability to contain an unidentified resource within the DocumentReference resource (and, in fact, it was this issue that led to the concept of contained resources in the first place, early on in FHIR’s evolution). There are, of course down-sides to this approach – but sometimes it’s the only way.

A couple of notes:

  • Astute readers will note that the DocumentReference resource can have subjects other than Patient. We’re not thinking about them at the moment, but if/when we do then the same rule will apply – the Source needs to specify the resource ID – i.e. identify the resource.
  • We do have the option of creating a custom Web Service (could be REST or SOAP) on the repository that performs the lookup – i.e. takes an identity and performs the query on the appropriate server on behalf of the source. We’d want to carefully think through the implications of that approach.
  • Having a copy of the Patient resources on the repository and using the bundle search facility to create local ID’s remains an option – after all from the perspective of XDS it is the identifiers we are after. It doesn’t feel right to me, but those more expert in REST than I might have a different opinion.

So we have our identifiers and ID’s sorted – now to submit the document! This is going to involve updating both the repository and the registry – in other words a transaction. As always, there are a number of options:

  • The Document Source could send the document to the repository – getting back an ID (or in some other way having a URI that allows a consumer to retrieve it), and then create and send a DocumentReference resource to the registry. This is a perfectly legitimate approach – and is required in some situations such as a non-FHIR repository – but it does mean that the client needs to manage the transaction, as we discussed with medications. That may or may not be an issue.
  • We could use FHIRs transaction functionality to off load the responsibility to the Repository Server.

The rest of this post will describe the second approach.

The steps we need to follow are:

  1. Create a new bundle
  2. Get the document we want to store, base-64 encode it, and save it in a bundle as a Binary resource
  3. Create the DocumentReference resource and place that in the bundle
  4. Send the bundle to the repository root
  5. Process the response (succeed or fail)

1. Create a bundle

This is a simple matter of creating the basic structure of the bundle.

2. Get the document.

Retrieve the document from whatever local store it is help in, base 64 encode it, and create an entry for it in the bundle using the binary resource. Its ID should be a cid: ID so that the repository server knows to create a new resource (i.e. treat it as a create operation).

3. Create the DocumentReference resource

This is where the work is. The DocumentReference resource contains all the metadata required for the document to be located via a query, and the contents will depend on what information is available and what is specified in the Affinity Domain (i.e. what are the valid options for each of the properties in the DocumentReference resource).

In addition, the ‘generic’ DocumentReference resource has been profiled specifically to suit the XDS requirements. It’s going to take a while to go over the details of this, so we’ll hold that discussion over until we’ve finished the mechanics of transmission.

The DocumentReference resource will also contain the references to other resources we need, which may be:

  • A straightforward resource reference to the resource – e.g. the patient resource – on a server. There’s no need to include the resource in the bundle, though we can if we want to.
  • A contained resource if we were unable to resolve the link.

When complete, add the resource to the bundle.

(There may be other resources to include – like attachments to documents – that we can talk about some other time.)

4. Send the bundle to the server and process the response.

POST the bundle to the root of the Repository server, as we discussed in the post on medications. The server will perform the required processing which has the following steps:

  1. Save the document in the local store, which will generate an ID. It will subsequently by available by a query against the /Binary endpoint of the server – eg GET /Binary/{ID}
  2. Update the location property in the DocumentReference resource to the local URI
  3. POST the DocumentReference resource to the registry
  4. Return a response to the client.

Note that we are assuming that the repository is managing the transaction. If there are errors back from the registry (eg missing or invalid data) – then it should remove the document from the local store and return an error to the client (ideally with a OperationOutcome resource).

Note also that the above processing – ie that the server should manage bundles containing DocumentReference and Binary resources by POSTing the DocumentReference to a configured Registry Server is one that depends on local policy and configuration. It may be clearer to create a custom endpoint to make this processing explicit.

There are other processing options for the server (eg asynchronous processing) – again, something to be discussed in a later post.

5. Process the response

The HTTP status code will indicate success or failure, and in the event of a failure then the server should return an OperationOutcome  resource with details.

So, we have finally saved a document in the repository and updated the registry. We didn’t have the space available to talk about the DocumentReference resource in any detail, and it’s kind of important to know what to put in there, so in the next post we’ll talk about that resource (and touch on the DocumentManifest), after which we can start to think about the consumer.

FHIR and XDS – an Overview

One of the ‘sweet spots’ for FHIR is establishing RESTful interfaces to an IHE XDS (Cross Enterprise Document Sharing) implementation. IHE had already started down this path when it created the ‘Mobile Access to Health Documents’ – MHD – profile as a way of making it easier for mobile devices to access XDS installations, but with the emergence of FHIR, the two efforts have been merged and the intention is that the MHD version 2 profile will be FHIR based. This is not to say that FHIR can replace XDS – there are circumstances where the native XDS interfaces can be required – especially in larger deployments, but the ease of use of FHIR makes an attractive façade in many situations.

It should also be mentioned that it is quite feasible to implement an ‘XDS-like’ system that follows the overall pattern of XDS, but doesn’t have an XDS ‘back-end’ if it meets the use cases.

This will be the first in a series of posts that talks about implementing the FHIR as a front end to XDS, and will be an overview to XDS and how FHIR resources match the profile.

For those who are not familiar with XDS, its purpose is to enable the sharing of documents between healthcare enterprises. The image below is the standard representation of XDS:

XDS-Actor-Transaction-b

It shows 5 principal actors:

  • The Document Registry that is the ‘index’ to available documents. Within a single implementation there is only a single registry, though there are profiles (like XCA) that support ‘inter-registry’ queries where there are multiple co-operating deployments
  • One or more Document Repositories that actually hold the documents
  • Multiple Document Sources that actually create the document, and submit them to a repository
  • Multiple Document Consumers that query the registry for documents, and then retrieve the documents from the indicated repository
  • A single Identity Source, that manages patient identity

There are a number of transactions defined, and the ones that are of interest to us are as follows:

  • Register and provide document set (ITI-41), where a document source assembles a package containing 1 or more documents, with associated metadata (which we’ll talk about shortly) and sends that to a repository for storage.
  • Register document set (ITI-42) where the repository forwards the metadata to the registry.
  • Registry stored query (ITI-18) which is the consumer making a query of the registry to retrieve specific documents based on some search criteria.
  • Retrieve document set (ITI-43) defines how a consumer retrieves a specific document. Generally, this will be after they have queried the registry.
  • Patient Identity Feed (ITI-8, ITI-44) shows the identity source updating the registry with demographic information about patients – especially ‘merges’ where a single patient is known by more than one identifier.

Refer to the XDS profile for more details.

Applying  FHIR resources to this picture, gives us the following:

FHIR-XDS

We can see that there is a good match (not unsurprisingly) between the XDS transactions & the FHIR resource end points.

  • A document source can submit the document and metadata to the repository via a transaction (like the medication update)
  • The repository updates the registry by posting a resource to the registries DocumentReference endpoint
  • A Consumer searches for documents via a query against the registries DocumentReference endpoint, and retrieves the document via a GET against the repositories /binary endpoint.
  • A SecurityEvent resource that records accesses of significance.

Note that for the purpose of this discussion (to keep things simple), we are ignoring the DocumentManifest resource, and only using the DocumentReference resource – which means that we will only be submitting and retrieving a single document at a time. The DocumentReference resource describes the metadata (and supplies the base data for searching) for a single document. The DocumentManifest resource ‘groups’ multiple documents (for example a document and an attachment) into a single package. We’ll come back to this in a later part of this series.

It’s also possible for the document source to update the registry directly. They may do this if the document is being stored outside of the XDS infrastructure (e.g. to support a repository that is not otherwise functioning as an XDS repository). We’ll also touch in this later on.

We’ve talked about a ‘document’ quite a lot – but what is a document? Well, in regards to this discussion it’s pretty much anything you need it to be – the spec states:

A document is some sequence of bytes that is identifiable, establishes it’s own context (e.g. what subject, author etc), can be displayed to the user, and has defined update management

It could be a CDA, a PDF, a text file, a FHIR document or even a Microsoft Word document. It’s the DocumentReference that has the metadata about the document – which includes the format of the document (as the mime type)

The last thing that we’ll touch on in this introduction is the concept of the Affinity Domain. IHE defines the affinity domain as “a group of healthcare enterprises that have agreed to work together using a common set of policies and share a common infrastructure”

In practice this includes things like:

  • The security & privacy policy that members will adhere to
  • Confidentiality codes & their meanings
  • What are the document ‘types’ and ‘sub-types’ – e.g. LOINC codes for things like ‘Patient Summary’, ’Discharge Summary’, ‘Clinic Note’ etc.
  • The supported document formats – eg the mimeType or more detailed descriptions
  • What are the possible statuses that a document can be in
  • The types of facility that are recognised (Primary Care, Hospital, Rest Home)
  • Clinical service (Orthopaedic, Paediatric, ED…)

There are other characteristics that can be applied to a document, but these are the most important that the affinity domain needs to agree on.

In the next post, we’ll dig into how the client sends a document to the server.

Regional Shared Medications with FHIR

Let’s wrap up this ‘mini-series on medications’ by moving up a notch and considering how we could build a regional repository of patient medications, which would be shared amongst all those involved in a patients care. The idea is that the repository is the ‘single source of truth’ for patient medications. Other systems – such as a GP (or Primary/Ambulatory Care system) , or a hospital on admission/discharge – would access that repository (via FHIR interfaces) when viewing/updating medications rather than their own databases (though they would likely synchronize with rather than replace their local data source – at least initially).

The repository could also support Patient and Provider portals, as well as being ideal for mobile devices.

The picture below shows the ‘big picture’ for what I’m talking about (thanks to Orion Health for the picture):

medman

It shows a single repository of data that is intended to be utilized by all providers – and also the patient and their care givers. The repository would contain a number of different types of medication related data, for example:

  • The current list of medications (modelled as a FHIR List)
  • The MedicationPrescription resources referenced by that List
  • Dispensing (MedicationDispense) resources.

There are also a few supporting resources as detailed below. And there are many other clinical resources (eg allergies) that could be added once the infrastructure was in place.

Use cases

We’ll support the following high-level Use Cases:

  • Store dispensing records from a pharmacy. Each time a pharmacy dispenses a medication, they construct a MedicationDispense resource and save it in the repository.
  • Retrieve dispensing data for a patient over a time period. Used by a clinician when reconciling the patients’ medication list.
  • Get the patients’ current list of medications. Used by anyone involved with the patients’ care – including the patient & their care givers – such as a rest home, GP or ED department.
  • Get the history of changes to the List, and previous versions of that list.
  • Update the patients’ current list of medications.

Note that our Use Cases are related to recording medication information only – specifically we are not (yet) including any ordering functionality (though the use of the MedicationPrescription resource will allow us to do so in the future).

Security

All communication with the system will be over an SSL connection. We’ll use oAuth to identify and authenticate the user. To keep things simple, we will assume that any registered user can access the records of any patient (we’ll have an audit record of course). We’d apply more robust privacy and security rules in a real implementation of course – especially around the updating of medication data – but this is a very large topic that we can’t go into here.

It is worth noting that FHIR has security ‘baked in’ to its design – so we can have confidence that we will be able to do this when we need to.

FHIR Interfaces / end points

The following section lists the FHIR interfaces that we will expose.

It’s not always appreciated that a FHIR server doesn’t have to support all resources – and can apply whatever business logic it needs at the interfaces. In fact, we’re only going to support the endpoints that we need to meet the use cases described above (for the moment).

The following section lists the resources and the end points that our solution will need to expose.

Patient

The Patient will need an identity on our server so that we can find them, and reference the other resources to them. We might maintain this ourselves (hard) or just provide a FHIR façade to an existing identity service. The queries we support are all about getting the patient resource, and include:

  • Find Patient (eg GET /Patient?name=eve)
  • Get Patient by identifier (eg GET /Patient?identifier=PRP1660)

Practitioner

The Practitioner resource is similar to the Patient resource – we need it in a number of places, but we don’t want the responsibility of maintaining the register. So, like Patient, we’ll just provide a façade to the appropriate identity service, with the following endpoints:

  • Find Practitioner (eg GET /Practitioner?name=smith)
  • Get Practitioner by identifier (eg GET /Practitioner?identifier=PRP1660)

MedicationDispense

The MedicationDispense resource is going to be useful when we are assembling the patients Medication List. We’ll take in a feed from pharmacies, and then a client can use that as they check that the list is correct (often termed ‘reconciling’ the list) – e.g. does the list include the medications that the patient has been dispensed?

We will assume that the submitting system has looked up the patient ID when they assemble and send the resource (for example they may use the /Patient endpoint described above to do so).

So we have:

  • Submit a dispense resource (POST /MedicationDispense)
  • Get dispense records in the past (say) month (GET / MedicationDispense?patient={patientID}&whenHandedOver < {1 month ago}

Given the volume of dispensing records, we may also want to support batch insertions of MedicationDispense resources as well.

As an aside, both MedicationDispense and MedicationPrescription resources refer to a Medication resource, which holds the details of the actual drug. The medication resource has a code property that identifies the specific drug within the specified drug terminology. For example in New Zealand we have the ULM (Universal List of Medications) – a terminology based on SNOMED – thus the code 44362701000116107 refers to a 100mg tablet of aspirin.

We’re assuming that the client system will be doing any searching/lookup against that terminology, so all we need is the code and the code system. For this reason our MedicationPrescription and MedicationDispense resources will contain the  Medication resource, rather than referencing  separate resource. There are tradeoffs in this decision as discussed in this post, but it does simplify our architecture, and we can easily change later if we need to without any migration cost.

MedicationPrescription

We’re using the MedicationPrescription resource to record the details of each medication the patient is taking, as it contains details like the drug, dosage information, reason for prescription and suchlike. We’ll use the ‘transaction’ based method of updating this resource that we described in the last post, which means that the only endpoint we need is to retrieve the resource based on the resource ID (which we’ll get from the List)– i.e.

  • Get a single MedicationPrescription (GET /MedicationPrescription/{ID})

List

We’ve talked in the last couple of posts about how to use the List resource to record the patients’ medication list, so lets not repeat all that here. The endpoints we’ll need are:

  • Get a patients list of medications (GET /Patient/{patientID}/List?code=10160-0)
  • Update a patients list of medications. This will be a transaction update as described earlier.

To get the history of changes to the List, we will use FHIR’s versioning abilities. First, the history of changes (assuming the listID is the ID of the list):

  • GET  /List/{listID}/_history

This will return a bundle of resources – each being an older version of the list. Once we have the versionID’s, we can perform a vread of the List as follows:

  • GET  /List/{listID}/_history/{versionID}

This will give a client application the ability to display the changes to the List over time.

SecurityEvent

We haven’t talked about SecurityEvent so far. Based on the IHE ATNA profile, the SecurityEvent is used to maintain an audit log of events that are of significance clinically or legally. We will create a new SecurityEvent resource automatically under the following situations:

  • When someone gets the list of medications for a patient
  • When someone updates the list of medications for a patient

The SecurityEvent will then be available so that we can see who has accessed a patients record – and we intend to make this available to the patient as well via a patient portal. The external endpoint will be quite simple:

  • Get a bundle of SecurityEvent resources for a patient over a given time period (GET /SecurityEvent?patientId={patientID}&date > {startDate} & date < {endDate}

 Notes:

We have had to expose a number of interfaces to support this functionality – but the benefits of doing so would justify the expense in providing a single place where medications are recorded for a patient and accessible by anyone who needs to do so (in the interest of the patient). As mentioned above, security & privacy mechanisms will need to be put in place in a real deployment.

It is worth noting that there are significant benefits to this architecture over a more simplistic approach like simply storing a series of documents – especially when it comes to population based analysis and research. It’s much easier to get at the information when stored in this ‘organized’ way than having to troll through millions of documents every time you wanted to find out specific information – such as who had received a specific drug, or how many patients with a Condition of Diabetes have not had an HBA1c performed in the past 6 months.

Version management also makes timeline changes straightforward, and FHIR is, of course, much easier for mobile devices.

However, I’m the first to admit that it represents quite a leap over how information is managed today! Still, one can dream…

And finally, being good FHIR citizens, our conformance resource is attached…

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
 &lt;Conformance xmlns=&quot;http://hl7.org/fhir&quot;&gt;
   &lt;text&gt;
     &lt;status value=&quot;generated&quot;/&gt;
     &lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
       &lt;p&gt;This conformance statement supports the Shared Medication repository, and specifies the following endpoints&lt;/p&gt;
       &lt;p&gt;Person: Read and Search on name and identifier&lt;/p&gt;
       &lt;p&gt;Practitioner: Read and Search on name and identifier&lt;/p&gt;
       &lt;p&gt;MedicationDispense. Create, and search on patient,whenHandedOver&lt;/p&gt;
       &lt;p&gt;MedicationPrescription. Read. &lt;/p&gt;
       &lt;p&gt;List. Create and search on code,patient. Version read.&lt;/p&gt;
       &lt;p&gt;SecurityEvent. Search on patient,date&lt;/p&gt;
       &lt;p&gt;Transaction interfaces to update the Medication List&lt;/p&gt;
     &lt;/div&gt;
   &lt;/text&gt;

   &lt;identifier value=&quot;68D043B5-9ECF-4559-A57A-396E0D452311&quot;/&gt;
   &lt;version value=&quot;.1&quot;/&gt;
   &lt;name value=&quot;My List Of Medicines (MLOM) Conformance Statement&quot;/&gt;
   &lt;publisher value=&quot;Elbonian MOH&quot;/&gt;
   &lt;telecom&gt;
     &lt;system value=&quot;email&quot;/&gt;
     &lt;value value=&quot;wile@elbonia.govt&quot;/&gt;
   &lt;/telecom&gt;
   &lt;description value=&quot;The FHIR endpoints required to support a regional Medication repository - My List Of Medicines&quot;/&gt;
   &lt;date value=&quot;2012-10-14&quot;/&gt;
   &lt;software&gt;
     &lt;name value=&quot;MLOM&quot;/&gt;
     &lt;version value=&quot;0.34.76&quot;/&gt;
   &lt;/software&gt;
   &lt;fhirVersion value=&quot;0.12&quot;/&gt;
   &lt;acceptUnknown value=&quot;false&quot;/&gt; &lt;!--   this system does not accepts unknown content in the resources   --&gt;

   &lt;!--   this system can do either xml or json. (Listing both implies full support for either, with interconversion)   --&gt;
   &lt;format value=&quot;xml&quot;/&gt;
   &lt;format value=&quot;json&quot;/&gt;
   &lt;!-- We only support REST interfaces at this time. This includes transaction to the server root to update the List--&gt;
   &lt;rest&gt;
     &lt;mode value=&quot;server&quot;/&gt;

     &lt;!-- SecurityEvent record --&gt;
     &lt;resource&gt;
       &lt;type value=&quot;SecurityEvent&quot;/&gt;
       &lt;operation&gt;
         &lt;code value=&quot;read&quot;/&gt;
       &lt;/operation&gt;
       &lt;searchParam&gt;
         &lt;name value=&quot;patient&quot;/&gt;
         &lt;type value=&quot;reference&quot;/&gt;
         &lt;documentation value=&quot;Lookup by patient.&quot;/&gt;
       &lt;/searchParam&gt;
       &lt;searchParam&gt;
         &lt;name value=&quot;date&quot;/&gt;
         &lt;type value=&quot;date&quot;/&gt;
         &lt;documentation value=&quot;Lookup by date the event occurred.&quot;/&gt;
       &lt;/searchParam&gt;
     &lt;/resource&gt;

     &lt;!-- MedicationDispense record --&gt;
     &lt;resource&gt;
       &lt;type value=&quot;MedicationDispense&quot;/&gt;
       &lt;operation&gt;
         &lt;code value=&quot;create&quot;/&gt;
       &lt;/operation&gt;
       &lt;operation&gt;
         &lt;code value=&quot;read&quot;/&gt;
       &lt;/operation&gt;
       &lt;searchParam&gt;
         &lt;name value=&quot;patient&quot;/&gt;
         &lt;type value=&quot;reference&quot;/&gt;
         &lt;documentation value=&quot;Lookup by patient.&quot;/&gt;
       &lt;/searchParam&gt;
       &lt;searchParam&gt;
         &lt;name value=&quot;whenHandedOver&quot;/&gt;
         &lt;type value=&quot;date&quot;/&gt;
         &lt;documentation value=&quot;Lookup by date the medication was given to the patient.&quot;/&gt;
       &lt;/searchParam&gt;
     &lt;/resource&gt;

     &lt;!-- MedicationPrescription resource. The prescription records are all created through the 'transaction' process so read-only --&gt;
     &lt;resource&gt;
       &lt;type value=&quot;MedicationPrescription&quot;/&gt;
       &lt;operation&gt;
         &lt;code value=&quot;read&quot;/&gt;
       &lt;/operation&gt;
     &lt;/resource&gt;

     &lt;!-- List resource. Used to support the List of Medications. --&gt;
     &lt;resource&gt;
       &lt;type value=&quot;List&quot;/&gt;
       &lt;operation&gt;
         &lt;code value=&quot;create&quot;/&gt;
       &lt;/operation&gt;
       &lt;operation&gt;
         &lt;code value=&quot;read&quot;/&gt;
       &lt;/operation&gt;
       &lt;operation&gt;
         &lt;code value=&quot;vread&quot;/&gt;
       &lt;/operation&gt;
       &lt;searchParam&gt;
         &lt;name value=&quot;patient&quot;/&gt;
         &lt;type value=&quot;reference&quot;/&gt;
         &lt;documentation value=&quot;Lookup by patient.&quot;/&gt;
       &lt;/searchParam&gt;
       &lt;searchParam&gt;
         &lt;name value=&quot;code&quot;/&gt;
         &lt;type value=&quot;token&quot;/&gt;
         &lt;documentation value=&quot;Lookup by code - this will be for the MLOM&quot;/&gt;
       &lt;/searchParam&gt;
     &lt;/resource&gt;

       &lt;!-- The Practitioner resource endpoint --&gt;
       &lt;resource&gt;
         &lt;type value=&quot;Practitioner&quot;/&gt;
         &lt;operation&gt;
           &lt;code value=&quot;read&quot;/&gt;
         &lt;/operation&gt;
         &lt;searchParam&gt;
           &lt;name value=&quot;name&quot;/&gt;
           &lt;type value=&quot;string&quot;/&gt;
           &lt;documentation value=&quot;Lookup by practitioner name. All parts of the name are searched.&quot;/&gt;
         &lt;/searchParam&gt;
         &lt;searchParam&gt;
           &lt;name value=&quot;identifier&quot;/&gt;
           &lt;type value=&quot;token&quot;/&gt;
           &lt;documentation value=&quot;Lookup by identifier. Both active and inactive practitioners will be returned.&quot;/&gt;
         &lt;/searchParam&gt;
       &lt;/resource&gt;

     &lt;!-- The Patient resource endpoint --&gt;
     &lt;resource&gt;
       &lt;type value=&quot;Patient&quot;/&gt;
       &lt;operation&gt;
         &lt;code value=&quot;read&quot;/&gt;
       &lt;/operation&gt;
       &lt;searchParam&gt;
         &lt;name value=&quot;name&quot;/&gt;
         &lt;type value=&quot;string&quot;/&gt;
         &lt;documentation value=&quot;Lookup by patient name. Only active patients will be returned. All parts of the name are searched.&quot;/&gt;
       &lt;/searchParam&gt;
       &lt;searchParam&gt;
         &lt;name value=&quot;identifier&quot;/&gt;
         &lt;type value=&quot;token&quot;/&gt;
         &lt;documentation value=&quot;Lookup by identifier. Both active and inactive patients will be returned.&quot;/&gt;
       &lt;/searchParam&gt;
       &lt;searchParam&gt;
         &lt;name value=&quot;birthDate&quot;/&gt;
         &lt;type value=&quot;date&quot;/&gt;
         &lt;documentation value=&quot;Lookup by patient birts date. Supports the :before and :after modifiers to allow for age ranges&quot;/&gt;
       &lt;/searchParam&gt;
     &lt;/resource&gt;
   &lt;/rest&gt;
 &lt;/Conformance&gt;

Updating the Medication List

In the previous post, we discussed using a List resource to represent a patient’s list of medications. In this post we’re going to talk about updating that list  – i.e. when a clinician changes the medications that a patient is taking, and wishes to record that change in the List. There are a few ‘gotcha’s to be aware of here. (By the way, do note that this discussion applies to any use of List – e.g. a list of conditions – as much as to medications).

Before we start, it’s important to appreciate that the List resource is something that ‘collects’ resources together (the other being the Group resource) – it contains a number of references to other resources that are stored somewhere else (often – but not necessarily – on the same server as the List).

Another thing to mention in passing is that much of what we are discussing here also applies to representing medications in other constructs such as FHIR Documents & Messages – though that is a big topic in its own right that will need to wait for another time…

Let’s consider the simple situation where a patient is taking 2 medications. There will therefore be:

  • 1 Practitioner resource (at least) as prescriber of medications and author (source) of the list
  • 1 Patient resource
  • 2 MedicationPrescription resources describing the medications
  • 1 List resource that has references (or pointers) to those resources.

This might look like this:

mlom v1a (1)

Note the links between the resources, representing the resource references – the meaning of those references should be evident. The List is labelled MLOM (My List Of Medicines) and has a specific code that identifies it as such as discussed in the previous post.

Now imagine that the Atenolol is stopped, and is replaced by Labetolol. This would give us the following picture:

mlom v2

Notes:

  • There is a new version of the List resource (It has the same ID as the previous one)
  • The new List version has a different ‘source’ practitioner – Dr Jones
  • We have indicated that the Prescriber of the medication is also the author (source) of the List.  They may be the same (as it is here), but they don’t have to be.
  • The new version of the List still points to the Atenolol resource – even though it has been stopped. We don’t strictly need this reference, but it is really useful as it suggests that the Atenolol was stopped (and possibly why) at the same time as the Labetolol was started. Of course the List resource and MedicationAdministration will have ‘formal’ properties that inform a consumer that the medication is no longer being taken – refer to the previous post for details. You can also set the date stopped in the MedicationPrescription directly as explained below.

So, lets walk through the sequence of actions that needs to occur in making this change in a RESTful fashion. We assume that we have the patient ID.

  1. GET the existing List of medications (eg GET /Patient/100/List?code=http://loinc.org|10160-0 which is a FHIR query that will return a bundle containing the List). Make a note of the ID of the List. (Note that we’ve been good here and added the LOINC namespace to the query).
  2. GET the medicationPrescription that we are stopping (its ID will be in the list), set the status property to ‘nullified’ and then PUT it back as an update. (You might also set the MedicationPrescription.dosageInstruction.timing.repeat.end to the date stopped if you are using a schedule datatype here).
  3. Create a new MedicationPrescription resource with the appropriate properties & references, and POST it as a new resource. Make a note of the ID that was assigned by the server (it will be in the Location header).
  4. Update the entry of the stopped medication in the List resource by:
    1. Setting the flag property to ‘cancelled’
    2. Setting the deleted property to true
    3. Optionally, add an extension to the entry indicating the reason why it was stopped
  5. Add a new entry in the List that references the new medication (which is why we made a note of the ID above).
  6. PUT a new version of the List back.

This isn’t particularly complex, but we can see that when we perform an update like this, there are a number of steps that must all succeed, or must all fail – i.e. this is really a transaction. We have a couple of ways of doing this.

  • The client can perform each step in turn as described above – checking that each one succeeds and taking responsibility for ‘rolling back’ any changes in the event that there is a failure or if some other client has updated any of the resources in the mean time. This could become complicated…
  • The alternative is to create or update all the resources on the client, and then place them into a bundle which is sent to the server to process as a single transaction, in which case the server takes the responsibility for ensuring the success – or failure – of the whole operation.

Lets walk through the same process as if it were a transaction – but first a few notes on using a bundle in this way.

In FHIR, a bundle is an Atom feed – and can be represented in both XML and JSON. I’ve already talked about this, and the spec has the definitive description, but just to point out a couple of things about the bundle entry elements that are pertinent. Each entry represents a FHIR resource and has a number of ID’s:

  • Entry.id is the logical ID of the resource – not the version specific ID. Ie it is always the same for any given resource.
  • Entry.link to self (<link rel=’self’…) is a version specific ID. Ie it points to a specific version of the resource. It’s an optional element, but its particular value here is that it will allow the server to apply update logic to avoid conflicts – as we will see in a moment.

Moving on, this is the process to update the List using a server transaction:

  1. Create a new bundle (an atom feed) that will hold all our updated and new resources and populate the required properties.
  2. GET the existing List of medications as described above. Make a note of the ID of the List.
  3. GET the MedicationPrescription that we are stopping, set the status property to ‘nullified’ and then add it to the bundle, setting the bundle entry.id to the ID of the MedicationPrescription. As Above, you might also set the medicationPrescription.dosageInstruction.timing.repeat.end to the date stopped if you are using a schedule datatype.
  4. Create a new MedicationPrescription resource with the appropriate properties & references and add it to the bundle. Create a temporary ID using the CID scheme and assign it to the bundle entry.id. (The server will know to replace this with a real ID).
  5. Update the resource of the stopped medication in the List entry by:
    1. Setting the flag property to ‘cancelled’
    2. Setting the deleted property to true.
    3. Optionally, add an extension to the entry indicating the reason why it was stopped (as shown in the previous post)
  6. Add a new List.entry to the List resource that references the new medication using the temporary ID that we created above, and setting the other properties of the entry as appropriate. (Unfortunately both List and Bundle use the word ‘entry’ which can be confusing).
  7. Add the updated List Resource to the bundle, setting the bundle entry.id to the ID of the List.
  8. POST the bundle to the root of the server.

The bundle will therefore contain:

  • 2 MedicationPrescription resources (1 new & 1 updated) and
  • 1 List resource

When the server receives the bundle, it will update and/or create all the resources as if they had been individually submitted – but will do so as a transaction, returning an HTTP statusCode to indicate success or failure.

The server can also implement other business and validation logic – for example checking that the version of the List resource (or any of the resources for that matter) in the bundle is the same as its current version on the server – i.e. the ‘optimistic locking’ pattern. In this case, the client should also include a “<link rel=’self’> “ element in each bundle entry that contains the version-specific URI of the resource so that the server can make the comparison. Refer to the discussion of the transaction and the bundle above for the details of this process.

So this method offloads the complexity of managing the transaction to the server, which ‘feels’ the right place to manage any transactional processing, and also minimizes the work for the client.

Incidentally, the ability for a server to require that an update operation specifies the version of the resource that it is updating can be applied to all update operations.

Notes:

  • If this process is repeated with further updates, you’d probably want to remove entries in the list that are already deleted – otherwise the List will bloat with lots of deleted medications.
  • You can always use the history operation to get previous versions of the List – ie the change history of medications for the patient. This allows you to create a ‘timeline’ of changes.
  • We suggested setting the MedicationPrescription.dosageInstruction.timing.repeat.end to the date the medication was stopped. This is not strictly the correct use of this property as it is more intended to represent an instruction (stop on this date) rather than a record (it was stopped on this date).

Representing a Patients list of Medications in FHIR

An interesting use case for FHIR is maintaining a list of a patients medications – i.e. the medications that they should be taking on a regular basis. Most EMR (Electronic Medical Record) systems will maintain such a list.

Each medication will have it’s own properties (such as drug name, dosage, how often to take, period of use and so forth), but for the purposes of this post we’ll focus on how to maintain the list on a day to day basis – including changes to the list – and consider the details of representing a single medication in a later post.

Knowing this list when treating a patient is, of course, of critical importance in healthcare as ‘medication related incidents’ – ranging from minor drug reactions through hospital admission and even death is a common cause of injury to the patient, as well as massive costs to the health system.

It is also important to know the history of changes to the list – i.e. when medications have been changed/stopped and new ones added, who made the change and the reasons why these changes have occurred. In some jurisdictions, this is called ‘reconciling’ the list from different sources of medication information such as dispense and administration information.

To represent this in FHIR, we’re going to need a number of resources.

Obviously we’ll need those that describe each medication. This is the MedicationPrescription resource that records the details of how a single medication should be taken. It includes references to the medication resource (representing the drug), the patient and the prescriber.

To represent the list that is active at any one time, we have a number of options.

Option 1: Query the MedicationPrescription resources for a patient

Each MedicationPrescription resource has a ‘status’ property whose value can be active, paused, completed or nullified. One option then is to get a list of all MedicationPrescription resources that have an active status like this:

GET /Patient/100/MedicationPrescription?status=active

(This uses the compartment feature to retrieve active MedicationPrescription resources for the patient with the ID of 100).

This would certainly work – but it becomes difficult to track changes over time – especially the reason for change.

Option 2: The document

Another option would be to place the medications in a FHIR document. We could have a section for the initial list of drugs and another section for the changed list of drugs. The advantage of this approach is that we can record the name of the person making the change, dates, locations etc. The disadvantage is that it’s a somewhat complex approach – requiring submitters and consumers to create and read documents.

Option 3: The List

There is a third approach that allows us to record these other data items as well as the actual list of medications – the List resource. This resource has been specifically created to maintain lists like this, and offers the following properties:

  • The type of list (we’ll use the LOINC code for ‘history of medication use’ to be consistent with CCDA). This is the code property, and is a codeableConcept datatype
  • The subject of the list – which will be a reference to the Patient resource
  • The source of the list. In this case it will be a reference to the Practitioner who created the list. The cool thing here is that when the list changes, we’ll create a new version of the list setting the source property to the Practitioner who changed it.
  • The date the list (or this version of the list) was created.
  • The mode of the list. In our scenario we’ll set this to ‘working’ as we intend it to be the on-going master list but there are other values that can be of use – eg ‘snapshot’ at a point in time which we may want to use in a referral for example.

Then there are any number of entries in the list. Each entry represents a single medication in the list, and has the following properties:

  • A reference to the medicationPrescription.
  • A flag for the entry. This allows us to indicate the nature of any change to the item between versions of the list – which is incredibly useful in reconciliation situations, or in Use Cases like representing the changes in medication when a patient is discharged from hospital. For example, one of the options could be ‘ceased’ to indicate that the patient was taking the medication on admission, but it was stopped while in hospital.
  • The deleted property. This allows us to explicitly state that this item is no longer active – it has been discontinued. In our case when we stop a medication we will also set the flag and the status in the

One thing that is missing in the entry is the reason for any change. For this we’ll add an extension to the entry element with a datatype of CodeableConcept and we’ll define a set of common reasons why a change occurred. In the example the extension is defined in a profile located at profile/mlomChangeReason

Putting all this together, the following example shows a medication list with two items:

  • Atenolol – which has been stopped due to an allergic reaction.
  • Labetolol – which was started in it’s place.

so the patient is actually only taking Labetolol. If you wanted the details of each prescription, you would get it from the reference in the list.
The list was created by Dr Welby on November 1st, 2011 for Mr I. Amanexample.

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-16&quot;?&gt;
&lt;!-- An example showinf a single item in the list - atenolol. As it is cancelled (due to an 
  allergic reaction), we know that the patient is not taking any medicines (as far as we know)--&gt;
&lt;List xmlns=&quot;http://hl7.org/fhir&quot;&gt;
  &lt;text&gt;
    &lt;status value=&quot;generated&quot;/&gt;
    &lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
      &lt;ul&gt;
        &lt;li&gt;Atenolol 50mg at night was stopped due to an allergis reaction&lt;/li&gt;
        &lt;li&gt;Labetolol 50mg at night was started as a replacement&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/text&gt;
  &lt;!-- This is the list Code - udentifying it as a list of medications --&gt;
  &lt;code&gt;
    &lt;coding&gt;
      &lt;system value=&quot;http://loinc.org&quot;/&gt;
      &lt;code value=&quot;10160-0&quot;/&gt;
      &lt;display value=&quot;History of Medication Use&quot;/&gt;
    &lt;/coding&gt;
    &lt;text value=&quot;Medication List&quot;/&gt;
  &lt;/code&gt;
  &lt;!-- The patient the list refers to --&gt;
  &lt;subject&gt;
    &lt;reference value=&quot;Patient/example&quot;/&gt;
    &lt;display value=&quot;Mr I Amanexample&quot;/&gt;
  &lt;/subject&gt;
  &lt;!-- who created the list --&gt;
  &lt;source&gt;
    &lt;reference value=&quot;Practitioner/100&quot;/&gt;
    &lt;display value=&quot;Dr Welby&quot;/&gt;
  &lt;/source&gt;
  &lt;date value=&quot;2013-11-01&quot;/&gt;
  &lt;mode value='working'/&gt;
  &lt;!-- The first medication entry --&gt;
  &lt;entry&gt;
    &lt;!-- The reason for change --&gt;
    &lt;extension&gt;
      &lt;url value=&quot;profile/mlomChangeReason&quot;/&gt;
      &lt;valueCodeableConcept&gt;
        &lt;coding&gt;
          &lt;system value=&quot;urn:oid:1.2.3.4&quot;/&gt;
          &lt;code value=&quot;ar&quot;/&gt;
          &lt;display value=&quot;Allergic Reaction&quot;/&gt;
        &lt;/coding&gt;
        &lt;text value=&quot;Developed a rash shortly after starting&quot;/&gt;
      &lt;/valueCodeableConcept&gt;
    &lt;/extension&gt;
    &lt;!-- Indicates this is a concelled medication --&gt;
    &lt;flag&gt;
      &lt;coding&gt;
        &lt;system value=&quot;urn:oid:1.2.36.1.2001.1001.101.104.16592&quot;/&gt;
        &lt;code value=&quot;03&quot;/&gt;
        &lt;display value=&quot;Cancelled&quot;/&gt;
      &lt;/coding&gt;
    &lt;/flag&gt;
    &lt;!-- So any consumner of this resource knows it is a cancelled med. The deleted property is a code with
    a fixed set of values, whereaes the flag is a codeableConcept, and the meaning depends on the terminology used.
    Every FHIR consumer MUST understand what a deleted property means...--&gt;
    &lt;deleted value=&quot;true&quot;/&gt;
    &lt;date value=&quot;2013-11-01&quot;/&gt;
    &lt;item&gt;
      &lt;!-- The reference is to the actual prescription resource. This will have details of dose, prescriber etc. --&gt;
      &lt;reference value=&quot;MedicationPrescription/100&quot;/&gt;
      &lt;display value=&quot;Atelolol 50mg at night&quot;/&gt;
    &lt;/item&gt;    
  &lt;/entry&gt;
  &lt;entry&gt;
    &lt;flag&gt;
      &lt;coding&gt;
        &lt;system value=&quot;urn:oid:1.2.36.1.2001.1001.101.104.16592&quot;/&gt;
        &lt;code value=&quot;04&quot;/&gt;
        &lt;display value=&quot;Prescribed&quot;/&gt;
      &lt;/coding&gt;
      &lt;text value=&quot;Started&quot;/&gt;
    &lt;/flag&gt;
    &lt;deleted value=&quot;true&quot;/&gt;
    &lt;date value=&quot;2013-11-01&quot;/&gt;
    &lt;!-- The reference is to the actual prescription resource. This will have details of dose, prescriber etc. --&gt;
    &lt;item&gt;
      &lt;reference value=&quot;MedicationPrescription/101&quot;/&gt;
      &lt;display value=&quot;Labetolol 10mg at night&quot;/&gt;
    &lt;/item&gt;
  
  &lt;/entry&gt;
&lt;/List&gt;

We can retrieve this list as follows:

GET /Patient/100/List?code=10160-0

Notes:

  • This is actually a query in FHIR, so would return a bundle containing the List resource – and there should only ever be one that matches for a patient.

In the next post, we’ll look at how we can record changes to the medication list, using the versioning mechanism in FHIR, as well as updates to the medicationPrescription resources as well.

Victor Chai on HL7 & SOA

Victor Chai on HL7 & SOA

An insightful presentation by Victor Chai (HL7 Singapore) on HL7 in a SOA world, from v2 through to FHIR 

Adventures in FHIR Searching: Getting a list of patients in a Ward

Had an interesting set of exchanges on the implementers skype chat yesterday talking about searching in FHIR.

The background was that we are looking at building a tablet app to support the recording of medication dispensings. Orion has an application (a Hospital Information System or HIS) that records this using a desktop interface, so the plan is to build a FHIR server that will act as a ‘proxy’ between the HIS and the tablet, with the tablet app consuming JSON FHIR resources over a RESTful interface.

We are thinking that the user will log into the tablet, then get a list of the wards they are currently scheduled on. They will select a ward, which will give them a list of patients, then select the patient from which they will get the medications to administer (or a similar type of workflow). I was asked to help out with the design of the FHIR resources & calls – and started to think about how to get as far as the patient list (I’ll consider the medication administration stuff later).

Looking through the current FHIR resource list I found a Location resource that specifically mentions that it models a Ward, so my first thought was to add an extension to that resource of type List, and then the List could reference a collection of Patients. The proxy server would be responsible for communicating with the HIS and constructing the bundle of these resources, which could be returned to the tablet app on request.

I mentioned this on the skype chat, and after some discussion with Grahame and Lloyd, we figured that a better solution would be to model the relationships using an encounter, and then we could do a FHIR query to get the data we need, using an _include parameter on the query for efficiency (I’ll get to this later).

So the relationship between the resources is like this:

ward query

Our approach will be:

  1. get the ward we want
  2. then get the encounters that are associated with that ward
  3. and then we can get the patients from the encounter.

Getting the ward is a straight forward query. Suppose the name of the ward is ‘Ward1’ then it’s a simple GET query against Location:

GET /Location?name=Ward1

Note that we will get back a bundle, and we may need to check for multiples – eg Ward1 and Ward11 would be valid matches (it’s a string search). We might also want to include a Location.type parameter if that is appropriate in our situation. But, we now have the Location resource– the Ward – and the ID of that resource. Let’s pretend the ID is 100.

Now to get the Encounters associated with that Location. Encounter does have a location property which is a sub-object with properties of Location & Period. In other words, an encounter can have many locations, and each location has a period over which the location was valid. This makes sense of course, as a patient can be moved around different places (Wards, radiology, therapy etc). So, we want encounters that are associated with our specific Location (Ward) right now. (There are other subtleties, but let’s keep it simple for now). We’ll assume that if there is a Location that matches ours, and where the period.end property is not present then that means they are currently associated with our ward.

However, there is a problem: There is no search parameter defined for location against Encounter – and in fact we need 2 of them – one for the location.location property and one for the location.period property. There are 2 things we can do:

  1. Petition the FHIR team to add these parameters
  2. Make these search parameters that our server supports (which is quite legitimate, though does mean our query on’t work on other servers)

We will do both. We will define 2 search parameters:

  • location that matches Encounter.location.location (which is a reference)
  • location-period that matches Encounter.location.period (which is a date).

and also ask the core team to add them (actually it is the HL7 committee that has oversight of this resource – you can look this up from this page, but sending a message to the core team via any of the supported channels will achieve this).

So now we have:

GET /Encounter?location =100 &location-period => 25-10-2013

(assuming that the date today is 25th October 2013)

So, now we have the encounters we want – all we need are the patients.

We could make a query for each patient – we can get that from Encounter.subject – but we can also ask the server to do that in our query by using the _include parameter. This is an instruction to the server to include the referenced resources in our query as well as the directly matched resources. So our complete query is:

GET /Encounter?location =100 &location-period => 25-10-2013&_include=Encounter.subject

will return a bundle that will contain all the encounters currently associated with Ward1 (resourceID=100), plus all the patient resources that are in these encounters. Job done!

Some notes:

  • We have defined some server specific parameters. Should the committee decide not to add these parameters to the spec, then either we accept this (after all we’re only concerned with our server at the moment anyway), or we could re-jig the query. Another exercise for the reader…
  • Servers are not obliged to support all search parameters – even if they are named in the spec. If we wanted our client to be able to talk to another FHIR server, then we would need to examine its conformance resource to make sure it does.
  • Servers are also not obliged to honour the _include parameter. If they don’t, then it simply means that we have more work to do client side – we just make direct requests for each patient after we retrieve the encounters.
  • In an earlier version of this post I was querying ‘into’ search parameters – eg GET /Encounter?location.period=… – it doesn’t work like that – it’s not like a path. Part of the issue is that we can’t have ‘ad-hoc’ queries into resources  – to quote Grahame: we’ve agreed that clients can’t simply launch random queries into the content of the resources – it has to be pre-agreed fields – there’s no way for the server to make that efficient, except in the case of special technologies 
  • Date querying is complicated. There’s a whole section in the spec on it.

This became a rather longer post than I intended, but hopefully it is a useful exercise to run through the thinking behind what became quite an elegant query – and I learned a lot doing it. It’s not the only way to do it of course, but it was a fun process!