FHIR enabling an immunization registry – part 2

In the previous post we discussed how an immunization registry might receive Immunization resources documenting the administration of vaccines to an individual (complicated), and how to expose those resources to a client (easy).

But vaccine administrations don’t exist in a vacuum – they’re generally related to some particular plan consisting of multiple administrations of different vaccines over a period of time with the intent of providing the recipient with protection against specific diseases. (And, as COVID has emphasized, protection of the planetary population as a whole).

So we need a way to represent that general plan, customize it for an individual – and then a way to link individual vaccine administrations back to that plan so we can be sure that an individual is receiving the vaccinations they should be. And we want to be able to report on this administration at a population level as well as the individual.

That’s a bit more complicated than it might appear.

As in the previous post, we’ll use the paediatric immunization protocol as the basis for discussion – actually the New Zealand one – though I expect that other countries have something similar.

So – the requirements.

  • We want to be able to represent the National immunization plan (or protocol) in a computable format
  • We want to be able to customize that plan for an individual to take account of variations that might exist such as allergies/intolerance to vaccine components, changes to the plan over time (new diseases or vaccines) or where an individual hasn’t been following a plan thus far and needs to catch up.
  • We want to be able to link the vaccine administrations (the Immunization resource) back to the plan in some way to support reporting at the individual level (this person is protected) and the population level (we have x% coverage for measles in our community). We could do this directly at the time of recording the administration (i.e. the Immunization resource), or ‘after the fact’ – perhaps when generating the patients customized plan or reporting on the population).

Now let’s think about the resource types that we might use.

We’ve already identified that the PlanDefinition / ActivityDefinition resources are the logical places to record the overall protocol – though we have yet to look into the detail. We’ll get to that in a moment.

There are (at least) a couple of options to represent the individual patients plan:

  • The CarePlan resource is the one focused on individual care delivery – and can be used in a wide range of scenarios. A patient can have multiple CarePlans, each with a specific focus, so a CarePlan that records the immunizations they should receive seems like a good idea.
  • But there’s also a resource specifically focused on Immunization plans – the ImmunizationRecommentation resource. And the ‘boundaries’ section in the CarePlan page states quite clearly that ImmunizationEvaluation is preferred when the focus is on vaccination.

So, we’ll go with ImmunizationRecommendation for an individuals immunization care plan.

With regard to linking the Immunization back to the plan, as stated above this could either be a property of Immunization that refers to the plan (either national or patient customized – or both), or it could be something that we do when generating the patients ImmunizationRecommendation resource.

Before we decide which approach we’re going to take to do this, let’s take a closer look at how we will use the PlanDefinition to represent the national program. We’ll return to this question when we discuss creating the ImmunizationRecommendation in a later post, as we’ll surely need to know what previous Immunizations a person has had at that time.

So, PlanDefinition.

PlanDefinition is a very complicated resource – intended to be able to represent a wide range of clinical reasoning scenarios. We’re only going to want a small subset of those capabilities, and we’re going to want to create profiles on both PlanDefinition and ActivityDefinition to describe how we’re using them.

We could start by defining the profiles, and then seeing how well they meet our requirements, but given that we actually have the paediatric protocol that we want to represent, let’s do it the other way around. Let’s create the PlanDefinition that represents the protocol, then create the profiles after that to describe what we did. That way we know that the profiles work (at least for us) and that they’re ready for others to take a look at and comment.

If we’re going to create examples, then we’re talking FSH and Sushi. Yes, you could create the examples manually or using other tools but FSH is way easier – both to generate the example instances and to understand what’s in them. In fact, in this post we’ll show the FSH as we go rather than the XML or Json representations. But we’ll wrap everything up into an Implementation Guide (IG) and publish into the build environment, so people can see what we’re doing – and download the completed examples if they want.

And we’re going to want an IG eventually anyway, so it’s good to get started. If you want to set this up yourself, the FSH School is the place to start.

If we take a look at the paediatric immunization protocol, we see that it defines a set of patient ages, and the specific vaccines that should be taken at that age. There’s quite a nice representation as a matrix with the ages as rows and the vaccines as columns, that makes it easy to understand (at least for me).

As well as the metadata about the plan, PlanDefinition contains any number of action elements which can also contain other action elements – in other words it’s like a hierarchical tree. It makes sense that the ‘top level’ of actions should be the age of administration, and the ‘child’ actions nested below that to represent the vaccines to give at that time.


6 week Immunization

            DTap-IPV-HepB/Hip vaccine

            PCV10 vaccine

            RV1 vaccine

