Dépendances entre modules : choisir qui dépend de qui
Une dépendance détermine quels modules devront changer ensemble. Un exemple de facturation pour choisir son sens, isoler les détails et éviter les cycles.
Article
Une application peut se déployer en un bloc et garder des modules distincts. Exemple avec l’inscription et la facturation, leurs données et leurs échanges.
Une modification de la facturation oblige à retoucher l'inscription, les commandes et plusieurs écrans. Le problème vient peut-être des dépendances entre ces parties, avant même de concerner le déploiement. Un monolithe modulaire cherche à les séparer clairement tout en conservant une application livrée comme un ensemble.
Le mot monolithe décrit ici une application déployée comme une unité. Il ne dit pas, à lui seul, si son code est bien organisé. On peut avoir des modules nets dans un même déploiement, comme des dépendances très fortes entre des services déployés séparément.
Trois décisions méritent donc d'être examinées séparément : quelles parties du métier isoler, comment organiser leur code et comment les déployer. CQRS peut aider à séparer commandes et lectures dans certains modules ; il n'est pas requis pour rendre un monolithe modulaire.
Prenons l'inscription et la facturation. À l'inscription, un client possède une identité, des moyens de connexion et des consentements. À la facturation, il possède une adresse de facturation, un numéro de TVA et un historique de règlements.
Les deux parties parlent de « client », mais n'ont pas besoin du même modèle. Les réunir dans une classe unique peut obliger chaque équipe à comprendre des règles qui ne la concernent pas.
Le DDD, ou conception guidée par le métier, appelle contexte délimité une zone dans laquelle un modèle et son vocabulaire ont un sens précis. Cette notion aide à trouver les frontières. Un contexte peut correspondre à un module ou en regrouper plusieurs : ce n'est pas une correspondance mécanique.
Des dossiers « contrôleurs », « services » et « accès aux données » organisent le code par rôle technique. Ils peuvent être utiles, mais une même fonctionnalité les traverse tous.
Un module de facturation regroupe au contraire les règles, les accès aux données et les points d'entrée nécessaires à la facturation. Il peut lui-même être organisé en couches. Les deux formes de découpage se complètent.
La frontière devient utile quand le reste de l'application passe par une interface définie, plutôt que par les classes internes ou les tables du module.
Trois formes d'échange donnent un vocabulaire simple :
Dans un même processus, ces échanges peuvent utiliser des interfaces locales. Un bus interne est une option pour acheminer les messages ; il n'est pas indispensable dans tous les cas. Le sens des dépendances reste à définir explicitement.
Le module de facturation possède ses données et les règles qui les modifient. Un autre module lui demande une opération ou une information, au lieu d'écrire directement dans ses tables.
Il peut aussi conserver une copie limitée des informations dont il a besoin. Cette copie réduit certaines dépendances, mais exige une mise à jour et une gestion du retard. Elle se choisit pour un usage précis.
Une convention écrite ne suffit pas toujours. La visibilité des types, les dépendances autorisées entre projets et les tests d'architecture peuvent empêcher un module d'importer des classes internes d'un autre.
Lorsque des éléments sont partagés, leur rôle doit rester clair. Un identifiant ou un petit contrat commun peut être pertinent. Un modèle métier commun qui grossit au gré des besoins risque de réunir de nouveau les responsabilités que l'on voulait séparer.
Un contrôle utile consiste à regarder une modification récente : quelles parties ont dû changer et pourquoi ? Des changements simultanés fréquents peuvent signaler une frontière mal placée ou une interface trop liée aux détails internes.
Un déploiement unique simplifie souvent le lancement local, les livraisons et le suivi des erreurs. Plusieurs instances de l'application peuvent servir davantage de trafic, si son fonctionnement et son stockage s'y prêtent.
En contrepartie, les modules partagent des ressources et un cycle de livraison. Un traitement coûteux peut affecter ses voisins. Multiplier les instances réplique aussi les parties qui n'en avaient pas besoin.
Ces limites peuvent justifier plus tard l'extraction d'un service. Une frontière déjà claire facilite le travail, mais le passage au réseau ajoute toujours des délais, des échecs partiels et des reprises à traiter.
Pour commencer, choisissez une responsabilité bien identifiée, attribuez-lui ses données et faites passer ses consommateurs par une interface. Vous pourrez ensuite mesurer si ce découpage rend les modifications plus locales.
Explorer
Choisis dans l’index d’après les étiquettes de cet article — les plus proches d’abord, hors de sa série. Rien n’est écrit à la main : un article publié demain avec les mêmes étiquettes y prendra place.
Une dépendance détermine quels modules devront changer ensemble. Un exemple de facturation pour choisir son sens, isoler les détails et éviter les cycles.
Appel direct, observation, médiateur ou contrat partagé : comment relier deux modules, choisir la responsabilité de chacun et repérer les dépendances en boucle.
Une donnée absente ou ancienne ne bloque pas tous les usages. Choisir, opération par opération, quand attendre, limiter le service ou accepter un retard.
Laisser un commentaire
Votre commentaire sera relu avant d'être publié.