Dans le monde PHP, pour ne citer que lui, on peut rencontrer le même fonctionnement, avec les fichiers à déployer fournis sous forme d'archive compressée « .zip » par exemple.
Pourtant, sauf en de rares exceptions, une application ne se limite pas à du code exécutable et à des ressources statiques, mais a besoin de ressources externes telles qu'une base de données, un service de messages, ou des services tiers comme des services web.
La base de données est une ressource externe au code déployé
Prenons le cas le plus courant, où l'application utilise une base de données seulement.
En ce qui concerne ce lien, l'application contient du code exécutable, qui encapsule du SQL, qui attaque base de données.
Pour le déploiement, d'un côté on a créé la structure de la base de données, de l'autre on a déposé l'archive de l'application.
Ces deux déploiements conjoints doivent produire un environnement global cohérent. Penser aux « contrats d'interfaces ».
La correspondance d'interfaces entre le client et la ressource invoquée, est cruciale.
Apporter la cohérence par le déploiement lui-même
L'application qui crée la base de données dont elle a besoin
Une astuce qu'emploient de nombreuses applications destinées au grand public est d'opérer la création de la base de données depuis la base de données elle-même.
(J'ai barré pour dire que cela ne me convient pas)
On met l'accent sur la facilité pour l'utilisateur lambda d'initialiser l'environnement d'exécution.
C'est le modèle choisi pour quasiment toutes les applications PHP. Plusieurs frameworks Java offrent également cette possibilité.
Cette approche est sympathique, mais n'est pas forcément cohérente avec une logique de production, et, surtout, de maintenance.
Je trouve d'autre part que c'est prendre un risque opérationnel, car même en production, le code qui permet de supprimer et de recréer les tables de la base de données est embarqué.
L'outil de déploiement couteau suisse
En posant la distinction entre composants déployés et outillage d'administration, on peut utiliser un outil de déploiement qui saura s'occuper de déployer et d'administrer aussi bien l'application que la base de données.
Même si elle a un très gros intérêt, une faiblesse de cette approche est qu'elle réclame la mise en œuvre d'un outil qui sache tout faire, et surtout son utilisation systématique : que se passe-t-il si un administrateur passe outre la procédure outillée ? Un ALTER TABLE en manuel est si vite arrivé…
Contrôler la cohérence avant le démarrage de l'application
Pour la qualité il n'est souvent pas nécessaire d'engager de grandes manœuvres ou de mettre en place des outils contraignants : il suffit parfois de contrôler quelques critères et de bloquer ou de continuer la procédure en fonction.
Une approche consiste alors à embarquer dans le module applicatif déployé une description du modèle physique de données attendu, et de le comparer avec la structure réelle de la base de données.
On bloque le processus de démarrage de l'application si les deux ne correspondent pas (un simple « hash » peut suffire).
Personnellement j'utilise comme référence du modèle un fichier YAML posé dans le répertoire WEB-INF/ de l'archive déployée, ce qui me permet d'avoir une approche unique pour PHP et J2EE/JEE.
Pour Java, on peut penser à introspecter les classes persistantes afin d'en déduire le modèle attendu.
Une sécurité supplémentaire est d'embarquer dans l'application elle-même, non pas du code de manipulation de base de données, mais l'outil de comparaison. L'application elle-même refuse alors de démarrer si la structure de la base de données n'est pas cohérente avec le modèle attendu.
Autres vérifications
Le fichier de référence qui décrit le MPD cohérent avec l'exécution de l'application peut servir à d'autres contrôles, et notamment les suivants :
- cohérence entre le MPD et les requêtes SQL externalisées (penser aux objets PreparedStatement)
- cohérence entre le MPD et les classes persistantes des divers frameworks ou approches (Hibernate, JDO…)
En phase de tests, on souhaite d'une façon ou d'une autre que l'application soit autoporteuse : à la fois déployer une application et exécuter des scripts SQL de modification de la base de données (de tests), peut être inutilement coûteux.
J'ai parlé plus haut des frameworks qui permettent ça nativement, en disant cependant que l'approche qui mixe modules applicatif et d'administration ne me convenait pas.
Le fait d'embarquer, dans le module applicatif lui-même, une description de référence du modèle attendu, permet de recréer à volonté une base de données de tests cohérente.
Pour ma part je fais cela avec un plugin Maven.
Approche générale, autres ressources
Le point crucial de ce qui précède est de bloquer le démarrage de modules dans le cas où on a décelé que les chaînes de liaison ne sont pas correctes.
Pour cela, l'idée proposée est simplement que le module client sache dire de quelle interface il a besoin, et que la ressource invoquée sache dire au runtime quelle est sa structure.
On obtient une cartographie « à froid » des composants déployés.
On fait ensuite une vérification statique des structures lues, grâce à des outils externes aux modules et ressources déployés.
Aucun outillage lourd n'est nécessaire en amont.
Il faut se rappeler du reste que, dans certains cas réels, l'outillage lourd en amont n'est pas suffisant à assurer la cohérence entre tous les composants déployés, en particulier quand les procédures de déploiement ne sont pas entièrement automatisées.
L'approche pragmatique présentée, complémentaire à l'outillage amont, et qui fait des sanity checks simples, permet de détecter des failles évidentes.