Skip to content

Add DkCoreImmunization profile (moved from feature-medication-addons) - #227

Open
jkiddo wants to merge 9 commits into
masterfrom
feature-immunization
Open

jkiddo wants to merge 9 commits into
masterfrom
feature-immunization

Conversation

@jkiddo

@jkiddo jkiddo commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

This pull request introduces the Danish Core Immunization profile, aligning the FHIR Immunization resource with Det Danske Vaccinationsregister (DDV) data structures and semantics. It defines the profile, provides detailed mapping and documentation, sets up relevant FHIR aliases for DDV systems, and configures validation warnings for DDV-specific URIs. The main changes are grouped below:

Danish Core Immunization Profile and Mapping:

  • Added DkCoreImmunization profile in DkCoreImmunization.fsh, specifying structure, constraints, and detailed mappings from DDV XML elements to FHIR Immunization fields, including handling of DDV-specific concepts like ActiveStatus, NegativeConsentIndicator, and vaccine coding. Also includes two example instances.
  • Added comprehensive introductory documentation in StructureDefinition-dk-core-immunization-intro.md, explaining DDV data model, mapping rationale, status handling, private-marking, credibility, and identifier namespace considerations.

FHIR Alias and Namespace Configuration:

  • Introduced FSH aliases for DDV and ATC identifier/coding systems in aliases.fsh, ensuring consistent referencing of DDV fields and codes in the profile.

Validation and Ignore Rules:

  • Updated ignoreWarnings.txt to suppress warnings for DDV-specific URIs and code systems that are not defined in standard FHIR packages, reflecting their authoritative use in this context.

@jkiddo
jkiddo marked this pull request as draft August 11, 2026 11:47
@tmsMedcom

Copy link
Copy Markdown
Collaborator

@jkiddo is this ready for review?

@jkiddo
jkiddo marked this pull request as ready for review August 12, 2026 13:12
@jkiddo

jkiddo commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator Author

I'd say so, yes

@boldagechris

boldagechris commented Aug 14, 2026 •

Copy link
Copy Markdown

Clinical validation feedback on DkCoreImmunization

I experimented a bit and ran an informal clinical validation on this profile before diving further into review: I built two cases from the two example instances already in the PR (JohnImmunizationInfluvac and JohnImmunizationNegativeConsent) and got two independent clinicians to go through them.

DkCoreImmunization_kliniske_cases-kopi.docx
DkCoreImmunization_kliniske_cases_MM.docx (1).docx

Where both clinicians agreed:

JohnImmunizationNegativeConsent should have the same data completeness as JohnImmunizationInfluvac (performer, lotNumber, note). Both clinicians pushed back on the idea that a private-marked record should carry less metadata — as written, the example implies that, which isn't the intent.

lotNumber matters more than the profile currently treats it. Both flagged batch number as safety-critical for tracing adverse events and product defects, and both said it's normally physically available on the vaccine — so I'd check whether the empty value in the example reflects a real DDV data-capture gap rather than genuine unavailability, and consider MustSupport here.

The mapping text already prefers a structured reference to AuthorisationIdentifier over free-text display for performer.actor — that's good, and I don't think it needs to change. But neither example demonstrates it (both use performer.actor.display), and the doc doesn't explain why it matters: one clinician noted the clinic itself doesn't really care who administered it, but Styrelsen for Patientsikkerhed would, in the event of death or an adverse event. Worth adding an example with a structured reference, and stating that rationale explicitly.

