|
10 min de lecture
|

Gestion des paquets FHIR : épinglage, élagage d'arbre et pourquoi la résolution à l'exécution a dû disparaître

Résumé de l'article

Aidbox résout désormais toutes les versions canoniques à la phase de configuration — et non à l'exécution. L'épinglage verrouille chaque référence à une version exacte, l'élagage d'arbre élimine les canoniques inutilisés des dépendances, et le résultat est un ensemble plat et entièrement résolu où chaque recherche est simplement URL+version.

Résumer cet article avec :
ChatGPTPerplexityClaudeGrok

Le problème de la résolution à l'exécution

Avant la version 2511, Aidbox résolvait toutes les références canoniques à l'exécution. Lorsqu'une StructureDefinition référence http://hl7.org/fhir/us/core/StructureDefinition/us-core-race sans version, Aidbox devait déterminer quelle version utiliser. Et même lorsque le résultat était mis en cache, le cache était fragile. L'installation d'un nouveau paquet pouvait introduire de nouveaux canoniques, réinitialiser les caches et faire en sorte que la même référence se résolve vers une version différente.

C'est ainsi que la plupart des paquets FHIR sont publiés. Les canoniques contiennent des références sortantes vers d'autres canoniques — des ValueSets pointant vers des CodeSystems, des profils pointant vers des StructureDefinitions de base — et la grande majorité de ces références ne portent aucune version. L'hypothèse est que le consommateur déduira la version à partir du contexte.

Cette hypothèse transfère la responsabilité de la résolution des versions au consommateur.

Le vrai problème n'était pas la performance — c'était l'absence de contexte. À l'exécution, tout ce qu'on possède est un ensemble plat de paquets installés et leurs canoniques. Mais quels paquets ont été explicitement installés par l'utilisateur? Lesquels sont des dépendances transitives? Lesquels dépendent de quoi? Cette information était effectivement perdue après l'installation. Chaque paquet n'était qu'un ensemble de canoniques déversés dans le même bassin commun. Comme le résultat de la résolution dépendait de ce qui se trouvait dans ce bassin, installer ou supprimer un paquet pouvait silencieusement modifier la version vers laquelle une référence se résolvait. Des parties du graphe de dépendances pouvaient apparaître ou disparaître sans que le résolveur puisse le détecter.

Cela rendait les scénarios de dépendances complexes difficiles à gérer correctement. Prenons hl7.fhir.us.davinci-hrex — il intègre trois versions différentes de hl7.fhir.us.core. Lorsqu'un profil de HRex référence http://hl7.org/fhir/us/core/StructureDefinition/us-core-provenance sans version, laquelle des trois versions de US Core Aidbox devrait-il choisir? Cela ne peut pas être déduit de manière fiable à l'exécution — les trois sont présentes simultanément, et il ne reste aucune information pour lever l'ambiguïté. Cette décision doit être prise avant le démarrage du système, et non lors d'un appel de validation.

Nous avions besoin d'un moyen de résoudre toutes ces ambiguïtés une fois pour toutes, en amont, lorsque le graphe de dépendances complet et l'intention de l'utilisateur sont encore disponibles — et non à l'exécution quand ils ont disparu.

La communauté FHIR se dirigeait déjà dans ce sens. Le guide FHIR IG sur l'épinglage a posé les bases — la spécification décrit l'algorithme de résolution prévu :

  1. Trouver le paquet qui contient la ressource avec la référence
  2. Compiler la liste complète des dépendances de ce paquet (transitives)
  3. Trouver toutes les ressources correspondant à l'URL canonique dans ces paquets
  4. Sélectionner la version la plus récente

Cela semble simple, mais les complications s'accumulent rapidement. « La plus récente » est ambigu lorsqu'on a des chaînes de version en semver, basées sur des dates, ou arbitraires. Plusieurs ressources peuvent avoir la même URL et la même version. Et dans le contexte d'une API RESTful, les canoniques arrivent sans métadonnées de paquet — le serveur peut ne pas savoir quel paquet « possède » un canonique donné.

La spécification est délibérément légère sur ces détails d'implémentation — elle laisse beaucoup à la discrétion des implémenteurs. Nous avons donc procédé à de l'ingénierie inverse à partir des outils existants qui tentent l'épinglage — IG Publisher et UploadFIG — et avons finalisé la stratégie de résolution lors du FHIR Camp 2025 en collaboration avec Grahame Grieve, Gino Canessa et Lloyd McKenzie.