3 month Immunization

            DTap-IPV-HepB/Hip vaccine

            RV1 vaccine

And so forth for the rest of the protocol.

Each child action refers to the administration of a single vaccine. The action has quite a number of elements related to documentation, timing, workflow and other aspects – but it’s a bit light on the details of exactly what the action is to do. For that we need the ActivityDefinition resource, where we can place details of the vaccine to administer, body site, dose and such like. We’ll need that data when we create the ImmunizationRecommendation.

So to represent the first couple of immunizations we’re going to need:

  • The PlanDefinition
  • Three ActivityDefinitions – one for each vaccine

Here’s the FSH for the actions in the PlanDefinition:

It should be reasonably straight forward to understand, but some things to note:

  • The rules that only have a single action in them represent the top level actions – the patient ages when the action takes place. These have got the age (as timingAge) as well as 4 behavior elements that basically state that all the child actions should take place once at the appointed time unless there is a documented reason why they shouldn’t (such as allergy). We’ll think a bit more about this when we discuss plan customization in the next post.
  • Each top level action has a number of child actions – one per vaccine to administer. Each child has a title and a reference to the ActivityDefinition with the details of the vaccine. The reference to ActivityDefinition is not the usual FHIR reference to a resource id – rather it specifies the canonical url of the ActivityDefinition which is globally unique. I’m not sure why it doesn’t use the ordinary way, but I’m sure there is a good reason – and anyway, that’s how it works.

Now for the ActivityDefinition – here’s an example:

It’s still incomplete, but note:

  • The url matches the one from the PlanDefinition.action
  • The vaccine to administer is in the productCodeableConcept. This would almost certainly be a SNOMED code (at least in New Zealand) but for the purposes of this design work, I made up a CodeSystem to use. ($vaccCode is an alias to that CodeSystem)

So using the sushi / IG publisher setup on my machine, I can build an IG containing the examples and publish it to the build environment for others to see and comment on. And the IG has a link back to the github repository (in the yellow box at the top) so people can see the source code (ie the FSH and other documentation) should they wish to. When the time comes to develop the profiles, it will be straightforward to add them – and we get the added bonus of example validation at the same time! Nice…

So that’s enough about the PlanDefinition/ActivityDefinition for the moment (we have lots of detail we need to add, but the skeleton is there).

Next post will start to think about creating the ImmunizationRecommendation resource for an individual. This will need to look at any existing Immunization data, as well other clinical data such as allergies that may affect the recommendation. And there’s also the need for ‘catch up’ immunizations if the person is not currently up to date, or there has been a change to the protocol.

About David Hay
I'm an independent contractor working with a number of Organizations in the health IT space. I'm an HL7 Fellow, Chair Emeritus of HL7 New Zealand and a co-chair of the FHIR Management Group. I have a keen interest in health IT, especially health interoperability with HL7 and the FHIR standard. I'm the author of a FHIR training and design tool - clinFHIR - which is sponsored by InterSystems Ltd.

4 Responses to FHIR enabling an immunization registry – part 2

  1. Pingback: Dew Drop – March 29, 2021 (#3411) – Morning Dew by Alvin Ashcraft

  2. Pingback: FHIR enabling an Immunization registry- part 3 | Hay on FHIR

  3. Craig Newman says:

    Wondering if it’s possible to develop the PlanDefinition in the “other” direction. Rather than grouping by age, group by vaccine or vaccine preventable disease. In the US, the CDC sponsored the CDSi project (https://www.cdc.gov/vaccines/programs/iis/cdsi.html) translated vaccination recommendations into structured data (Excel and XML rather than FHIR) and organized by the vaccine because sometimes there are multiple different paths to immunity that require different doses (sometimes of different products) as different ages and intervals.

  4. Craig Newman says:

    ImmunizationEvaluation also exists as a resource to capture the validity of a particular immunization event against a particular protocol. These are typically relevant when a set of recommendations is being generated and are used to help determine the patient’s recommendations. It gets complicated because it’s possible that a given dose may be simultaneously valid for one purpose but invalid for another. For example, in the US, a dose of MMR vaccine can be given at 10 months. For the standard protocol, this dose is considered invalid because the first dose should not be given until 1 year. But if the child was going to be travelling internationally, somewhere were measles is present, then a dose is actually recommended and the dose is valid for that purpose. So it’s simultaneously valid and invalid (I’m sure there’s a Schrodinger’s cat reference in there somewhere).

Leave a Reply