I recently had an opportunity to explore HL7 FHIR in more detail (thanks to a lighter workload this week), and as a developer felt a need to actually write something.
Many of the FHIR implementations I’ve seen publicly hosted are greenfield reference implementations that demonstrate the resource based paradigms of this new messaging standard being developed by HL7. So, I did something novel, I tried to put FHIR atop an existing software implementation. But what to write? The answer was simple, I’d add FHIR to our client registry.
This post is just a rambling of some of the experiences I’ve had with FHIR as it pertains to our client registry reference implementation. I will share some opinions about how FHIR can be improved in a future blog post.
Getting Started
The first thing I did was reach out to http://hl7.org/fhir and download the C# reference classes for FHIR. After opening the solution I found that they weren’t annotated with .NET serialization attributes, rather you had to use the supplied serializers provided in the class library. While this is great for getting started on the client side I had trouble figuring out a way to integrate them with the WCF REST stack (not a complaint, I have this same issue with Everest and extended WebServiceHost serialization formatters is not a trivial task).
So I decided instead of piggy-backing on the existing FHIR classes provided by HL7, I’d write some XmlSerializer annotated classes from scratch (I’m only supporting XML for the client registry). This was a good way for me to learn the resources and class models.
Case Based Design
One thing that immediately popped out at me about FHIR was the fact that it was case based software design. Each resource, although linked to common data types and other resources, is like an island and the style of data representation isn’t necessarily common between them. Some resources use “code” to identify themselves, other use “identifier” or “name”. Coming from a v3 implementation background this was quite new to me.
For example, a Patient resource can have zero or more associations named “contact” with a class named “Contact” describing a relationship. When implementing the Patient resource I created a class named “Contact” and assigned a property on Patient of that type. However, the Organization resource also has zero or more associations named “contact” with a class named “Contact” … Only the Contact class in the Patient resource and the Contact class in the Organization resources are different (well, almost the same). Of course, that should not be confused with the FHIR data type “Contact” which represents a telecommunications object.
There are lots of examples of this in FHIR where there are several classes, conveying similar concepts, yet they are completely different. Something that could’ve been solved easily using …
Inheritance
Another thing that bugs me about FHIR (from an implementation standpoint) is the lack of object inheritance. Admittedly this was abused in HL7v3 (supporting extension and restriction), but used properly it could be of benefit to FHIR. Take again, for example, the “Contact” class (from either Patient or Organization). Would it not make sense to have a common ancestor, say “Person” containing properties “name, telecom, address, gender” and then extend that for use in the “Patient” or “Organization” resources. Even Patient or Practitioner could extend “Person” as they too carry those attributes.
True, deep object hierarchies are a bad thing but some level of inheritance can do quite a bit of good. It is the equivalent to saying “operator overloads could be abused so we just don’t support them” .. True, but when not abused it is a very useful tool (apologies for the Java bash there).
Extensions
I used to hate extensions in FHIR, but after implementing FHIR on the client registry I see why they’re present. There are lots of times (especially on the CR) where data provided from a patient feed (either PIXv3, PIXv2, or the pan-Canadian HL7v3 implementation) can’t be appropriately mapped to FHIR, or where FHIR is too limited in some respect.
For example, consider a PIX feed (v2 or v3) where a mother’s information is provided (http://cr.marc-hi.ca:8080/fhir/0.09/Patient/@27423 provides an example where Edna Brown is Patricia Brown’s mother). In FHIR I can represent this relationship via a “contact” association and a “RelationshipKind” of “parent” however I’ve lost data fidelity (the real relationship is “Mother”, but might have been “Step Mother” in either case “parent” is an applicable mapping). FHIR handles this quite nicely as I can simply extend the RelationshipKind element to add the original relationship code (if you look at the source document you’ll see that).
Personally I would have liked to seen extensions take the form of the WS-* extensions (new namespace, and well defined element structure etc.) however because the FHIR standard supports JSON in addition to XML this isn’t possible, so I think they’ve come up with a flexible enough structure for resources.
Changes … Frequently
I understand that FHIR is an evolving standard, however it would be nice to have more structured changes being posted. For example, I started implemented FHIR 0.09 on June 26th, was nearly finished by July 2nd. At that time there was an association on the Patient resource named “Details” pointing to a class called “Demographics”. On July 3rd (or 4th I can’t recall) I was nearly ready to deploy my solution and was just cleaning up some code, verifying resources, etc. However when I got around to the Patient resource, the Details association was gone and there was no longer a class called “Demographics”. Wait a minute! Was I dreaming?! Did I just hallucinate an entirely new class?
Turns out I didn’t, although the spec still read “FHIR 0.09” the structure of some of the resources changed. This is more of a version management / tagging issue than anything but still should be something to beware of.
Paradigm Shift
This last one isn’t a complaint about FHIR, it is a REST based standard. This last point speaks more to the pains of putting a REST based interface on an RPC based system. It was however, the most challenging part of the project (it still isn’t complete). REST is a paradigm shift from the event based (read RPC) model on which our client registry is designed. This follows the paradigm that HLv3 and HLv2 use where an event is processed and stored (patient was admitted, patient was merged, etc.). For this project I had to somehow map those events to RESTful CRUD operations.
It isn’t too bad for querying and reading (the only operations currently supported) as we can simply pluck contents out of the patient table however we do lose some data fidelity and context (at least in our implementation). It will cause us some grief as I move to extend the FHIR interface to support creation, update, and merge operations.
For example, it is possible on an admit (or register in v3) to specify the “custodian” of the data, or the location where data was entered, etc. All of which are used by many facilities including business rule enforcement and auditing. The registry has some context as to the event that occurred which resulted in the new record being created, updated, etc. In FHIR this context is a little more difficult to attain. To the FHIR interface (as with any REST system) we’re stuck with CRUD operations on the resource (to be fair extensions and/or the Transaction resource can be used to facilitate this, but then we’re just stuck putting RPC atop REST).
Final Thoughts
All-in-all, the FHIR implementation for the CR wasn’t too bad. It took about a week of effort to get read, version read, version history and query on the ValueSet (another discussion on its own), Profile, and Patient resources. When I have more time I’ll get to work on the create and update operations.
The final product is quite impressive. Assuming I’ve interpreted the FHIR documentation correctly; we now have a PIX manager that can accept v2, and v3 patient identity feeds and allow clients to query using rest (you can try it here : http://cr.marc-hi.ca:8080/fhir/0.09). There are approximately 15,000 patients in the registry (all randomly generated of course).
To assist in your exploring, here are some patients of note:
- Justin Fyfe (lots of versions as the result of Merge operations)
- Patricia Brown (has a relationship with her mother)
I will post more about this adventure as it progresses.
Hi Justin,
ReplyDeleteThis is really useful feeback. I'll react to some specifics:
* Serialization attributes: I haven't generated them because I was convinced I couldn't get the XmlSerializer to correctly handle our choice properties (say value[x]), if you found a solution to do that, I'd be happy to add serialization attributes to the model generator.
* About reusing classes and inheritance...we are avoiding re-use and inheritance in many places because of the experiences with v3: stuff might look the same, but it just isn't. Which means that documenting it or writing business rules will get context-specific (if Contact is used within an Organization, the following rules hold, if it is used within a Patient, other rules hold), so we keep them separate. The Demopgrahics class you mentioned is a good example: we got pushback against it, because many of its attributes were not relevant in all contexts...
* Frequent changes: I can fully imagine your pain, I need to do the same ;-) This is part of the openness of the project however. Version 0.9 is meant to go "live" in september, but in the meantime you have access to the very latest as we work on it. I try to post these bigger changes to our implementor's Skype channel, but that's easy to miss. Your only reference here is the build number (in the bottom of the left navigation frame), that's currently 1511 and will increase with every single change. I'll take a look if I can get this build number into the C# assembly version number.
All in all, I am very satisfied that you got stuff to work that quickly with just the current 0.9 version. We'll need your input and that of others to make it even faster and pleasant to work with.
Regards,
Ewout
Thanks for the reply Ewout.
DeleteUnfortunately I could only get the XmlSerializer attributes to behave in a manner which breaks the JSON serialization in WCF. This solution is fine for me (as I am only planning on supporting XML). I can understand these attributes probably won't work for the HL7 FHIR C# classes as that API needs to support JSON as well.
I can see that from a messaging standpoint inheritance really doesn't add much, but from an object model it does help. I've chosen to use some inheritance (keeping wire-level compatibility) for this project as I don't like typing more than I have to ;). There are also some good helper methods that can be added to help process resources, generate profiles, etc. I have a big backlog of "developer guidance" posts I've been meaning to write and I'll most likely include those in a future post.
Cheers
-Justin
hi Justin
ReplyDeleteThe REST issue will be the biggest one - some things just don't sit well with REST, and you've put your finger on the way they manifest in FHIR.
As for inheritance - it's something we've gone back and forward on. Though it comes naturally to object developers, that's the only place. And while there's superficial consistency in the structures, it often is consistent when you start providing definitions, etc. But I agree that we have some consistency issues to work on still. Especially around ancillary data like contact, custodian, provenance stuff