Read in English · Leer en español · Auf Deutsch lesen
Section 3.2 sur 3.8 · Intermédiaire · 24 min de lecture
Couplage et cohésion
Leçon vidéo
5 min · narré en anglais, avec sous-titresÉcouter seulement (audio)
Lire la transcription
Le couplage et la cohésion sont les deux plus anciennes mesures d’une bonne structure. Dans cette leçon, vous allez découvrir leurs niveaux classiques, comment compter les dépendances, les sortes de couplage supplémentaires entre services, et comment le relâcher.
Pensez à deux voisins qui partagent un jardin. S’ils se prêtent seulement un outil de temps en temps, chacun peut réaménager son propre jardin sans histoires. Ils sont faiblement couplés. S’ils partagent un abri, un tuyau d’arrosage et une clé cachée sous un pot de fleurs, chaque changement exige une conversation. Ils sont fortement couplés. Pensez maintenant à un tiroir de cuisine. S’il ne contient que des couverts, vous savez où chercher. Il est cohésif. S’il contient des couverts, des piles, de la ficelle et de vieux tickets de caisse, il ne l’est pas. Un bon logiciel a des parties cohésives, faiblement couplées les unes aux autres.
Le couplage est le degré de dépendance d’un module envers un autre, tel qu’un changement de l’un peut imposer un changement de l’autre. La cohésion est le degré auquel les éléments d’un module vont ensemble, parce qu’ils servent un même objectif. Mais le couplage n’est pas un défaut. Un système dont les parties ne dépendraient jamais les unes des autres ne ferait rien d’utile. Le but est de le choisir délibérément : peu de dépendances, par des interfaces étroites et stables, et aussi faibles que la tâche le permet.
La conception structurée, dans les années 1970, a classé les manières dont les modules peuvent être reliés, du plus fort à gauche au plus faible à droite. Le couplage de contenu consiste à pénétrer dans les éléments internes d’un autre module, comme un service qui écrit dans les tables de base de données d’un autre service. Le couplage commun consiste à partager des données globales, ou un même schéma de base de données. Le couplage externe consiste à partager un format imposé, comme la structure de fichier d’un partenaire. Le couplage de contrôle consiste à passer un indicateur qui dit à l’autre module quoi faire. Le couplage par estampille transmet un enregistrement entier quand seule une partie est nécessaire. Le couplage de données, le plus faible, ne transmet que des données simples, toutes nécessaires, par une interface étroite et documentée. La cohésion demande pourquoi les éléments d’un module sont ensemble. À l’extrémité faible, la cohésion accidentelle : un module utils qui contient le formatage des dates et un taux de TVA. À l’extrémité forte, la cohésion fonctionnelle : un module Tax qui calcule la TVA, et uniquement cela.
Les niveaux décrivent la nature de la dépendance. Les architectes les comptent aussi. Le couplage afférent est le nombre de modules qui dépendent d’un composant : ses flèches entrantes. Le couplage efférent est le nombre de modules dont il dépend : ses flèches sortantes. Ces termes viennent de l’anatomie, où les nerfs afférents transportent les signaux vers l’intérieur, et les nerfs efférents vers l’extérieur. Un composant au couplage afférent élevé, comme une bibliothèque de gestion de l’argent utilisée par quarante modules, est responsable. Tout changement peut en casser beaucoup d’autres : il doit donc changer avec prudence et rarement. Un composant au couplage efférent élevé, comme un flux de validation de commande qui appelle cinq autres modules, est exposé. Le problème survient quand un composant est les deux à la fois.
Entre services, trois autres sortes de couplage apparaissent, et aucune ne se voit dans la signature d’une fonction. Le couplage temporel signifie qu’une partie ne fonctionne que si une autre est disponible au même moment. Si la validation de commande appelle directement le service de paiement et que celui-ci est en panne, la validation l’est aussi. Le couplage de déploiement signifie que deux parties doivent être mises en production ensemble. Si chaque mise en production de Commandes exige une mise en production de Facturation, les deux forment en pratique une seule unité, quoi qu’en dise le schéma. Le couplage sémantique consiste à s’accorder sur une signification qu’aucune interface n’exprime, comme un statut quatre qui veut dire « expédiée », ou des prix exprimés en pence. Ces accords se rompent en silence.
Voici un exemple commenté de refactorisation. Avant, le module Commandes d’une boutique en ligne appelle directement Facturation à chaque commande, en lui passant un indicateur de mode. C’est un couplage de contrôle, et aussi un couplage temporel : Commandes attend Facturation. Facturation lit aussi directement la table des commandes, en décodant ses codes de statut et sa colonne de TVA. C’est un couplage commun, et un couplage sémantique de surcroît. Après, Commandes publie un événement OrderPlaced par l’intermédiaire d’un courtier de messages, qui transporte exactement les faits dont Facturation a besoin, y compris les totaux que Commandes a déjà calculés. Chaque service possède ses propres données. Ce qui reste est un couplage de données, par un petit contrat publié. Et Commandes n’attend plus Facturation.
Mesurons ce qui a changé. Avant, une table partagée, un indicateur de mode et des codes de statut cachés. Après, un seul événement versionné à cinq champs. Avant, une commande échouait si Facturation était en panne. Désormais, l’événement attend dans le courtier jusqu’à ce que Facturation soit rétabli. Avant, modifier la table des commandes exigeait une mise en production conjointe. Désormais, Commandes peut la modifier librement. Mais il y a de nouveaux coûts : un courtier à exploiter, des factures qui arrivent quelques secondes plus tard, et un événement qui est désormais un contrat public. Le couplage n’a pas été supprimé, il a été choisi. Il est désormais explicite, étroit et versionné.
La section complète contient des tableaux de tous les niveaux, un exemple commenté de décompte des dépendances, et les tactiques pour relâcher le couplage, chacune avec son prix. Ensuite, dans la section trois point trois, nous verrons le masquage de l’information. À très vite.
Ce qu’il faut savoir
- Le couplage est le degré de dépendance d’un module envers un autre ; la cohésion est le degré auquel les éléments d’un module vont ensemble. Visez un couplage faible et une cohésion forte, qui vont généralement de pair.
- Les échelles de la conception structurée classent le couplage du couplage de contenu, commun, externe, de contrôle et par estampille jusqu’au couplage de données (du plus fort au plus faible), et la cohésion de la cohésion accidentelle, logique, temporelle, procédurale, communicationnelle et séquentielle jusqu’à la cohésion fonctionnelle (de la plus faible à la plus forte).
- Le couplage afférent (Ca) compte les dépendances entrantes et le couplage efférent (Ce) les dépendances sortantes ; un module dont le Ca est élevé est risqué à modifier, et un module dont le Ce est élevé est exposé aux changements des autres.
- Les systèmes distribués ajoutent le couplage temporel (les deux côtés doivent être disponibles en même temps), le couplage de déploiement (ils doivent être mis en production ensemble) et le couplage sémantique (ils doivent partager une signification cachée), qu’une API propre ne suffit pas à supprimer.
- Parmi les tactiques qui réduisent le couplage figurent les interfaces étroites, les intermédiaires comme les adaptateurs et les courtiers, les événements à la place des commandes, et une propriété stricte des données, chacune avec son propre coût.
- Le couplage ne peut pas être éliminé, seulement choisi : une partie du couplage est la raison d’être du système ; le but est donc de le rendre explicite, faible et local là où le changement est probable.
Deux mesures de la structure
Le couplage est le degré de dépendance d’un module envers un autre, tel qu’un changement de l’un peut imposer un changement de l’autre. La cohésion est le degré auquel les éléments d’un module vont ensemble parce qu’ils servent un même objectif. Ces deux idées ont été introduites par Larry Constantine à la fin des années 1960, dans le cadre du mouvement de la conception structurée, et publiées en 1974 dans l’article Structured Design de Wayne Stevens, Glenford Myers et Constantine paru dans l’IBM Systems Journal, puis plus longuement dans le livre d’Edward Yourdon et Constantine, Structured Design (1979). La section 0.3 les a présentées de manière informelle ; cette section les précise.
Les deux sont liées. Si les choses qui changent ensemble vivent dans le même module (cohésion forte), alors moins de changements traversent les frontières des modules, et les modules ont besoin d’en savoir moins les uns sur les autres (couplage faible). Yourdon et Constantine allaient jusqu’à dire qu’en pratique, la cohésion était la plus importante des deux. Les tactiques de modifiabilité de la section 2.6 (augmenter la cohésion, réduire le couplage) reposent exactement sur ce couple.
Une précision d’emblée : le couplage n’est pas un défaut. Un système dont les modules ne dépendraient jamais les uns des autres ne ferait rien d’utile. Martin Fowler, qui écrivait sur le sujet en 2001, avertissait que chercher à supprimer entièrement le couplage ne fait que tout repousser dans un seul module géant, où le couplage est caché plutôt que disparu. Le but est de choisir le couplage délibérément : peu de dépendances, passant par des interfaces étroites et stables, orientées dans des directions sensées, et aussi faibles que la tâche le permet.
Les échelles classiques
Les niveaux de couplage
La conception structurée a classé les manières dont deux modules peuvent être reliés, de la plus nocive à la plus bénigne. La forme à six niveaux ci-dessous remonte généralement aux livres de Glenford Myers sur la conception composite du milieu des années 1970, et a ensuite été popularisée par Meilir Page-Jones ; les noms et les limites varient légèrement selon les sources, mais c’est la forme que l’on trouve dans la plupart des manuels. Ces niveaux ont été définis pour les procédures d’un même programme, mais chacun a un équivalent direct entre composants et entre services, et c’est là que les architectes les rencontrent aujourd’hui.
Niveaux de couplage, du plus fort au plus faible
| Niveau | Ce que cela signifie | Dans le code | Entre services |
|---|---|---|---|
| Contenu | Un module pénètre dans les éléments internes d’un autre : il modifie ses données privées ou saute dans son code. | Modifier cart._items depuis l’extérieur ; remplacer à chaud (monkey-patching) la fonction d’un autre module. | Lire ou écrire directement dans les tables de base de données d’un autre service. |
| Commun | Plusieurs modules lisent et écrivent les mêmes données globales. | Un dictionnaire global config que n’importe quelle fonction peut modifier. | Plusieurs services partageant un même schéma de base de données ou un même espace de clés de cache. |
| Externe | Des modules partagent un format, un protocole ou un périphérique imposé de l’extérieur. | Deux modules analysent tous deux le même format de fichier d’un fournisseur. | Plusieurs services dépendant de la structure de fichier d’un partenaire ou d’une version précise d’un protocole de messagerie. |
| Contrôle | Un module passe un indicateur qui dit à l’autre quoi faire, si bien que l’appelant connaît la logique interne de l’appelé. | process(order, mode="refund") | Une API avec un champ action qui sélectionne des comportements complètement différents. |
| Stamp | Un module reçoit toute une structure de données mais n’en utilise qu’une partie. | Passer l’objet Customer complet à une fonction qui n’a besoin que de l’adresse e-mail. | Envoyer l’enregistrement client complet dans chaque événement alors que les consommateurs ont besoin de deux champs. |
| Données | Les modules ne communiquent que par des paramètres de données simples, tous nécessaires. | send_receipt(email, order_id, total) | Une API ou un événement étroit et documenté, avec seulement les champs dont les consommateurs ont besoin. |
Deux mises en garde. D’abord, l’échelle est un guide du risque, pas une loi. Le couplage par estampille (stamp coupling) est souvent un compromis raisonnable : passer un objet entier est plus simple que d’en extraire six champs, et le seul coût est que le destinataire peut désormais casser quand on modifie des champs qu’il n’utilise même pas. Ensuite, l’échelle ne dit rien du nombre de dépendances ni de la fréquence à laquelle l’autre côté change. Un unique appel couplé par contrôle vers un module qui ne change jamais peut être inoffensif ; un couplage de données vers un module qui change chaque semaine peut faire mal. Plus loin dans ce chapitre, la connascence offre un vocabulaire plus fin, et Balancing Coupling in Software Design (2024), de Vlad Khononov, soutient que la force du couplage doit toujours être jugée en même temps que la distance entre les modules et leur volatilité.
# Control coupling: the caller chooses the callee's internal branch
def handle_payment(order, mode):
if mode == "charge":
... # call the provider, store the transaction
elif mode == "refund":
... # look up the transaction, reverse it
elif mode == "dry_run":
... # validate only
handle_payment(order, "refund")
# Data coupling: separate operations, each taking only what it needs
def charge(order_id: str, amount_pence: int) -> str: ...
def refund(transaction_id: str, amount_pence: int) -> None: ...
refund("tx_81f2", 1999)Les niveaux de cohésion
La cohésion demande pourquoi les éléments d’un module sont ensemble. Yourdon et Constantine ont décrit sept sortes d’association, là encore de la pire à la meilleure. Les niveaux inférieurs regroupent des choses pour des raisons sans rapport avec le problème (elles ont été écrites au même moment, ou elles s’exécutent au même moment) ; les niveaux supérieurs regroupent des choses parce qu’elles travaillent sur les mêmes données ou vers un même objectif unique. Les synthèses ultérieures des manuels s’accordent sur le classement général : les cohésions accidentelle et logique sont médiocres, les cohésions communicationnelle et séquentielle sont bonnes et la cohésion fonctionnelle est la meilleure, même si les écarts entre niveaux ne sont pas réguliers.
Niveaux de cohésion, du plus faible au plus fort
| Niveau | Les éléments sont regroupés parce que... | Exemple |
|---|---|---|
| Accidentelle | ils n’ont absolument aucune relation significative. | Un module utils contenant le formatage des dates, un décorateur de nouvelle tentative et un taux de TVA. |
| Logique | ils font le même genre de chose, choisie par un indicateur. | Un module exports doté d’une fonction qui écrit du CSV, du PDF ou du XML selon un paramètre. |
| Temporelle | ils s’exécutent au même moment. | Une routine startup() qui ouvre la base de données, préchauffe les caches et envoie un ping de supervision. |
| Procédurale | ils se suivent dans une séquence de contrôle, mais sur des données différentes. | Une fonction qui valide un formulaire, puis journalise l’adresse IP de l’utilisateur, puis rafraîchit un menu. |
| Communicationnelle | ils opèrent sur les mêmes données. | Un module qui calcule plusieurs rapports à partir du même historique de commandes. |
| Séquentielle | la sortie de l’un est l’entrée du suivant. | Analyser un relevé bancaire, puis classer ses lignes par catégorie, puis totaliser chaque catégorie. |
| Fonctionn. | ensemble, ils accomplissent une tâche bien définie et rien d’autre. | Un module Tax qui calcule la TVA d’un panier, et uniquement cela. |
À l’échelle de l’architecture, la cohésion revient à se demander si un composant ou un service a une seule raison d’exister. Un service appelé CustomerService qui gère la connexion, les points de fidélité et les préférences marketing a au mieux une cohésion logique : trois équipes le modifieront pour trois raisons sans rapport. Une bonne heuristique consiste à décrire l’objectif d’un module en une phrase sans employer et ; la section 3.5 développe cette idée sous la forme du principe de responsabilité unique, défini en fonction des personnes qui demandent les changements.
Couplage afférent et efférent
Les niveaux ci-dessus décrivent la nature d’une dépendance. Les architectes ont aussi besoin de les compter. Le couplage afférent (Ca) est le nombre de modules extérieurs à un composant qui dépendent de lui : ses flèches entrantes. Le couplage efférent (Ce) est le nombre de modules extérieurs dont il dépend : ses flèches sortantes. Ces termes viennent de l’anatomie, où les nerfs afférents transportent les signaux vers l’intérieur et les nerfs efférents vers l’extérieur. Robert C. Martin les a définis pour les paquets en 1994 en comptant des classes ; les outils actuels comptent des classes, des modules ou des paquets : vérifiez donc toujours ce que compte un outil avant de comparer des chiffres.
Les deux nombres racontent des histoires différentes. Un composant dont le Ca est élevé, comme une bibliothèque partagée money utilisée par quarante modules, est responsable : tout changement qui le touche peut en casser beaucoup d’autres, il doit donc changer avec prudence et rarement. Un composant dont le Ce est élevé, comme un flux de validation de commande qui appelle la tarification, le stock, le paiement, la livraison et les notifications, est exposé : il peut devoir changer chaque fois que l’un d’eux change. Ni l’un ni l’autre n’est un problème en soi ; le problème apparaît quand un composant est à la fois très sollicité par d’autres et lui-même dépendant de nombreux éléments volatils. La section 3.4 transforme Ca et Ce en la métrique d’instabilité de Martin et en règles fondées sur elle.
Exemple commenté
Exemple commenté : compter Ca et Ce dans une application de livraison de repas
Sept modules (Validation, Carte, Tarification, Paiements, Notifications, Suivi des livreurs et Administration) ont les dépendances suivantes (« A dépend de B » s’écrit « A vers B ») : Validation vers Carte, Validation vers Tarification, Validation vers Paiements, Validation vers Notifications ; Suivi des livreurs vers Notifications ; Tarification vers Carte ; Administration vers Carte, Administration vers Tarification.
| Carte | Ca = 3 (Validation, Tarification, Administration) ; Ce = 0 |
|---|---|
| Tarification | Ca = 2 (Validation, Administration) ; Ce = 1 (Carte) |
| Notifications | Ca = 2 (Validation, Suivi des livreurs) ; Ce = 0 |
| Validation | Ca = 0 ; Ce = 4 (Carte, Tarification, Paiements, Notifications) |
| Lecture | Carte est le module dont on dépend le plus : son interface demande donc le plus de soin et les changements les plus lents. Validation est le module qui dépend le plus des autres : il devrait donc être le plus facile à modifier, et celui qui absorbe les changements venus des autres. |
Le décompte prend quelques minutes avec un outil d’analyse des dépendances et transforme une impression vague (« Carte est un module central ») en un chiffre que vous pouvez suivre dans le temps.
Le couplage dans les systèmes distribués
Quand les modules deviennent des services qui communiquent par un réseau, les niveaux classiques s’appliquent toujours, mais trois autres sortes de couplage prennent de l’importance. Aucune n’est visible dans la signature d’une fonction, ce qui explique pourquoi les équipes qui découpent un système en services sont souvent surprises de voir à quel point les services restent couplés.
Le couplage temporel signifie qu’une partie ne fonctionne que si une autre est disponible et réactive au même moment. Un appel HTTP synchrone de Validation vers Paiements couple leur disponibilité : si Paiements est en panne, Validation l’est aussi, et la disponibilité combinée est le produit des deux (section 2.5). Des messages asynchrones passant par une file suppriment ce couplage, au prix de résultats obtenus à terme plutôt qu’immédiatement. (En conception au niveau du code, le même terme désigne parfois le fait que des appels doivent se faire dans un ordre fixe, comme open() avant read() ; le contexte rend en général le sens évident.)
Le couplage de déploiement signifie que deux parties doivent être mises en production ensemble, ou dans un ordre fixe, parce qu’un changement dans l’une casse l’autre. Si chaque mise en production de Commandes exige une mise en production simultanée de Facturation, les deux forment en pratique une seule unité déployable, quoi qu’en dise le schéma. Les changements rétrocompatibles, les API versionnées et le patron expansion-contraction (expand and contract) sont les remèdes habituels.
Le couplage sémantique signifie que deux parties doivent s’accorder sur une signification qu’aucune interface n’exprime : qu’une commande avec status = 4 est expédiée, que les prix sont en pence et TVA comprise, qu’un « client actif » a commandé au cours des 90 derniers jours. De tels accords se rompent en silence quand l’un des côtés change son interprétation. Le remède consiste à rendre la signification explicite dans le contrat (états nommés, unités dans les noms de champs, schémas publiés) ou à garder la règle en un seul endroit que les autres interrogent.
Tactiques pour réduire le couplage
Des interfaces étroites. N’exposez que le minimum d’opérations et de champs dont les clients ont besoin. Chaque champ supplémentaire dans une réponse d’API est une chose dont un client peut finir par dépendre (voir la section 3.3 sur la loi de Hyrum). Des intermédiaires. Placez quelque chose entre les deux parties pour qu’aucune ne connaisse les détails de l’autre : un adaptateur ou une couche anticorruption qui traduit le modèle d’un fournisseur, une API gateway (passerelle d’API), ou un courtier de messages (message broker) qui découple l’émetteur du destinataire dans le temps et dans l’identité. Des événements plutôt que des commandes. Une commande (Facturation, crée une facture) oblige l’émetteur à connaître le destinataire et sa tâche ; un événement (une commande a été passée) permet à l’émetteur d’énoncer un fait et à un nombre quelconque de destinataires d’y réagir, si bien que de nouveaux consommateurs peuvent être ajoutés sans modifier le producteur. La propriété des données. Chaque donnée a un seul propriétaire qui l’écrit ; les autres modules interrogent le propriétaire par son interface ou gardent leur propre copie alimentée par des événements, sans jamais lire les tables du propriétaire (section 7.1).
Chaque tactique a un prix. Les intermédiaires ajoutent un saut et un élément de plus à exploiter. Les événements renoncent à la cohérence immédiate et rendent le déroulement d’un processus métier plus difficile à voir en un seul endroit. Les copies de données peuvent être périmées. La propriété des données oblige à construire et à versionner des API qu’une table partagée vous donnait gratuitement. Le compromis vaut la peine là où le changement est fréquent ou les équipes séparées, et souvent pas entre deux classes maintenues par une seule personne.
Exemple commenté : refactoriser un couple commandes-facturation fortement couplé
Le module Commandes d’une boutique en ligne appelle directement Facturation chaque fois qu’une commande est passée. Facturation lit ensuite la table des commandes pour en trouver les détails, interprète les codes numériques de la colonne de statut et recalcule la TVA à partir des prix des lignes. Le code ci-dessous en est une version simplifiée ; il montre trois des problèmes à la fois.
# orders.py
def place_order(basket, customer_id):
order_id = db.insert("orders", customer_id=customer_id, status=1,
lines=basket.lines)
billing.process(order_id, mode="invoice") # Orders waits for Billing
return order_id
# billing.py
def process(order_id, mode):
row = db.query_one("SELECT * FROM orders WHERE id = %s", order_id)
if row["status"] != 1: # 1 means 'placed', by convention
return
net = sum(l["price"] * l["qty"] for l in row["lines"])
vat = round(net * 0.2) # VAT rule duplicated here
if mode == "invoice":
db.insert("invoices", order_id=order_id, net=net, vat=vat)
elif mode == "credit_note":
db.insert("credit_notes", order_id=order_id, net=-net, vat=-vat)Toute modification de la structure de la table des commandes, des codes de statut ou des règles de TVA doit désormais être faite dans les deux modules à la fois. Commandes ne peut pas accepter une commande si Facturation est lent ou en panne. Et les deux comportements de Facturation sont choisis par un indicateur que Commandes doit connaître. La refactorisation se déroule en quatre étapes : Commandes cesse d’appeler Facturation et publie à la place un événement OrderPlaced qui transporte exactement les faits dont Facturation a besoin, y compris les totaux que Commandes a déjà calculés ; Facturation s’y abonne et enregistre les factures dans son propre stockage ; les deux comportements deviennent deux opérations ; et la table des commandes devient privée à Commandes.
# orders.py
def place_order(basket, customer_id):
order = Order.create(customer_id, basket) # computes net and VAT once
repository.save(order)
events.publish("OrderPlaced", {
"schema_version": 1, "order_id": order.id,
"customer_id": customer_id,
"net_pence": order.net_pence, "vat_pence": order.vat_pence})
return order.id
# billing.py
@events.subscribe("OrderPlaced")
def on_order_placed(event):
create_invoice(event["order_id"], event["net_pence"], event["vat_pence"])
def create_invoice(order_id, net_pence, vat_pence): ...
def create_credit_note(order_id, net_pence, vat_pence): ...Le couple commandes-facturation avant et après
Exemple commenté
Ce qui a changé, mesuré
Comparaison des deux versions à l’aide du vocabulaire du couplage présenté dans cette section.
| Sorte de couplage | Avant : quasi-couplage de contenu et couplage commun (table partagée), de contrôle (indicateur de mode), sémantique (statut 1, règle de TVA). Après : couplage de données par un événement versionné à cinq champs. |
|---|---|
| Couplage efférent de Commandes | Avant : Ce inclut Facturation. Après : Commandes ne dépend que du contrat de l’événement et du courtier ; il ne sait pas que Facturation existe. |
| Couplage temporel | Avant : une commande échoue si Facturation est en panne. Après : l’événement attend dans le courtier jusqu’à ce que Facturation soit rétabli. |
| Couplage de déploiement | Avant : une modification de la table des commandes exige une mise en production conjointe. Après : Commandes peut modifier sa table librement ; seules les modifications de l’événement demandent de la prudence, et le versionnage s’en charge. |
| Nouveaux coûts | Un courtier à exploiter ; les factures apparaissent quelques secondes plus tard au lieu d’instantanément ; le schéma de l’événement est désormais un contrat public à versionner ; le processus est plus difficile à tracer sans une bonne observabilité. |
L’événement transporte les totaux calculés par Commandes : la TVA est donc calculée en un seul endroit. Facturation n’a plus besoin de connaître les règles fiscales ni la structure des lignes de commande.
La refactorisation a supprimé un choix, pas tout couplage : Facturation dépend toujours de la signification de OrderPlaced. Cette dépendance est désormais explicite, étroite et versionnée, et c’est ce que signifie un couplage faible en pratique.
Comment les professionnels utilisent ces idées
Les concepteurs expérimentés citent rarement le nom des niveaux lors des revues, mais ils les appliquent constamment : ils remettent en question chaque paramètre indicateur, recherchent les tables partagées et l’état global, demandent quel est l’objectif unique de chaque module, et observent dans la gestion de versions quels modules changent ensemble. Aux frontières entre équipes et entre services, ils sont stricts, car le couplage y est coûteux à défaire ; à l’intérieur d’un petit module détenu par une seule personne, ils sont plus souples, car il y coûte peu.
Ils rendent aussi le couplage visible. Un graphe de dépendances généré à partir du code, un décompte de Ca et Ce par composant et une liste des tables de base de données auxquelles accède plus d’un service font partie des vues les plus instructives d’une architecture, et tous peuvent être produits automatiquement et suivis dans le build. La section suivante aborde le principe qui maintient le couplage faible dès le départ : cacher les décisions derrière des interfaces.
Vérifiez votre compréhension
Pourquoi une fonction dotée d’un paramètre de mode qui sélectionne des comportements complètement différents est-elle un exemple de couplage de contrôle ?
Parce que l’appelant doit savoir comment l’appelé est organisé à l’intérieur et choisir sa branche interne ; modifier la logique de l’appelé peut imposer des modifications chez les appelants.
Un module a Ca = 12 et Ce = 1. Qu’est-ce que cela suggère ?
De nombreux modules dépendent de lui et il dépend de peu de choses : les modifications qui le touchent sont donc risquées et doivent être rares et prudentes ; son interface mérite une stabilité particulière.
Deux services ne communiquent que par des événements, mais doivent toujours être mis en production ensemble. Quelle sorte de couplage subsiste ?
Le couplage de déploiement, généralement causé par des modifications non rétrocompatibles de l’événement partagé ou par des accords sémantiques cachés.
Citez un coût du remplacement d’un appel direct par un événement.
Les résultats deviennent cohérents à terme plutôt qu’immédiatement ; vous héritez aussi d’un courtier à exploiter, d’un schéma d’événement à versionner et d’un déroulement plus difficile à tracer.
Sources et lectures complémentaires
Une question sur cette section ? ArchitectBot, l’assistant d’étude IA du cours, répond d’abord à partir du cours.
Demander : « Nos deux services partagent une même table de base de données. Comment les séparer étape par étape, sans migration en un seul bloc ? »