Dans Aidbox, cela se traduit par une approche en deux phases pour la gestion des paquets : tout le travail difficile se produit avant l'exécution réelle du serveur, et l'exécution reçoit un ensemble canonique plat et entièrement résolu.

Approche en deux phases : configuration et exécution

FHIR Packages Configuration Phasepinning + tree-shaking Runtime Phaseflat canonical lookup

Phase de configuration. Lorsque vous installez un paquet — via l'API, l'interface utilisateur ou la configuration BOX_BOOTSTRAP_FHIR_PACKAGES — Aidbox télécharge le paquet et toutes ses dépendances, puis parcourt l'intégralité du graphe de dépendances. Chaque référence canonique non versionnée est réécrite avec un suffixe |version explicite :

before: http://hl7.org/fhir/ValueSet/observation-status
after:  http://hl7.org/fhir/ValueSet/observation-status|4.0.1

En même temps, les canoniques des paquets de dépendances que personne ne référence réellement sont supprimés — c'est l'élagage d'arbre.

Phase d'exécution. Tous les canoniques sont déjà résolus. Chaque référence sortante porte une version explicite. Pour Aidbox, résoudre un canonique est désormais une simple requête de base de données par <url>|<version> — pas de traversée de graphe, pas de résolution dépendante du contexte, pas d'ambiguïté. Les paquets en tant que concept n'existent plus à l'exécution; seuls les canoniques entièrement résolus subsistent.

Comment fonctionne l'épinglage dans Aidbox

Le processus d'épinglage parcourt chaque canonique dans les paquets installés et réécrit ses références sortantes avec des versions exactes.

Collecte des références sortantes

