Using Rhapsody as a FHIR® Façade (Part Two)
In the last post, we discussed the overall architecture of our project – providing a FHIR® façade to a database to retrieve and store encounter data. Before we start on the Rhapsody configuration though, we do need to do a bit of design work. Specifically, we need to map the information in the database to FHIR® resources and decide what API’s we’ll use — the specific query patterns that we’ll support.
Let’s start with the database structure. Assuming that it’s a reasonably normalized structure, then it’s going to look something like this:
Obviously a very simple schema – but enough for us to work with — there’s an encounter table with linked patient and clinician tables.
So, the question is, how will that map to FHIR®? Well, as it turns out — in this case — very easily! In fact, each table will map to a single resource as follows:
Patient table → Patient resource
Encounter table → Encounter resource
Clinician table → Practitioner resource
It won’t always be as clean as this is a real implementation of course but, generally speaking, if the database has been properly normalized, then it will happen more often than not. Here’s how this will look like with FHIR® resources – assuming a single patient with two encounters – each with a different clinician. The first is a visit to the Doctor with a sore throat, the second a visit to the Emergency Department with a rash. (Reading between the lines we can guess a reaction to antibiotics – but that data isn’t in the database, so we can’t display it).
Here’s the model we created:
(The model was generated using the Scenario Builder in clinFHIR. This is a useful on-line tool for visualizing FHIR® instances, and especially the relationship between them. You can access it at http://clinfhir.com/ — it’s free to use).
So you can see that there are two Encounter resources, each referencing a different Provider and a single patient resource that both Encounters reference. The reference between resources is analogous to the joins in the database. In the real world, this is not always as clean, for example if there was a single database table with both encounter and provider data, we’d still want to pull it out into separate resources.
Next up we need to map the data items from the database into the individual resources. Essentially this is a table per resource with the database attributes in one column and the resource elements in the other. This can be a little tricky in practice due to a number of factors.
Database elements tend to be ‘simple’ types (string, date, integer, etc.), whereas in FHIR® an element can be more complex than that. Take the patient name for example, in the database, there are separate fields for first and last names, whereas the corresponding element in the resource (Patient.name) has a data type of ‘HumanName’, which has child elements for first and last names (and other children besides). So, two fields from the database will be in one element in the FHIR® resource.
Some elements in FHIR® are coded – they refer to entries from a ‘terminology’ – like SNOMED or LOINC, or even one defined by FHIR®. Technically, each coded element in FHIR® is ‘bound’ to a set of possible values using a special FHIR® resource called a ValueSet. This is a big topic so we won’t go into the nuances of it here (let me know if you’d like more information in the comments below). In our example, there are three coded entries: Patient.gender, Encounter.class (the encounter type) and Encounter.reason.
Some elements in FHIR® resources are required (not too many, fortunately). For example, Encounter.status is required (and is coded as well). We don’t have status in the database, so we’re going to need to figure out how to represent it in the FHIR® resource. In our simple case, we’ll hard code it to ‘finished’, but it may not always be that easy. (And we’ll need to do more work when we come to creating encounters in the database).
There may be elements in the database that don’t have an obvious home in the FHIR® resource. However, we can still represent them in the FHIR® resource using extensions. Here’s a rather light-hearted explanation I gave a few years back - https://fhirblog.com/2014/03/06/extensions-are-not-second-class-fhir-citizens/ and the more formal description in the spec: http://hl7.org/fhir/STU3/extensibility.html.
We should also populate the resource text property. This is not strictly required, but good practice, so we should do so if we can.
Putting all that together – here’s what one of the encounter resources is going to look like when we’re done (also built using clinFHIR):