Template Versioning for Consolidated CDA (C-CDA)
The HL7 Structured Documents Working Group (SDWG) is discussing approaches to template versioning in Consolidated CDA (C-CDA). I suggest here a strategy that is consistent with current policy on identifiers.
The HL7 Structured Documents Working Group (SDWG) has had a lot of discussion recently about approaches to template versioning in C-CDA. The current approach is to provide a human-readable table in an appendix to the implementation guide that describes the specification.
Here is an excerpt:
|Name||Continuity of Care Document (CCD)||Continuity of Care Document (CCD) (V2)|
Keith Boone wrote several blog posts about the topic and the issues he has with template versioning as it stands.
I agree that having a change list in a document appendix is insufficient, however, the solution Keith presents is also problematic.
To summarize Keith’s solution, he proposes leaving the OIDs untouched and adding an extension of “2.0” to all templates that have changed.
While I have mulled over similar ideas, such as putting template publish dates in the extension, I steered away from those options because they hijack the existing semantics of template IDs. Template IDs are not just OIDs. They are Instance Identifiers. The following are all valid unique template Ids that could potentially require versioning:
<templateId root=”2.16.840.1.113818.104.22.168.2.17″ extension=”2.0”/>
<templateId root=”2.16.840.1.113822.214.171.124.2.17″ extension=”2013-09”/>
<templateId root=”2.16.840.1.1138126.96.36.199.2.17″ extension=”I made a new one”/>
<templateId root=”569619bc-22db-11e3-86b1-f23c91aec05e”><!—UUID instead of OID –></templateId>
If we override the meaning of the extension and redefine it as a version label, then it becomes impossible to version any template that uses an extension for another reason.
Instead of restricting the extension for this one purpose, we should accept that template IDs are opaque identifiers. We should then treat them as such and manage their versioning externally, leaving the internal structure of those IDs alone.
A Better Solution
Instead of constraining the extension to managing versioning in an instance, it is better to track template versions externally. A simple XML file or even a CSV provided with the C-CDA specification would be sufficient.
You can write transformations or search/replace patterns to update artifacts. The following XSLT does just that; it reads a file called templates.xml structured as above and updates a supplied CDA document, converting old template IDs into new ones.
<?xml version=”1.0″ encoding=”UTF-8″?>
<xsl:stylesheet version=”1.0″ xmlns=”urn:hl7-org:v3″ xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” xmlns:cda=”urn:hl7-org:v3″ xmlns:lcg=”http://www.lantanagroup.com” exclude-result-prefixes=”cda lcg”>
<xsl:output method=”xml” version=”1.0″ encoding=”iso-8859-1″ indent=”yes”/>
<xsl:variable name=”this-template” select=”.”/>
<xsl:variable name=”replacement” select=”document(‘templates.xml’)/lcg:templates/lcg:template[lcg:replaces[ (lcg:templateId[@root=$this-template/@root][@extension=$this-template/@extension] or lcg:templateId[@root=$this-template/@root and not(@extension) and not($this-template/@extension)] )] ]/lcg:templateId”/>
<xsl:attribute name=”extension”><xsl:value-of select=”$replacement/@extension”/></xsl:attribute>
<xsl:comment>Updated <templateId root=”<xsl:value-of select=”@root”/>” <xsl:if test=”@extension”> extension=”<xsl:value-of select=”@extension”/>”</xsl:if>/></xsl:comment>
Keith said he could do the “extension=’2.0’” update in 10 lines of XSLT. I’m sure that’s true. Unfortunately my XSLT skills are a bit rusty so this solution is 30 lines long, but it accomplishes the same thing without making assumptions about the internal format of the template IDs themselves. It took me a couple hours to come up with the templates.xml file structure and to write the XSLT to process it. Looking at it now, especially the ugly XPath statement at the top, an argument could be made that I should not be writing XSLT anymore :). I’m sure someone who writes XSLT more often than me can clean this up and come up with a more elegant version. My point though is that you don’t need to hijack the semantics of template IDs in order to make updates easy.
Keith’s Use Cases
Keith lists several use cases in his solution post. I list them here, along with counterpoints.
The XML and XSLT supplied above shows that you can accomplish the Gap Analysis that Keith discusses without making any changes to the existing sematics of template IDs.
I don’t buy the argument that adding a “2.0” extension makes life easier for all developers. So far, all balloted CDA specifications have only used OIDs for template IDs, and I’m willing to bet there are a fair number of folks out there who have written code that expects an OID and nothing else. Keith’s solution will cause a lot more work for them. Granted, they should fix their code anyway, but the claim that everyone will have an easier job is just wrong. Some folks are going to have a much harder time. How should they update the code? Should they assume that extensions will always be version identifiers? Will those identifiers be decimals, or could they be strings? The smart money would say to treat them as what they are, Instance Identifier types, in which case you need to manage versioning externally anyway since you can’t rely on any particular format for the extension.
Given a machine-readable file with explicit template replacements, model updates should be easy. Any self-respecting developer could do this update with a computable version history file and a few hours of scripting (though they might grumble about it).
Version Compatibility Testing
Just modify the XPath for the “replacement” variable at the top of the XSLT I provided and you can reverse the transform to “downgrade” a document and test compatibility with prior versions.
Other Use Cases
Below are some cases that Keith does not address.
Implied vs. Explicit Version Labels
Look at these two examples.
<templateId root=”2.16.840.1.1138188.8.131.52.2.17.2″ extension=”1.0”/>
It may not be obvious, but they represent two DIFFERENT template IDs. Yet people who blindly apply “2.0” extensions to their instances inadvertently think that these are synonymous when they later try to down-translate to older versions for compatibility. They would be wrong. The one with extension 1.0 DOES NOT EXIST. If someone writes code that processes extensions like version numbers and just decrements them by 1, they will end up with invalid documents. So even with Keith’s approach, you still need an external mechanism to link the correct template IDs, or some weird logic that says “subtract one, unless they result is 1.0, in which case remove the extension entirely.” Yuck.
Even if Keith’s recommendation is accepted, you should still track changes outside the instance, because who’s to say that this versioning mechanism will still be used after this release C-CDA? Maybe the HL7 Templates Working Group will recommend a different solution. If you just update extensions and the template working group picks another approach then you have even more work to do. If you accept that template IDs are opaque Instance Identifiers and process them as such then your efforts will work with any final solution. The examples below show that if you track template versioning externally, you can handle all cases.
<!– example 1: Current C-CDA approach –>
<!– example 2: Keith’s approach –>
<templateId root=”2.16.840.1.1138184.108.40.206.2.17″ extension=”2.0”/>
<!– example 3: Someone else using extensions in a valid way –>
<templateId root=”220.127.116.11″ extension=”I made a new one”/>
<templateId root=”18.104.22.168″ extension=”this is an old one”/>
<!– example 4: UUIDs–>
<templateId root=”569619bc-22db-11e3-86b1-f23c91aec05e” />
Documentation of Past Updates
Keith’s approach does not provide a way to go back to C32 or CCD 1.0. However, if the version relationships are managed externally this is not a problem. You can add old templates and their replacements to the version relationship file.
The FHIR ballot, section 1.10.0 (http://www.hl7.org/implement/standards/fhir/resources.htm), says this: “Ids are always opaque, and systems need not and should not attempt to determine their internal structure.” Enough said.
In the absence of a formal template versioning strategy from HL7, we should not hack C-CDA to put versioning inside instances. For those who are interested, I’ve included a link to my XSLT file, as well as an XML file and schema that handles template versioning for all the templates in the current balloted version of C-CDA.