ValueSet Secondary Coding Extension¶
| Canonical | ../StructureDefinition/valueset-secondary-coding |
|---|---|
| Status | draft · 1.26.0 |
| Base | Extension (constraint) |
| Context | ValueSet.expansion.contains (element) |
| Source | FSH · JSON |
A second coding for one member of a pre-expanded ValueSet, carried on that member beside its own
system and code. Repeats: a member with two secondary codings carries two.
FSH
The authored source of truth: extensions/ValueSetSecondaryCodingExtension.fsh (edit on GitHub). One FSH file may define several related artifacts.
// ValueSetSecondaryCodingExtension.fsh
// A second code for one member of a pre-expanded ValueSet, carried on the member itself.
//
// WHY AN EXTENSION AT ALL. `ValueSet.expansion.contains` in R4 carries a member's `system`, `code`
// and `display` and nothing else -- there is no per-member property, and no other element on the
// resource belongs to one member. A curated list that wants to say something more about each of its
// members has nowhere in the base resource to say it. This extension is that missing slot.
//
// WHY A CURATED LIST WANTS ONE. These lists are shipped pre-expanded and filtered in the client, so
// the picker holds the whole member in hand at the moment of choosing. Carrying the second code on
// the member lets the picker emit a complete `CodeableConcept` -- the member's own coding, then each
// secondary coding -- instead of resolving a map afterwards, at a point where the map would have to
// be fetched, cached and kept in step with the list.
//
// READING IT BACK: match on the VALUE's `system`. The extension URL says only "there is a second
// code here"; what that code IS comes from `valueCoding.system`. Repeats are unordered and a member
// may carry more than one, so nothing may be inferred from position, and nothing from the URL.
//
// IT ANNOTATES MEMBERSHIP, IT DOES NOT DECIDE IT. A concept is in the set because `compose` includes
// it. This adds a code to a member and never adds or removes one.
//
// ────────────────────────────────────────────────────────────────────────────────────────────────
// ONE CANONICAL, TWO PRE-MIGRATION ADDRESSES, AND THAT IS A JUDGEMENT RATHER THAN AN OVERSIGHT.
//
// The software writes this concept at two addresses on a host this specification does not govern.
// The older of the two is spelled for one classification; the newer is general. They are not two
// extensions: the value is the same datatype, the reader applies the same rule to both, and what
// distinguishes a classification code from any other second code is `valueCoding.system`, which the
// value already carries. Declaring the special case as its own artifact would publish a second
// address for one fact, and give a writer a choice between two spellings of it -- which is the
// condition this base exists to end, not one to reproduce on it.
//
// So this guide declares ONE canonical. Both pre-migration addresses map onto it, both are matched
// exactly by a reader, and neither is written by anything new.
//
// MOVING THE WRITERS, AND THE SEEDED EXPANSIONS BEHIND THEM, IS TRACKED AS FHIR-374. The handbook
// states the rule and indexes every published artifact in this position: see
// `docs/guide/patterns/canonical-urls.md`.
// ────────────────────────────────────────────────────────────────────────────────────────────────
Extension: ValueSetSecondaryCoding
Id: valueset-secondary-coding
Title: "ValueSet Secondary Coding Extension"
Description: """
A second coding for one member of a pre-expanded `ValueSet`, carried on that member beside its own
`system` and `code`. Repeats: a member with two secondary codings carries two.
**SUPERSEDED BY A PUBLISHED EXTENSION. Write
`http://hl7.org/fhir/5.0/StructureDefinition/extension-ValueSet.expansion.contains.property`
instead.** R4's `ValueSet.expansion.contains` has no element for a per-member property, which is why
this extension was minted. R5 adds `expansion.contains.property` -- and HL7 publishes that element
back to R4 as an extension, ACTIVE, `fhirVersion 4.0.1`, contexted on
`ValueSet.expansion.contains`, in `hl7.fhir.uv.xver-r5.r4`. It was already available when this one
was written.
**It is a strict superset, and the difference is the part that matters.** The published extension
carries a `code` child naming WHAT THE PROPERTY IS, beside a `value` child that admits `code`,
`Coding`, `string`, `integer` or `boolean` -- and a `subProperty` for nested pairs. This extension
has only a value, so what the value MEANS has to be inferred from `valueCoding.system`.
That inference fails on a case this guide already has. The allergen picker's list annotates each
member with an allergen class, and the problem picker's list annotates each member with a
classification edition. Both are axes over expansion members; the moment two axes are drawn from ONE
vocabulary, `system` no longer tells them apart and there is nowhere to say which is which. The
published extension names the axis explicitly and does not have that failure mode.
**ADOPTING IT NEEDS A DEPENDENCY.** `hl7.fhir.uv.xver-r5.r4` is not currently pinned by this guide,
and it must be declared before a validator can resolve the canonical. That declaration belongs with
the change that moves the writer, not ahead of it -- the package carries over 1,400
StructureDefinitions and pinning it for an extension nothing yet writes would add weight for no
reader.
**What it buys.** These lists ship pre-expanded and are filtered in the client, so the second code
travels with the member the user picks. The picker can then emit a complete `CodeableConcept` --
the member's own coding, followed by each secondary coding -- rather than resolving a map after the
fact. The [allergen class](CodeSystem-nexus-allergen-class.md) rides this way on the allergen
picker's list, so an entry knows what kind of allergen it is at the moment it is recorded; the
problem picker carries a classification coding the same way, the edition named by that coding's own
`system`.
**Read it by the value's `system` -- and this is the limitation that supersedes it.** The extension
URL says only that a second code is present; what the code IS comes from `valueCoding.system`.
Repeats are in no guaranteed order and a member may carry more than one, so infer nothing from
position either. Two axes drawn from one vocabulary are indistinguishable under this rule, which is
exactly what the published extension's `code` child exists to state.
**It annotates a member; it does not decide membership.** A concept is in the set because
`compose` includes it. This adds a code to a member and never adds or removes one.
**MIGRATION.** Nexus EMR writes this extension today at
`https://fhir.nexus.health/StructureDefinition/valueset-secondary-coding` (FHIR-374): it is live in
seeded expansions and in code, and is under migration to the canonical declared here. That address
shares the final segment `valueset-secondary-coding` with the canonical, so matching that segment
reads both -- but it does NOT read the second address below.
**A SECOND PRE-MIGRATION ADDRESS, which no suffix rule reaches.** The earliest form of this
extension was spelled for one classification and is written at
`https://fhir.nexus.health/StructureDefinition/valueset-secondary-icd10` (FHIR-374). It is the same
extension under a narrower name -- the same value, the same reading rule, and the classification it
carries is named by `valueCoding.system` rather than by the URL -- so this guide declares one
canonical for both rather than a second artifact for the special case. It is the address the seeded
problem picker's classification codings are filed under today. It has no segment in common
with the canonical and must be matched exactly; a reader built on the suffix alone misses every
member filed under it. Write the canonical, and neither of these.
"""
* ^url = "../StructureDefinition/valueset-secondary-coding"
* ^status = #draft
// Kept published and kept describable: it is live in seeded expansions and in code, so a reader
// must be able to resolve it. New writers take the published R5 backport named above.
* ^context[0].type = #element
* ^context[0].expression = "ValueSet.expansion.contains"
* extension 0..0
* url 1..1
* value[x] 1..1 MS
* value[x] only Coding
* valueCoding ^short = "The second code for this expansion member"
* valueCoding ^definition = "A second coding for this expansion member. `system` states which vocabulary the code is drawn from and is the only thing that identifies what the code means; it is not constrained here, because a curated list chooses the vocabulary its members are annotated with."