One of the many changes that occurred in DSTU-2 (at least in the candidate spec) was the way in which transactions are managed in REST. Not so much what a transaction is (that’s reasonably well defined) – but how it is implemented. For a start, a bundle (used for a number of purposes as well as a transaction) is now a resource in it’s own right (rather than an Atom feed).
But apart from this structural change, the transaction has become more closely aligned with the simpler REST interactions – it’s easier to see how a transaction can be thought of as a sequence of simple interactions that succeed or fail as a whole. Let’s start by taking a look at the Bundle from the perspective of Transactions, including some more tricky aspects to how IDs are managed when you want to create a resource that needs to be referenced by another within the same transaction.
The final scenario that is a candidate for the clinical connectathon is one that deals with allergies and Intolerance. Here are the details, but at a high level:
A patient is prescribed penicillin and 8 days into the course develops symptoms that are diagnosed as an allergy to penicillin, which is recorded in their allergy list, along with details of the nature of the allergy.
In addition, the patient (through a patient portal) updates their allergy list – adding an entry that is subsequently updated (or reconciled) by the clinician.
FHIR supports transactions by POSTing a bundle of resources to the root of a server. The server processes them separately as if they had been sent as individual requests – except that the entire group of updates either succeeds or fails as a group (which is what a transaction is, after all…)
It’s one of the more complex processing that a server has to do – particularly as it needs to manage references between resources for new and existing resources within the bundle.
But there’s one particular aspect to this, and that is what if you don’t know if the resource already exists on the server?
For example, we’re working on a project to import glucose data from a personal device. Our current thinking is to create a bundle containing a Patient resource, a Device resource and Observation resources for each of the glucose results we want to save. (We’re probably not using the DeviceObservationReport as it doesn’t add anything extra for us in this scenario).
So when the client creates the bundle, what does it use for the Patient ID – and the Device ID for that matter? (The Observations will always be new, so cid: IDs are appropriate for them). We do have an identifier for both, but that’s not the same as the ID – and having to look up the resources before putting them in the bundle kind of defeats the purpose. And if we include the resource anyway with a cid: ID , then the server will always create a new one – not what we want.
The answer to this is a couple of paragraphs in the spec that talks about how a server can manage this. Here it is (it’s in the transaction description):
The application constructing a bundle may not be sure whether a particular resource will already exist at the time that the transaction is executed; this is typically the case with reference resources such as patient and provider. In this case, the bundle should contain a candidate resource with a cid: identifier, and an additional search parameter using an Atom link:
A search link with a root of http://localhost means to search the local resource store for a match as specified in the parameters (which must conform to the servers capability for searching as specified in its conformance statement). If the search returns no matches, the server process the resource normally. If the search returns one match, the server uses this matching resource instead, and ignores the submitted resource. If more than one resource is found, the transaction SHALL be rejected.
This means that we can include the identifier of the Patient and the Device in the bundle, and the server will locate the resource if it exists – and create it if it does not.
So here’s a sample of a patient resource in a bundle:
Note the <link> element where we specify that the search parameter is an identifier with the value of PRP1660. (and, of course, we have this identifier in the candidate resource – otherwise it will never work!)
This is incredibly useful! In fact it does raise the issue of whether a server claiming to be able to process transactions in its conformance statement SHALL be able to implement this functionality. I don’t think the spec is clear on this – and probably needs to be.
While at the HINZ conference this year, I was at dinner with Grahame and ‘Friendly FHIR’ (your identity is safe with me), and we were talking about the roles of the Identity Source and XDS Registry in the context of a FHIR XDS implementation. In previous posts I had indicated that the Identity Source could simply expose a FHIR Patient endpoint – and that is still an option for general use – but in the case of the XDS Registry, Grahame pointed out 2 issues with that approach:
If the XDS infrastructure is heavily used, then the load on the Identity Source is going to be high
Merged patients are an issue because DocumentReference resources in the registry will continue to have references to patients that have been merged with another and it needs to return all references to all merged patients when responding to a query for a single patient.
So, the registry really does need to maintain it’s own store of patients – though this is really a specialized ‘cache’ that is only updated by the Identity Source. This also means that there is really no need for a Document Consumer to access the Identity Source directly – at least in the XDS context – which is a bonus.
We’ve talked before about how FHIR represents merged patients (Actually, just checking the specs I note that there is now a link type property that must be set to ‘replace’ to indicate the merge), so I won’t repeat that here, but it is worth thinking about how this could all work in the context of an Identity Source needing to update an XDS Registry.
The requirement is that when there are changes to patient information at the identity source, then that needs to be communicated to the XDS registry somehow. We don’t need all the patient details all of the time – the more we store then the greater the volume of updates – but we probably want the ID, the links, the identifier information and the basic name/Date of Birth/Gender stuff. We would want to be notified about:
new patients
changes to the patient data we are storing
any merges
The XDS registry then needs to maintain an internal data store of some sort that maintains those links between patients so that when a document consumer queries the registry, all the DocumentReference resources for that patient and any patients merged to it are returned as well. (And note that each DocumentReference resource will continue to refer to the Patient it was associated with at creation time – it is the patient that is merged and not the other resources that refer to it).
There are a couple of ways we could do this:
Either the Identity Source could push changes to the XDS registry as they occur (a ‘push’ model)
Or the Registry could periodically request changes from the Identity Source. (a ‘pull’ model).
Lets consider the push model first.
When the Identity Source merges a patient with another, there are 2 Patient resources to change:
The Primary resource (the one you want to keep) has a reference to the secondary resource (that is being replaced) with a link type of ‘replace’.
The Secondary resource has a link back to the primary (also with a type of replace) and has it’s ‘active’ property set to false.
As these really need to be managed as a transaction, then the Identity Source places the two resources in a bundle and either:
Posts it to the root of the XDS registry
Or (and this is probably more explicit) the XDS registry exposes a service (maybe /service/patient/merge) that takes the bundle of resources
In either case, the registry updates its data store. It might return an OperationOutcome resource to indicate the outcome.
So that is the push from the Identity Source to the XDS registry – how would the pull model work?
Well, because FHIR is built using standard internet type technologies, there’s a pre-existing standard that is intended for communicating changes and updates between systems, and that is the Atom feed – which we’ve seen used as the bundling mechanism when we want to move multiple resources around.
In this use, we’re implementing a ‘pub/sub’ architecture where the client (the XDS registry) subscribes to a feed of changes published by the Identity Source. Reviewing the spec, we note that the history operation allows a server to publish changes for a specific resource, a type of resource, or all resources on that server. In our use case, we’d go for the middle option – we want a feed from the Identity Source that publishes all changes for any Patient resource.
So what happens is something like this:
Periodically the XDS registry issues a GET request to the Identity Source like this:
GET /identityServer/Patient/_history?_since=<dateLastQueried>
This will return a bundle containing all the changed resources since the last request. The registry can then process the Patient resources in that bundle and update it’s internal data store as required.
So which pattern to choose? Well, that depends on your implementation.
The ‘classical’ XDS way is the push from Identity Source to Registry, and that is probably the simplest (though in practice you might want to put an integration engine in the middle so the Identity Source doesn’t need to be concerned with communication issues – and could support other systems that maintain patient caches).
However the pull feed option might be useful if there are multiple clients needing updates, of if that technology is already supplied by the Identity Source.
Hopefully that finishes off XDS stuff for a while, because I’d like to start thinking about FHIR documents…
It’s not uncommon for a document to need to be updated after it has been posted to a repository and registry. Sometimes new information has come to hand that affects the document, or there may be normal workflow where a ‘draft’ document is saved, and is subsequently updated or finalized, or maybe the original was just plain wrong and needs to be retracted.
None of these are unique to a document of course, but there are some ‘quirks’ – for want of a better word – to the DocumentReference resource for a couple of reasons:
It is a resource whose main role is to refer to another entitity – so there are 2 lifecycles to consider.
Documents are often summaries of clinical care (e.g. Discharge Summaries, Progress notes, Clinic notes) and it’s very common for clinical decisions to be made on the contents. Keeping an easily accessible (to the end user) ‘audit trail’ of changes is very important for medico-legal reasons.
When thinking about changes to a document, there are 2 ‘types’ of change to consider:
Changes to the metadata (e.g. the document type was incorrect)
Changes to the content of the document itself.
We’ll consider these separately.
Changes to the metadata
If the change is only to the metadata – the contents of the DocumentReference resource itself, then it can be updated it in the usual way – i.e. retrieve the existing version, make the changes, and PUT the document back. The standard FHIR versioning process will manage the update of the resource. (Resource versioning is not required by FHIR – but highly recommended). There are some caveats to this approach, as mentioned in a minute…
Changes to the document.
This is more tricky. What we need to do is:
Update the document in it’s location (e.g. as a binary resource on a FHIR /Binary endpoint)
Update the existing DocumentReference resource that points to it, setting the status property to ‘superseded’
Create a new DocumentReference resource that references the updated document (and if it’s a FHIR server that we may want to make this a version specific reference), setting the relatesTo.code to ‘supercedes’, and the relatesTo.target as a reference to the original DocumentReference resource
We might also want to create a provenance resource to indicate who has made the change.
Because this needs to be managed as a transaction, we’ll use a FHIR bundle and we’ll use the same pattern as when we submitted the document in the first place – i.e. have the repository server manage the updating process. (Other patterns are possible of course).
So our bundle will contain:
The updated document as a base-64 encoded binary resource. It will have a real ID so the server knows to update it
The original DocumentReference resource, with updated status. It will also have a real ID so the server knows to update it
The new DocumentReference resource – largely a copy of the original with the relatesTo property set. It will have a ‘cid:’ ID so the server knows to add it as new.
Optionally a provenance resource with the details of who made the change – and when. The provenance resource refers to the resource being changed (not the other way around) so it will have 2 targets – the 2 DocumentReference resources that are effected in this transaction. It also has a cid: ID.
The repository server will process the bundle as described above.
Although this second approach is more complex, it does make it quite obvious that a significant change has occurred. For this reason, some metadata changes – such as a change of subject or author – might be better managed this way rather than a simple update to the DocumentReference resource. It’s up to you…
All of this does raise an interesting point that we didn’t think about when considering how to query the registry for documents (i.e. a query against /DocumentReference) – the response will include DocumentReference resources that have been superseded as well as current ones.
If we don’t add the status parameter in the query, then we will get back all DocumentReference resource’s – so we will need to check this value when assembling the list of documents for the consumer (Of course, we may want to do this anyway so that we can place some visual indication in the display that changes have occurred – it’s up to the implementer, as it should be…)
Otherwise we can be explicit that we only want current documents, so the examples we gave in the previous post becomes:
And as a last word, and following on from the post that Keith Boone made about task orientated services, we may choose to define specific service end points for this functionality (and even for the ‘add document’ scenario), perhaps:
/service/document/new
/service/document/update
both of which receive the bundles we’re described.
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):
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…
<?xml version="1.0" encoding="utf-8"?>
<Conformance xmlns="http://hl7.org/fhir">
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>This conformance statement supports the Shared Medication repository, and specifies the following endpoints</p>
<p>Person: Read and Search on name and identifier</p>
<p>Practitioner: Read and Search on name and identifier</p>
<p>MedicationDispense. Create, and search on patient,whenHandedOver</p>
<p>MedicationPrescription. Read. </p>
<p>List. Create and search on code,patient. Version read.</p>
<p>SecurityEvent. Search on patient,date</p>
<p>Transaction interfaces to update the Medication List</p>
</div>
</text>
<identifier value="68D043B5-9ECF-4559-A57A-396E0D452311"/>
<version value=".1"/>
<name value="My List Of Medicines (MLOM) Conformance Statement"/>
<publisher value="Elbonian MOH"/>
<telecom>
<system value="email"/>
<value value="wile@elbonia.govt"/>
</telecom>
<description value="The FHIR endpoints required to support a regional Medication repository - My List Of Medicines"/>
<date value="2012-10-14"/>
<software>
<name value="MLOM"/>
<version value="0.34.76"/>
</software>
<fhirVersion value="0.12"/>
<acceptUnknown value="false"/> <!-- this system does not accepts unknown content in the resources -->
<!-- this system can do either xml or json. (Listing both implies full support for either, with interconversion) -->
<format value="xml"/>
<format value="json"/>
<!-- We only support REST interfaces at this time. This includes transaction to the server root to update the List-->
<rest>
<mode value="server"/>
<!-- SecurityEvent record -->
<resource>
<type value="SecurityEvent"/>
<operation>
<code value="read"/>
</operation>
<searchParam>
<name value="patient"/>
<type value="reference"/>
<documentation value="Lookup by patient."/>
</searchParam>
<searchParam>
<name value="date"/>
<type value="date"/>
<documentation value="Lookup by date the event occurred."/>
</searchParam>
</resource>
<!-- MedicationDispense record -->
<resource>
<type value="MedicationDispense"/>
<operation>
<code value="create"/>
</operation>
<operation>
<code value="read"/>
</operation>
<searchParam>
<name value="patient"/>
<type value="reference"/>
<documentation value="Lookup by patient."/>
</searchParam>
<searchParam>
<name value="whenHandedOver"/>
<type value="date"/>
<documentation value="Lookup by date the medication was given to the patient."/>
</searchParam>
</resource>
<!-- MedicationPrescription resource. The prescription records are all created through the 'transaction' process so read-only -->
<resource>
<type value="MedicationPrescription"/>
<operation>
<code value="read"/>
</operation>
</resource>
<!-- List resource. Used to support the List of Medications. -->
<resource>
<type value="List"/>
<operation>
<code value="create"/>
</operation>
<operation>
<code value="read"/>
</operation>
<operation>
<code value="vread"/>
</operation>
<searchParam>
<name value="patient"/>
<type value="reference"/>
<documentation value="Lookup by patient."/>
</searchParam>
<searchParam>
<name value="code"/>
<type value="token"/>
<documentation value="Lookup by code - this will be for the MLOM"/>
</searchParam>
</resource>
<!-- The Practitioner resource endpoint -->
<resource>
<type value="Practitioner"/>
<operation>
<code value="read"/>
</operation>
<searchParam>
<name value="name"/>
<type value="string"/>
<documentation value="Lookup by practitioner name. All parts of the name are searched."/>
</searchParam>
<searchParam>
<name value="identifier"/>
<type value="token"/>
<documentation value="Lookup by identifier. Both active and inactive practitioners will be returned."/>
</searchParam>
</resource>
<!-- The Patient resource endpoint -->
<resource>
<type value="Patient"/>
<operation>
<code value="read"/>
</operation>
<searchParam>
<name value="name"/>
<type value="string"/>
<documentation value="Lookup by patient name. Only active patients will be returned. All parts of the name are searched."/>
</searchParam>
<searchParam>
<name value="identifier"/>
<type value="token"/>
<documentation value="Lookup by identifier. Both active and inactive patients will be returned."/>
</searchParam>
<searchParam>
<name value="birthDate"/>
<type value="date"/>
<documentation value="Lookup by patient birts date. Supports the :before and :after modifiers to allow for age ranges"/>
</searchParam>
</resource>
</rest>
</Conformance>
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:
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:
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.
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).
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).
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).
Update the entry of the stopped medication in the List resource by:
Setting the flag property to ‘cancelled’
Setting the deleted property to true
Optionally, add an extension to the entry indicating the reason why it was stopped
Add a new entry in the List that references the new medication (which is why we made a note of the ID above).
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 thelogical 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:
Create a new bundle (an atom feed) that will hold all our updated and new resources and populate the required properties.
GET the existing List of medications as described above. Make a note of the ID of the List.
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.
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).
Update the resource of the stopped medication in the List entry by:
Setting the flag property to ‘cancelled’
Setting the deleted property to true.
Optionally, add an extension to the entry indicating the reason why it was stopped (as shown in the previous post)
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).
Add the updated List Resource to the bundle, setting the bundle entry.id to the ID of the List.
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.
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).
Recent Comments