The private-marking UX (show that something's hidden, let the clinician reveal it) matches what both clinicians expect and called "standard setup." I don't think this needs a profile change as meta.security = Restricted already models it. But I'd add an explicit implementer note in the intro doc since the profile can't enforce consuming-app UX on its own.

Counting a private-marked vaccination fully in coverage statistics while hiding it from individual clinicians checked out with both.

Worth digging into further

Both independently want a computable "next dose due," at least for interval/multi-dose vaccines (one called it potentially life-critical for something like Twinrix), while agreeing it's unnecessary for annual vaccines like influenza. CoverageDuration currently just becomes free-text note, which kills computability. I'd check whether DDV's source field is actually structured (duration + unit) — if it is, a structured element or extension seems better than folding it into a note string.

Out of scope for the profile, but..

Both clinicians landed on: routine single-vaccine administration (e.g. giving a tetanus shot) probably doesn't on its own justify "værdispring" on a private-marked record. That's reserved for broader clarification/workup. This traces back to Sundhedsloven, and the actual threshold probably sits with SSI (?). I'd just add a line in the intro saying the profile intentionally doesn't (and can't) define this access threshold, so nobody assumes it's covered (if we want to acknowledge that...)

Comment thread input/fsh/DkCoreImmunization.fsh Resolved
Comment thread input/fsh/DkCoreImmunization.fsh Resolved
@tmsMedcom

Copy link
Copy Markdown
Collaborator

Further I asked Claude to compare DkCoreImmunization agaist the Immunization profile from HL7 EU.

Sammenligning: DK Core Immunization vs. HL7 Europe Immunization (EU core)

Overordnet forskel i formål: DK Core er en snæver, kildesystem-tro profilering af DDV (kun administrerede vaccinationer, status reelt altid completed). EU core er en mere generisk, use-case-bred profil beregnet til flere europæiske scenarier (inkl. afviste/udeblevne vaccinationer, cross-border-udveksling).

Hvor EU core er strengere/mere restriktiv

  • performer.actor — kardinalitet og typer: EU core kræver actor 1..1 (obligatorisk reference), og har en dedikeret slice performer:administeringCentreOrHp med et required pattern på function (fast kode "AP" fra v2-0443), altså en tvungen rollekode. DK Core sætter også actor til 1..1, men uden functions-slicing/faste rollekoder — DDV leverer ofte kun fritekst (display).
  • vaccineCode terminologibinding: EU core binder vaccineCode til IPS-vaccine-valuesettet (preferred, med yderligere anbefalede bindinger til WHO ATC og eHDSI-vaccine for cross-border brug) — en indholdsmæssigt mere specifik og internationalt harmoniseret binding. DK Core bruger kun FHIR's generiske example-binding og lader selve kodningen ske via egne DDV/ATC/SNOMED-slices uden krav om et bestemt fælles værdisæt.
  • occurrence[x] slicing: EU core slicer eksplicit occurrence[x] (type:$this) og lægger en udvidelse for "period of life" på occurrenceDateTime, hvilket giver en strammere, mere struktureret definition end DK Core, der blot beholder choice-typen uden yderligere slicing.
  • patient-reference: Begge kræver 1..1, men EU core's patient-reference er strammet med sit eget Patient (EU core)-profilkrav og indgår i et bredere EU-patientkoncept; forskellen er dog mest arkitektonisk, ikke i kardinalitet.
  • Constraints/invariants: EU core lister eksplicit en række FHIR-standard invarianter (dom-2 til dom-6, ele-1, ext-1) i profilen — herunder best practice-krav om narrative (text.div.exists()). DK Core's constraint-tabel er tom (ingen invarianter defineret ud over base-FHIR).
  • language: EU core inkluderer og profilerer language med en preferred binding (og en max-binding til AllLanguages) — et element DK Core slet ikke nævner/profilerer.
  • Nye R5-extensions: EU core tilføjer to komplekse "backported" R5-extensions (administeredProduct, informationSource) med krav om, at CodeableReference-strukturen enten har concept eller reference (styret af ext-1-constraint) — en betydeligt mere kompleks/struktureret modellering end DK Core, der ikke har disse extensions.

Hvor DK Core er strengere (modsat retning)

  • status: DK Core sætter reelt kun completed/entered-in-error i praksis (dokumenteret adfærd, om end ikke et hårdt value-set-constraint) og forklarer eksplicit at not-done/statusReason ikke kan forekomme, fordi DDV ikke registrerer afvisninger. EU core holder status åben til hele det required value-settet (inkl. not-done), da profilen skal understøtte afviste vaccinationer.
  • vaccineCode.coding slicing: DK Core har tre faste, veldefinerede slices (ATC, SCTVaccineCode, DdvVaccine) med flere faste systemer (fixed value/required pattern) — strammere strukturelt end EU core, som ikke slicer coding på tilsvarende vis.
  • patient-profil: DK Core kræver specifikt DkCorePatient; ligeledes er performer.actor bundet til tre specifikke danske profiler (Practitioner/PractitionerRole/Organization) — en nationalt strammere reference-binding end EU core's tilsvarende (men EU-egne) profiler.

Vigtigste strukturelle observation

EU core's ekstra strenghed ligger primært i terminologibindinger (vaccineCode), obligatorisk performer-rolle-kodning, og formelle FHIR-invarianter — dvs. den er strengere på interoperabilitets-niveau. DK Core's strenghed ligger i fast kildesystem-mapping og eksplicit begrænsning af statusrummet til det, DDV faktisk kan levere — dvs. den er strengere på semantisk præcision ift. kildedata, men mere lempelig på selve terminologibindingerne.

@jkiddo

jkiddo commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

On the findings from the clinicans: We need to add text to the profile describing that e.g. the importance of batch and lot numbers should not carry significant value during consumption of the data with regards to forensics and audit - as DDV - being the central governed system of Immunizations is the system that is responsible to notify and carry out any needed investigation should it come to that matter. All batch and lot numbers and all other data will reside in DDV. Whether its exposed to consumers of the data is another matter, so I'd say its safe to to require it to be present at all times - e.g. the 'private marking case'. The story will be the same for 'private marking cases' on medication in #216. It may also generally be relevant to revisit this in the future @tmsMedcom on a higher level, depending on whether Denmark will adopt https://eur-lex.europa.eu/legal-content/DA/TXT/?uri=CELEX%3A32025R0327#art_10.

@boldagechris

Copy link
Copy Markdown

Will we see a follow up commit? should I directly address the findings from clinicians or will we have a discussion on our next meeting?

@Kirstinerosenbeck

Kirstinerosenbeck commented Aug 27, 2026 •

Copy link
Copy Markdown
Collaborator

Christopher writes:

lotNumber matters more than the profile currently treats it. Both flagged batch number as safety-critical for tracing adverse events and product defects, and both said it's normally physically available on the vaccine — so I'd check whether the empty value in the example reflects a real DDV data-capture gap rather than genuine unavailability, and consider MustSupport here.

  • Do not use MustSupport. Correct use of MustSupport requires a more specific use context, and as such is not used in dk-core. I Suggest adding this very valid point to the documentation.

Christopher writes:
Both independently want a computable "next dose due," at least for interval/multi-dose vaccines (one called it potentially life-critical for something like Twinrix), while agreeing it's unnecessary for annual vaccines like influenza. CoverageDuration currently just becomes free-text note, which kills computability. I'd check whether DDV's source field is actually structured (duration + unit) — if it is, a structured element or extension seems better than folding it into a note string.

  • We should promote compliant FHIR design practice meaning that using an extension is not a good way to go, and this is also an important point to convey to our implementers. My best suggestion for a design would be to have a plan-element (i.e. a ServiceRequest or CarePlan to organize the vaccination-treatment as a whole), and then make Immunizations (actually given doses) and Encounters/appointments (next dose) reference this plan-element. I see two ways of communicating this. We could write the solution that I just outlined in the documentation, or we could make an example.

@boldagechris

boldagechris commented Aug 27, 2026 •

Copy link
Copy Markdown

@Kirstinerosenbeck Your arguments make complete sense to me with reference to compliant FHIR design for DK Core (which I'm still in the process of understanding). I agree with what you're proposing if it hits the mark while still addressing the clinical feedback. Essentially, I understand your point as: both are implementation-relevant rather than profile-relevant

Thank you for teaching me!

@jkiddo

jkiddo commented Sep 7, 2026 •

Copy link
Copy Markdown
Collaborator Author

@tmsMedcom / @Kirstinerosenbeck / @boldagechris - I've been across the different comments and updated the branch. I've left this - especially - for further comments: https://github.com/hl7dk/dk-core/pull/227/changes#diff-9dfe020e1f2a22a6aa404b3054d720123b668ed9852fb33bc66d4f6c42cba4f8R3 - while the profile is not bound to DDV, its highly related. Let me hear what you think of it (the top description part).

@jkiddo

jkiddo commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

Note to self - 4 codes are missing from #terminology > ATC codesystem missing or wrong? @ 💬

@tmsMedcom tmsMedcom added this to the 3.8.0 milestone Sep 21, 2026

@RikkeVestesen RikkeVestesen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments :)

* meta.source ^short = "Register or system the vaccination was recorded in, where one can be identified"
* meta.security ^short = "[DA] Privatmarkering"
* status ^short = "Status of the vaccination - an administered vaccination is completed"
* statusReason ^short = "Reason the vaccination was not administered - not used by this profile"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since statusReason is described as "not used by this profile", should it be 0..0?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No - that would narrow it down unnecessarily

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Omformuleres, så det fremgår, at den ikke er understøttet i DDV. @jkiddo


#### Identifier namespaces

Earlier drafts of this profile used `https://www.sundhed.dk/vaccination/...` URLs deduced from the sundhed.dk JSON proxy. Those URLs are not registered anywhere and have been replaced with URIs rooted at the authoritative DDV XML schema namespace (`http://vaccinationsregister.dk/schemas/2013/12/01`). Consumers proxying DDV via sundhed.dk should normalise their identifier systems to these authoritative URIs when producing dk-core resources.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it relevant to mention drafts of the profile?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jkiddo fjerner formulering

A **computable "next dose due"** is not part of this profile, and it should not be added as an extension — for interval or multi-dose vaccines such as Twinrix that information is clinically important, but modelling it on the `Immunization` resource is not compliant FHIR design. `Immunization` records a dose that *was* given; a dose that is *due* is a plan. The recommended design is:

- organise the vaccination treatment as a whole in a plan element — a `ServiceRequest` or a `CarePlan`;
- have each `Immunization` (the doses actually given) reference that plan;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation recommends that each Immunization references a ServiceRequest or CarePlan. Which element is intended to carry this reference? I cannot find a reference from Immunization to the resources.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jkiddo Omformuleres til noget ala: "Traditional set har "next due dose" været understøttet i DDV, men er ikke en del af denne profil og bør ikke understøttes med en extension. Fremtidige doser kan repræsenteres i en 'event'-ressource."
Det behøver ikke have sit eget afsnit, men kan være en note.


`lotNumber` carries the batch number of the administered product. Batch numbers are clinically important for tracing adverse events and product defects, and the batch number is normally physically available on the vaccine at the time of administration.

The element is nevertheless **optional** in this profile, and `MustSupport` is deliberately **not** used on it. Two reasons:

@RikkeVestesen RikkeVestesen Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it deliberately not used specifically here, when MustSupport is not used in general in DK Core?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jkiddo Fjern delen med MustSupport

@tmsMedcom

Copy link
Copy Markdown
Collaborator

Input fra Kirsten Lei Hansen.

Her er noget input til profilen
Til 7.5.1.1: Beskrivelsen har allerede, at DDV er den autoritative kilde. Vaccinatører i DK skal indberette alle vaccinationer til DDV og hvis en borger ønsker at indberette fx en gammel vaccination, der stadig er relevant eller en udenlandsk effektuering sker det i en snitflade til DDV. Ingen andre systemer end DDV kan forventes at have et komplet datasæt for en borgers vaccinationer.

Til 7.5.1.1.4: Der er eksempler på vacciner med flere komponenter, hvor den samlede vaccine omtales med batchnr og lotnr bruges om enkeltkomponenterne. Derfor er det ærgerligt at lotnr bruges om batchnummeret i profilen. Som minimum bør det være tydeligere i beskrivelsen, at det er batchnr på den samlede effektuering der fremgår af DDV, hvis det ikke er muligt at ændre til batchnr i feltnavnet. Af forskellige årsager er batchnr et fritekstfelt i DDV og kvaliteten af data varierer, afhængigt af scenariet.

Til 7.5.1.1.5: I statistik- og analysesammenhæng fokuserer eksisterende rapportering på vaccinationsstedet frem for den udførende, da der, specielt for de større vaccinationsindsatser sker en delegering af den udførende opgave. Dvs. det kan være en midlertidigt ansat, der står som effektuerende, hvor vaccinationsstedet er mere sigende om ansvaret for effektueringen. Ordinationsansvaret er en tredje oplysning, som kan variere afhængigt af, om det er et nationalt program (sæsonvaccination) eller en individuel ordination.

Til 7.5.1.1.6: Som jeg læser beskrivelsen læner den sig op ad, hvad der i DDV-kontekst omtales som vaccinationsforløb. Vaccinationsforløbene skabes i DDV, som også har en aktiv rolle med at påminde om kommende vaccinationer. Specielt børnevaccinationerne er længerevarende forløb, som derfor kan være effektueret forskellige steder. DDV holder logikken for forløb, inkl. et sæt regler for substituering mellem vacciner, som defineres og vedligeholdes af SSI. Det anbefales at det kommunikeres at DDV er den ajourførte master på forløb.

@tmsMedcom

Copy link
Copy Markdown
Collaborator

:

See #228

@tmsMedcom

Copy link
Copy Markdown
Collaborator

Der er eksempler på vacciner med flere komponenter, hvor den samlede vaccine omtales med batchnr og lotnr bruges om enkeltkomponenterne. Derfor er det ærgerligt at lotnr bruges om batchnummeret i profilen. Som minimum bør det være tydeligere i beskrivelsen, at det er batchnr på den samlede effektuering der fremgår af DDV, hvis det ikke er muligt at ændre til batchnr i feltnavnet. Af forskellige årsager er batchnr et fritekstfelt i DDV og kvaliteten af data varierer, afhængigt af scenariet.

@jkiddo "lotNumber" skal erstattes af "Batch Number". lotNumber er ikke registreret.

@tmsMedcom

Copy link
Copy Markdown
Collaborator

I statistik- og analysesammenhæng fokuserer eksisterende rapportering på vaccinationsstedet frem for den udførende, da der, specielt for de større vaccinationsindsatser sker en delegering af den udførende opgave. Dvs. det kan være en midlertidigt ansat, der står som effektuerende, hvor vaccinationsstedet er mere sigende om ansvaret for effektueringen. Ordinationsansvaret er en tredje oplysning, som kan variere afhængigt af, om det er et nationalt program (sæsonvaccination) eller en individuel ordination.

@jkiddo Performer afsnittet gøres kortere, behold den første sætning, men tilføj en paragraf, at det er lokationen, der er obligatorisk og vigtigt i DDV. Performer er ikke interessant i DDV sammenhæng.
Ændre desuden overskriften til 'Performer and lokation'.

@jkiddo

jkiddo commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator Author

Der er eksempler på vacciner med flere komponenter, hvor den samlede vaccine omtales med batchnr og lotnr bruges om enkeltkomponenterne. Derfor er det ærgerligt at lotnr bruges om batchnummeret i profilen. Som minimum bør det være tydeligere i beskrivelsen, at det er batchnr på den samlede effektuering der fremgår af DDV, hvis det ikke er muligt at ændre til batchnr i feltnavnet. Af forskellige årsager er batchnr et fritekstfelt i DDV og kvaliteten af data varierer, afhængigt af scenariet.

@jkiddo "lotNumber" skal erstattes af "Batch Number". lotNumber er ikke registreret.

Jeg har padlet igennem noget dokumentation. Så vidt jeg kan læse, så bruges batch og lot 'for det meste' som synonymer. Der findes undtagelser, men som oftes er det synonymer. Datamodellen i DDV (https://wiki.fmk-teknik.dk/fmk:ddv:1.4.0:services) omtaler kun batch - og ikke lot. Hvis der er terminologisk forskellighed imellem batch og lot i DDB, som bør understøttes, burde det så ikke have været en del af datamodellen?

@tmsMedcom

Copy link
Copy Markdown
Collaborator

Der er eksempler på vacciner med flere komponenter, hvor den samlede vaccine omtales med batchnr og lotnr bruges om enkeltkomponenterne. Derfor er det ærgerligt at lotnr bruges om batchnummeret i profilen. Som minimum bør det være tydeligere i beskrivelsen, at det er batchnr på den samlede effektuering der fremgår af DDV, hvis det ikke er muligt at ændre til batchnr i feltnavnet. Af forskellige årsager er batchnr et fritekstfelt i DDV og kvaliteten af data varierer, afhængigt af scenariet.

@jkiddo "lotNumber" skal erstattes af "Batch Number". lotNumber er ikke registreret.

Jeg har padlet igennem noget dokumentation. Så vidt jeg kan læse, så bruges batch og lot 'for det meste' som synonymer. Der findes undtagelser, men som oftes er det synonymer. Datamodellen i DDV (https://wiki.fmk-teknik.dk/fmk:ddv:1.4.0:services) omtaler kun batch - og ikke lot. Hvis der er terminologisk forskellighed imellem batch og lot i DDB, som bør understøttes, burde det så ikke have været en del af datamodellen?

@jkiddo Som jeg forstod det på Kirsten er batch og lot synonymer, og at man i DDV har ensrettet brugen til kun at anvende batch.

@jkiddo

jkiddo commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Ok ... Så er der noget description der skal rettes til

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants