Développement et maintenance du code pour les composants d'interopérabilité au sein d'un environnement institutionnel de soins de santé
Il y a des leçons que nous avons tirées du développement et de la maintenance du code des composants d’interopérabilité dans un environnement institutionnel de santé.
L’avion est déjà en vol
Soyez prêt à reconstruire, améliorer, étendre et réparer l’avion en plein vol.
Les fenêtres de maintenance des systèmes hospitaliers sont souvent très limitées, certains devant rester opérationnels 24 h/24 et 7 j/7. Si les systèmes de santé critiques, tels que les modalités d’imagerie ou les bornes d’enregistrement, ou du moins leurs fonctions critiques, doivent pouvoir fonctionner de manière autonome, l’efficacité et l’ergonomie peuvent être compromises lorsque les composants d’interopérabilité dysfonctionnent. Par exemple, les bornes d’enregistrement ambulatoire peuvent tomber en panne ou orienter incorrectement les patients si les données de rendez-vous et de praticiens sont absentes ou obsolètes, ce qui augmente la charge de travail et le stress.
Comme les équipes informatiques et leurs budgets sont généralement plus réduits dans le secteur de la santé que, par exemple, dans la banque, les développeurs sont souvent responsables de l’ensemble du cycle de vie des composants d’interopérabilité : développement, tests, déploiement, maintenance et retrait final.
La haute disponibilité et le mirroring dans IRIS, combinés à une infrastructure moderne (par exemple des hôtes de serveurs virtuels), offrent une forte résilience technique. Cependant, ils n’éliminent pas le risque de problèmes logiciels. Pour réduire ces risques tout en garantissant la continuité du service, vous pouvez appliquer plusieurs stratégies complémentaires :
- Maintenir un dépôt de code et utiliser un système de gestion de versions.
- Mettre en place et maintenir des environnements d’exploitation dédiés où les fonctionnalités peuvent être développées et où les mises en production planifiées peuvent être testées de bout en bout.
- Surveiller les environnements de production et alerter les administrateurs système en cas de défaillances critiques.
Maintenir un dépôt de code
De mon point de vue, utiliser un dépôt Git unique pour tous les namespaces d’interopérabilité est le moyen le plus efficace de favoriser la réutilisation du code et la cohérence. Selon la maturité de l’équipe de développement, le schéma de branches peut évoluer d’un simple duo main/develop vers une architecture complète de GitFlow.
Le dépôt comporte les répertoires suivants :
|
Répertoire |
Description |
|
(racine) |
Readmes, changelogs, Dockerfiles, … |
|
src |
Toutes les ressources prises en charge par le compilateur IRIS : classes, tables de correspondance, schémas HL7, etc. La structure des packages et les noms de classes doivent suivre les directives de codage et conventions de nommage établies. |
|
resources |
Toutes les ressources qui ne sont pas prises en charge par le compilateur IRIS, comme les données de test, les jeux de codes, les fichiers source de diagrammes, etc. |
|
scripts |
Scripts utilisés pour exécuter les tests unitaires, déployer le code, etc. (voir les exemples ci-dessous). |
|
out |
Toutes les sorties (résultats de test, etc.) qui ne doivent pas être poussées dans le dépôt, comme les résultats de tests ou d’autres artefacts générés, diagrammes, etc. Ce répertoire convient également très bien pour servir de ^UnitTestRoot. Ce répertoire est ajouté au .gitignore. |
Le dépôt doit contenir tous les artefacts (classes, tables de correspondance, schémas HL7, etc.) qui ne sont pas considérés comme des éléments de configuration.
Certaines classes, cependant, sont considérées comme des éléments de configuration et nécessitent une gestion particulière, car leur code est généralement généré, varie selon les environnements d’exploitation et peut être modifié par les administrateurs système via les éditeurs du portail de gestion.
Cela inclut les productions d’interopérabilité (classes héritant de Ens.Production) et les règles métier ou de routage (classes héritant de Ens.Rule.Definition).
Environnements d’exploitation et gestion des changements
Le développement et la gestion des mises en production pour les services construits sur InterSystems IRIS suivent généralement les mêmes pratiques que sur d’autres plateformes logicielles. Pour maintenir une haute disponibilité dans l’environnement de production, des configurations d’exploitation supplémentaires sont utilisées pour soutenir un processus de mise en production structuré et maîtrisé.
Les environnements s’exécutent dans des instances IRIS distinctes, idéalement en utilisant la même version d’IRIS.
Utiliser des plateformes différentes pour le développement et les autres domaines est tout à fait acceptable, à condition que le code prenne en compte ces différences. En pratique, cela signifie par exemple éviter les problèmes liés aux accès partagés aux fichiers, aux terminateurs de ligne et aux séparateurs de chemin.
Environnement de développement (DEV)
Lors de la conception d’interfaces avec des logiciels fournis par des éditeurs, des spécifications précises et complètes sont essentielles pour un processus efficace et réussi. Dans le contexte des appels d’offres publics de l’Union européenne, il est recommandé d’inclure ces spécifications techniques comme exigences obligatoires dans la partie technique du dossier.
Pour le développement et les tests unitaires, vous pouvez utiliser soit un environnement partagé mis en place avec l’excellent contrôle de code source côté serveur embedded git, soit des environnements séparés avec un dépôt git partagé unique.
Les tests unitaires exécutés dans l’environnement de développement ne doivent pas dépendre de ressources externes telles que des bases de données, des API ou des interfaces HL7 applicatives. Je mets en place deux namespaces sur les instances DEV :
- DEV : utilisé pour le développement actif et l’exécution des tests unitaires un par un
- TEST (ou SCRATCH) : utilisé pour exécuter toutes les suites de tests après clonage d’une branche du dépôt et recréation (ou nettoyage) du namespace. Charger et compiler l’ensemble de la base de code dans un namespace propre permet de s’assurer que toutes les dépendances sont correctement résolues. Exécuter les tests unitaires et corriger les problèmes jusqu’à leur réussite minimise le risque de régression.
Les tâches de maintenance sur ces namespaces sont réduites au minimum :
- Pas de journalisation (sauf dans de rares cas où elle est nécessaire pour les tests).
- Pas de sauvegardes
- La purge des messages d’interopérabilité est configurée pour conserver un historique minimal
Les tests reposent sur des classes héritant des classes %UnitTest.* (voir par exemple ks-iris-lib). Les tests unitaires ne dépendent pas de ressources externes ; ils utilisent soit des données stockées dans le répertoire « resources » du dépôt, soit des blocs XData inclus dans les classes de test.
Pendant le développement, les tests sont exécutés selon les besoins à l’aide du harnais de test VSCode, tandis que les exécutions complètes se font via un script.
Le script clone une branche du dépôt (généralement la branche develop ou une branche feature), charge et compile toutes les classes, puis exécute l’ensemble complet des tests unitaires disponibles. Voici un exemple de script shell Windows :
@echo off
setlocal
rem A utility to clone IRIS Health items from git repository and run unit test in an empty namespace
rem
rem Usage :
rem
rem unit-tests <iris instance> <iris namespace>
rem
rem example :
rem
rem deploy-classes IRISHEALTH SCRATCH
rem
rem pre-requisites :
rem
rem * git installed and in path
rem * InterSystems IRIS Health installed and
rem * disk space in %TEMP% volume for a clone of repository
rem * existing IRIS Health instance and namespace
rem
rem
rem IRIS installation folder
rem
set IRISHome=c:\intersystems\IRISHealth
rem
rem repository http URL
rem
set Repository=https://github.com/theanor/ks-iris-lib
if "%3"=="" (set Branch=develop) else (set Branch=%3)
if "%1"=="" (set IRISInstance=IRISHealth) else (set IRISInstance=%1)
if "%2"=="" (set IRISNameSpace=SCRATCH) else (set IRISNameSpace=%2)
set TargetDir=%TEMP%\iris-health-deploy
echo Cleaning target directory
if exist %TargetDir% rmdir /s/q %TargetDir%
echo Cloning repository %Repository% branch %Branch%
git clone --branch %Branch% %Repository% %TargetDir%
echo Wiping namespace %IRISNameSpace%
"%IRISHome%\bin\irissession" %IRISInstance% -U %IRISNameSpace% "##class(%%UnitTest.Manager).WipeNamespace()"
echo Importing code into %IRISNameSpace%
"%IRISHome%\bin\irissession" %IRISInstance% -U %IRISNameSpace% "##class(%%SYSTEM.OBJ).ImportDir(""""""%TargetDir%\src"""""",""""""*.*"""""",""""""ckbry"""""",,1)"
echo Running repository tests in %IRISNameSpace%
"%IRISHome%\bin\irissession" %IRISInstance% -U %IRISNameSpace% "##class().RunRepositoryTests(""""""%IRISNameSpace%"""""",""""""%TargetDir%"""""")"
Environnement de test (TEST)
La mise en place et la maintenance d’un environnement de test applicatif peuvent entraîner des coûts supplémentaires, notamment en licences. Lors de la rédaction des spécifications techniques pour les marchés publics européens, il est recommandé d’exiger au moins un environnement de test.
Alors que l’environnement DEV est utilisé pour les tests unitaires, l’environnement TEST est utilisé pour les tests d’intégration. Il peut utiliser des ressources externes telles que des bases de données et des API exposées par des instances de test ou des répliques des applications interfacées.
Dans ce contexte, il est utile de séparer clairement les classes de tests unitaires et celles d’intégration. Cela peut se faire en les plaçant dans des packages distincts ou en utilisant un paramètre marqueur dédié (par exemple INTEGRATIONTEST = 1) pour identifier les tests d’intégration.
Le code de l’environnement de test est déployé (généralement depuis la branche develop ou une branche feature) en utilisant les mêmes techniques que pour l’environnement de production, soit via des scripts, soit via Git embarqué pour importer et compiler les artefacts depuis les branches du dépôt Git.
Environnements d’assurance qualité (QA)
Alors que l’environnement TEST est utilisé pour les tests d’intégration, le ou les environnements QA sont utilisés pour les tests de bout en bout et les tests d’acceptation utilisateur. Ils peuvent également être utilisés pour la formation des utilisateurs.
Les environnements QA s’interfacent avec les instances de test des applications externes.
Si vous devez effectuer simultanément des tests de non-régression, l’analyse d’incidents (reproduction de bugs hors production) et de la formation utilisateur, il peut être nécessaire de disposer de plusieurs environnements QA afin de maintenir trois configurations différentes :
- QA -1 : version précédente, utilisée pour la gestion des incidents (vérifier si un bug était déjà présent).
- QA 0 : version actuelle, utilisée pour la formation et la gestion des incidents (reproduction d’incidents ou de bugs).
- QA +1 : version suivante, utilisée pour les tests d’intégration et de non-régression.
Une couverture complète des cas d’usage applicatifs impliquant les composants d’interopérabilité lors des tests de bout en bout est la clé d’un déploiement réussi. Pensez à définir ces scénarios de bout en bout autour de cas d’usage réels avec les parties prenantes du projet.
Supervision et alerting
Comme ces environnements connectent des dizaines de systèmes échangeant des données sur le réseau, les productions d’interopérabilité (et les instances IRIS qui les exécutent) peuvent générer des volumes importants de logs et d’informations d’alerte.
Maintenir une liste structurée des applications interfacées et les configurer comme business partners permet de s’appuyer sur le framework d’alerting existant. Le code des business partners peut également faire référence à des données supplémentaires sur l’application obtenues à partir d’autres sources, comme une CMDB.
Pour éviter la fatigue d’alerte, il est crucial de filtrer le bruit afin de n’envoyer que des alertes claires et pertinentes aux bons destinataires. Cependant, le faire avec un haut niveau de précision est loin d’être simple !
Il est utile de commencer par quelque chose de simple, puis de réduire progressivement le niveau de bruit en ajustant le filtrage et le routage des alertes.
Dans sa forme la plus simple, les alertes sont routées par un EnsLib.MsgRouter.RoutingEngine vers EnsLib.EMail.AlertOperation, qui envoie tous les avertissements aux adresses e-mail configurées via SMTP :

Pour aller plus loin, nous devons définir des processeurs d’alertes.
La méthode CreateManagedAlertRule de Ens.Alerting.AlertManager permet de décider s’il faut créer une alerte gérée.
La méthode OnProcessNotificationRequest() de Ens.Alerting.NotificationManager permet de déterminer si une alerte gérée doit faire l’objet d’une notification.
Des fonctions personnalisées peuvent vous aider dans ces décisions. Vous trouverez ci-dessous quelques exemples pratiques.
L’élément de configuration source est à l’origine de la session ; transmettez donc AlertRequest.SessionId à une fonction comme dans l’exemple ci-dessous :
Include Ensemble
Class dc.alert.FunctionSet Extends Ens.Rule.FunctionSet
{
ClassMethod HeaderSourceConfigName(headerId As %Integer) As %String
{
#Dim header as Ens.MessageHeader
s header = ##class(Ens.MessageHeader).%OpenId(headerId)
return $select($isobject(header):header.SourceConfigName,1:"")
}
ClassMethod ConfigNameBusinessPartner(configName As Ens.DataType.ConfigName) As %String
{
#Dim result As StPierre.Type.ApplicationCode
return:$get(configName)="" ""
return $get($$$ConfigSetting(configName,"Host","BusinessPartner"))
}
}Si vous souhaitez envoyer les alertes de faible criticité uniquement pendant les heures de travail, vous devez définir un planning pour ces heures, par exemple :
STOP:WEEK-*-01T17:00:00,START:WEEK-*-01T08:00:00,STOP:WEEK-*-02T17:00:00,START:WEEK-*-02T08:00:00,STOP:WEEK-*-03T17:00:00,START:WEEK-*-03T08:00:00,STOP:WEEK-*-04T17:00:00,START:WEEK-*-04T08:00:00,STOP:WEEK-*-05T17:00:00,START:WEEK-*-05T08:00:00
Pour éviter de répéter la même alerte, le gestionnaire d’alertes utilise la fonction Ens.Alerting.Rule.FunctionSet.IsRecentManagedAlert.
Les notifications d’alerte peuvent également s’intégrer à des outils de supervision externes comme Prometheus ou Nagios en étendant Ens.Alerting.NotificationOperation
Déployer des changements pendant que la production est en cours d’exécution
Changements de code
Dans son excellente série d’articles sur la livraison continue avec GitLab, @Eduard Lebedyuk explique les options dont nous disposons pour mettre à jour le code et la configuration pendant que l’interopérabilité fonctionne. Cet article se concentre en particulier sur les productions d’interopérabilité.
L’importation et la compilation des classes n’affectent pas les processus de business hosts déjà en cours d’exécution ; ceux-ci ne peuvent utiliser le code nouvellement compilé qu’après redémarrage.
En revanche, les classes nouvellement instanciées peuvent être impactées. Par exemple, les nouvelles instances de business process utiliseront immédiatement le code récemment compilé. Cela vaut également pour tout code invoqué dans le contexte d’un autre processus, comme les transformations de données.
Même si vous parvenez à éviter un couplage fort, les classes peuvent toujours dépendre les unes des autres, et vous devrez peut-être contrôler précisément l’ordre dans lequel les business hosts sont désactivés puis réactivés. Pour cela, appliquez un déploiement scripté afin de :
- Sauvegarder toutes les classes dans un fichier XML
- Importer et compiler les éléments depuis la branche du dépôt
Voici un exemple de script pour sauvegarder les classes :
@echo off
setlocal
rem A utility to backup IRIS Health classes from a namespace to a xml file
rem
rem Usage :
rem
rem backup-classes <iris instance> <iris namespace> <target file>
rem
rem example :
rem
rem backup-classes IRISHM PROD
rem
rem
rem IRIS installation folder
rem
set IRISHome=c:\intersystems\IRISHealth
set IRISInstance=%1
set IRISNameSpace=%2
set BackupFile=%3
for /f %%i in ('powershell get-date -format "{yyyyMMdd}"') do set CurrentDate=%%i
if "%3"=="" (set BackupFile=classes-%CurrentDate%.xml)
echo %CurrentDate%
echo exporting classes to %BackupFile%
"%IRISHome%\bin\irissession" %IRISInstance% -U %IRISNameSpace% "##class(%%SYSTEM.OBJ).ExportAllClasses(""""""%BackupFile%"""""")"
echo:
echo Finished exporting classesVoici maintenant un exemple de script pour importer et compiler un élément UDL depuis le dépôt :
@echo off
setlocal
rem A utility to clone IRIS Health items from git repository and deploy (load, compile) them to an iris instance
rem
rem Usage :
rem
rem deploy-classes <iris instance> <iris namespace>
rem
rem example :
rem
rem deploy-classes IRISHEALTH SCRATCH
rem
rem pre-requisites :
rem
rem * git installed and in path
rem * intersystems IRIS Health installed and
rem * disk space in %TEMP% volume for a clone of repository
rem * existing IRIS Health instance and namespace
rem
rem
rem IRIS installation folder
rem
set IRISHome=c:\intersystems\IRISHealth
rem
rem repository http URL
rem
set Repository=http://git.stpierre-bru.be/integration/iris-health.git
set Branch=develop
set TargetDir=%TEMP%\iris-health-deploy
set IRISInstance=%1
set IRISNameSpace=%2
echo Cleaning target directory
if exist %TargetDir% rmdir /s/q %TargetDir%
echo Cloning repository %Repository% branch %Branch%"
git clone --branch %Branch% %Repository% %TargetDir%
echo Loading UDL items
"%IRISHome%\bin\irissession" %IRISInstance% -U %IRISNameSpace% "##class(%%SYSTEM.OBJ).ImportDir(""""""%TargetDir%\src"""""",""""""*.*"""""",""""""ckbry"""""",,1)"
Changements de configuration
Dans le contexte de productions d’interopérabilité fonctionnant en continu, et compte tenu du contrôle strict sur les types de classes compilées ainsi que sur leur synchronisation avec les business hosts en activité, je ne recommanderais pas l’utilisation d’IPM pour le moment.
Je ne connais pas de sites qui utilisent les fonctionnalités de déploiement du portail de gestion. Cela s’explique peut-être par le fait qu’il faudrait un environnement de fond reproduisant strictement l’environnement de production réel pour construire des packages avec le portail de gestion.
Même en partant de zéro, avec un seul dépôt et un seul module pour votre code d’interopérabilité, je recommande l’utilisation de scripts de déploiement.
Les scripts de déploiement peuvent être exécutés « manuellement » à l’aide du portail de gestion, du terminal, de VSCode, ou d’une combinaison de ces outils.
Par exemple, lors du déploiement de nouveaux business hosts, je copie généralement leurs paramètres depuis le bloc XData de la classe de production de l’environnement QA, puis j’ajuste les éléments XML obtenus dans l’environnement de production selon les besoins.
Dans les rares cas de changement complexe ou fastidieux, j’ai tendance à utiliser des méthodes de classe ad hoc pour mettre à jour l’interopérabilité par programmation. Ces méthodes sont testées dans l’environnement QA.
Correctifs rapides et petits changements
Les hotfixes et les petits changements de configuration qui n’affectent que quelques éléments peuvent être déployés à l’aide de :
- Le terminal : en utilisant les méthodes de la classe SYSTEM.OBJ ou les classes Security.* pour des éléments de sécurité tels que les rôles, les privilèges SQL et les applications
- L’édition côté serveur dans VSCode ou Studio : Studio prend en charge le glisser-déposer de classes entre instances, ce que je trouve très pratique.
- Le portail de gestion : pour importer manuellement des éléments compilables (classes, LUT, schémas HL7, …) et des éléments de configuration (applications web, rôles, …).