Pour chaque ressource canonique, Aidbox collecte toutes les références canoniques sortantes — les URLs qui pointent vers d'autres canoniques. Il le fait pour les cinq types de ressources canoniques qui peuvent contenir des références sortantes :

  • StructureDefinition (à l'exclusion de snapshot — seul differential compte)
  • CodeSystem
  • ValueSet
  • SearchParameter
  • CapabilityStatement

Par exemple, une StructureDefinition peut référencer un ValueSet pour une liaison, une StructureDefinition de base dont elle dérive, et des définitions d'extension qu'elle utilise. Chacune de ces références a besoin d'une version épinglée.

Construction de la liste de candidats

Pour chaque référence sortante, Aidbox construit une liste de candidats : tous les canoniques avec l'URL correspondante disponibles dans l'arbre de dépendances du paquet (y compris les dépendances transitives). Si un paquet dépend de hl7.fhir.us.core@5.0.0, et que US Core dépend de hl7.terminology.r4, alors les canoniques de terminology sont des candidats valides.

L'algorithme de sélection de candidats

Lorsqu'il existe plusieurs candidats — différentes versions, différents paquets, même URL canonique — Aidbox sélectionne le meilleur en utilisant une chaîne de comparaison déterministe à plusieurs étapes :

PrioritéCritèreRègle
1Statutactive > draft > retired > unknown
2Terminology par rapport à CoreLes canoniques des paquets hl7.terminology ont la priorité sur les canoniques portant le même nom des paquets core
3VersionComparée à l'aide de l'algorithme détecté : semver, entier, date ou alphabétique
4lastUpdatedCritère de départage final utilisant meta.lastUpdated

Avant la comparaison, les candidats sont filtrés. Aidbox exclut les canoniques des paquets correspondant à des motifs de bruit connus — expansions, exemples, liés à la recherche, éléments, corexml — et les ressources CodeSystem dont le content n'est pas "complete".

La détection de l'algorithme de version mérite une note. Aidbox vérifie d'abord versionAlgorithmString ou versionAlgorithmCoding sur la ressource (une fonctionnalité R5+). Si aucun n'est présent — ce qui est le cas la plupart du temps avec le contenu R4 — Aidbox déduit le schéma en inspectant la chaîne de version : ressemble-t-elle à du semver (1.2.3)? Un entier (4)? Une date (2024-01-15)? Le repli est la comparaison alphabétique. Cela correspond à l'approche pratique recommandée dans le guide FHIR IG.

Épinglage récursif

L'épinglage est récursif. Une fois qu'une référence est épinglée à un canonique spécifique, les propres références sortantes de ce canonique sont également épinglées — et ainsi de suite, sur toute la chaîne de dépendances. Chaque paire <url>|<version> n'est traitée qu'une seule fois (mémoïsée), de sorte que le processus se termine même avec des références circulaires.

Ce parcours récursif est également ce qui pilote l'élagage d'arbre.

Élagage d'arbre : seulement ce dont vous avez besoin

Un paquet FHIR typique contient des centaines ou des milliers de canoniques. Mais lorsque vous installez un paquet, vous n'avez pas besoin de tous les canoniques de ses dépendances — vous n'avez besoin que de ceux qui sont réellement référencés.

Considérons l'installation de hl7.fhir.us.core@5.0.0. Il dépend de hl7.terminology.r4 (4 158 canoniques) et de us.nlm.vsac (8 918 canoniques). Mais US Core ne référence qu'un petit sous-ensemble de chacun. Pourquoi charger le reste?

L'élagage d'arbre est la réponse. Lors du parcours d'épinglage récursif, Aidbox suit les canoniques de dépendances qui sont réellement atteints en suivant les références sortantes du paquet cible. Seuls ces canoniques accessibles — et leurs propres dépendances récursives — font partie de l'ensemble canonique final.

Le résultat :

  • Paquet cible : tous les canoniques sont inclus (c'est ce que l'utilisateur a demandé)
  • Paquets de dépendances : seuls les canoniques référencés sont inclus (élagués)

La réduction est spectaculaire. Comparez les comptes installedCanonicals d'une installation réelle :

POST /fhir/$fhir-package-install
Content-Type: application/json

{
  "resourceType": "Parameters",
  "parameter": [
    { "name": "package", "valueString": "hl7.fhir.us.core@5.0.0" }
  ]
}

La réponse montre à quel point l'élagage d'arbre réduit considérablement le nombre de canoniques. Sur les 4 158 canoniques dans hl7.terminology.r4, seulement 7 survivent. Sur les 8 918 dans us.nlm.vsac, seulement 20 :

PaquetCanoniques installésIntention
hl7.fhir.us.core@5.0.0194direct
us.nlm.vsac@0.3.020transitive
hl7.fhir.uv.sdc@3.0.011transitive
hl7.terminology.r4@3.1.07transitive

Chaque canonique dans le système est unique par <url>|<version>. Pas de doublons, pas de conflits, pas d'ambiguïté.

Sources des paquets

Aidbox prend en charge trois façons d'obtenir des paquets :

npm file url NPM Registryfs.get-ig.org/pkgs, Simplifier, Verdaccio Local Filesystem/srv/aidbox-fhir-packages Direct URLshttps:// or file:// Aidbox Artifact Registry

Registres NPM. Le registre par défaut est https://fs.get-ig.org/pkgs, qui fait office de miroir de packages2.fhir.org avec des synchronisations quotidiennes. Vous pouvez diriger Aidbox vers n'importe quel registre compatible NPM — Simplifier, une instance privée Verdaccio, ou tout miroir personnalisé.

Système de fichiers local. Montez un répertoire contenant des paquets .tgz dans /srv/aidbox-fhir-packages dans le conteneur Aidbox. Les paquets doivent suivre la convention de nommage {name}#{version}.tgz — par exemple, hl7.fhir.r4.core#4.0.1.tgz. Aidbox vérifie d'abord les paquets locaux avant d'accéder au registre distant. C'est essentiel pour les environnements isolés du réseau et accélère également considérablement le démarrage.

URLs directes. Vous pouvez fournir une URL https:// pointant vers une archive .tgz ou un chemin file:// vers un fichier local. Utile pour installer des paquets qui ne sont publiés dans aucun registre — comme des IGs personnalisés construits localement avec SUSHI.

Substitutions de dépendances

Vous souvenez-vous du problème hl7.fhir.us.davinci-hrex mentionné plus tôt — trois versions de US Core, impossible à désambiguïser à l'exécution? Avec les substitutions, l'utilisateur fait ce choix explicitement à la phase de configuration.

HRex intègre hl7.fhir.us.core@7.0.0, hl7.fhir.us.core.v610 et hl7.fhir.us.core.v311. Si vous n'avez besoin que de la version 7.0.0, ignorez les deux autres :

POST /fhir/$fhir-package-install
Content-Type: application/json

{
  "resourceType": "Parameters",
  "parameter": [
    { "name": "package", "valueString": "hl7.fhir.us.davinci-hrex@1.1.0" },
    {
      "name": "override",
      "part": [
        { "name": "from", "valueString": "hl7.fhir.us.core.v610" },
        { "name": "to", "valueBoolean": false }
      ]
    },
    {
      "name": "override",
      "part": [
        { "name": "from", "valueString": "hl7.fhir.us.core.v311" },
        { "name": "to", "valueBoolean": false }
      ]
    }
  ]
}

Trois types de substitutions :

SubstitutionSyntaxeCas d'utilisation
Ignorer une dépendance"to": false (valueBoolean)Exclure une dépendance — comme supprimer des versions indésirables de US Core
Épingler une version"to": "0.17.0"Forcer une version spécifique d'une dépendance
Remplacer un paquet"to": "npm:alt.package@1.0.0"Substituer entièrement un paquet par un autre

Le champ from prend en charge la correspondance par nom seul (hl7.fhir.us.core — correspond à n'importe quelle version) et la correspondance qualifiée par version (hl7.fhir.us.core@6.1.0 — correspond uniquement à cette version exacte). Les substitutions qualifiées par version ont la priorité.

Les substitutions résolvent également les lacunes des registres. hl7.fhir.us.core@8.0.0 dépend de us.nlm.vsac@0.23.0, mais Simplifier a cessé d'héberger les versions VSAC après 0.17.0. Épinglez-le à 0.17.0, ignorez-le entièrement, ou remplacez-le par une alternative — sans modifier les paquets sources.

Qu'en est-il de l'installation à l'exécution?

Cela signifie-t-il que vous perdez la possibilité d'installer des paquets et des canoniques à l'exécution? Non — mais avec quelques précisions.

Les paquets installés à l'exécution sont toujours épinglés automatiquement. La différence est que l'épinglage se produit dans un contexte isolé : seul l'arbre de dépendances propre au paquet est impliqué. Le contenu déjà installé est inaccessible lors de cette passe d'épinglage, de sorte que le nouveau paquet obtient un ensemble canonique cohérent en lui-même sans interférer avec ce qui est déjà chargé.

Les canoniques individuels que vous téléversez directement — en dehors de tout paquet — relèvent de votre responsabilité en matière d'épinglage. Si vous fournissez un canonique avec des références sortantes non versionnées, Aidbox ne tentera pas de les épingler pour vous. Il conserve cependant un mode de repli pour les références non épinglées : il utilisera le même algorithme de sélection de candidats décrit ci-dessus — statut, priorité de terminology, comparaison de version — pour résoudre vers la meilleure correspondance disponible. Rien ne se casse donc — vous n'obtenez simplement pas les garanties de déterminisme que fournit l'épinglage au niveau du paquet.

Ce que cela signifie en pratique

Voici ce que cela vous apporte dans Aidbox :

Prévisibilité. La même entrée d'installation de paquet produit toujours le même ensemble canonique. Il n'y a pas d'ambiguïté à l'exécution, pas de comportement dépendant de l'ordre, pas d'état caché affectant la résolution.

Performance. Les recherches de canoniques sont de simples requêtes de base de données par <url>|<version>. Pas de traversée de graphe, pas de parcours de dépendances, pas d'heuristiques de mise en cache.

Débogage facilité. Chaque canonique dans le système possède une version explicite. Lorsque la validation échoue, vous voyez exactement quelle version de quelle StructureDefinition a été utilisée — et non « ce qui s'est résolu à ce moment-là ».

Empreinte réduite. L'élagage d'arbre signifie que les dépendances transitives ne contribuent que les canoniques réellement nécessaires. L'installation de US Core intègre 7 canoniques de hl7.terminology.r4 au lieu de 4 158.

La contrepartie est que l'installation prend un peu plus de temps — Aidbox doit parcourir l'intégralité du graphe de dépendances, construire des listes de candidats et épingler récursivement les références. Mais pour l'ensemble de paquets initial, cela ne se produit qu'une seule fois, au premier démarrage.

Références

Spécification :

Documentation Aidbox :

Outils :

  • IG Publisher — l'éditeur officiel de guides d'implémentation de HL7
  • UploadFIG — l'outil de téléversement de canoniques de Brian Postlethwaite
  • SUSHI — compilateur FHIR Shorthand pour construire des IGs personnalisés
  • Verdaccio — proxy/registre NPM léger pour l'hébergement privé

Événements :

  • FHIR Camp 2025 — où la stratégie de résolution a été finalisée
Partager cet article
Comments
Comments
Sign in
Loading comments...
Subscribe to our blog

Get the latest articles on FHIR, interoperability, and healthcare IT.