Aller au contenu principal

11. Le couplage : rendre les dépendances intentionnelles

Pourquoi ce chapitre existe

Le couplage est souvent traité comme un défaut à éliminer. Cette ambition est impossible : un système utile est fait de relations. Une commande dépend d'un client, un calcul dépend d'une politique, un service dépend d'une plateforme. Le problème n'est pas l'existence de ces dépendances ; c'est leur forme. Une dépendance invisible, instable ou trop large transforme un changement local en risque distribué.

Ce chapitre donne des critères pour distinguer les dépendances nécessaires de celles qui épuisent la capacité d'évolution.

Les idées essentielles

  • Le couplage est le degré auquel un changement dans une partie oblige d'autres parties à connaître, modifier ou déployer quelque chose.
  • Les formes les plus coûteuses sont souvent implicites : données partagées, conventions de temps, ordres d'événements, valeurs par défaut et comportements d'erreur.
  • Réduire le couplage ne veut pas dire multiplier les indirections. Cela veut dire stabiliser les engagements et masquer les détails changeants.
  • Les dépendances doivent être observables : on doit savoir qu'elles existent, qui les possède et comment elles échouent.

Les dépendances qui ne portent pas leur nom

Un appel d'API est visible : un type, une URL, un timeout et souvent une équipe propriétaire permettent d'en discuter. Les dépendances les plus dangereuses sont plus discrètes. Deux applications peuvent écrire la même table. Un consommateur peut interpréter l'absence d'un champ comme « gratuit ». Un job peut supposer qu'un événement arrive avant minuit. Une alerte peut dépendre d'un libellé qui vient d'être renommé.

Ces contrats existent même lorsqu'aucune interface ne les formalise. Les ignorer ne les rend pas souples ; ils deviennent simplement impossibles à versionner et difficiles à tester. Le premier travail consiste à les nommer.

Forme de couplageExempleRéduction de risque
StructurelUne application lit la table interne d'une autreContrat ou vue explicitement possédée
TemporelDeux déploiements doivent avoir lieu dans un ordre précisCompatibilité de transition et étapes séparées
Sémantiquestatus = active n'a pas le même sens selon le lecteurVocabulaire partagé et contrat documenté
OpérationnelUne dépendance lente bloque un parcours critiqueTimeout, limite, dégradation et instrumentation
OrganisationnelToute évolution requiert trois équipesFrontière, délégation ou interface de décision plus claire

Le prix des détails exposés

Lorsqu'un consommateur connaît les détails de l'implémentation d'un producteur, tout changement dans ces détails devient une coordination. Ce prix peut être acceptable pour un prototype ou une optimisation ponctuelle. Il doit alors être explicite. Le danger vient des détails exposés par défaut : types internes réutilisés, accès direct à une base, configuration copiée, règles de retry implicites.

Un bon contrat n'est pas nécessairement riche. Il expose le minimum qui permet au consommateur d'atteindre son objectif et décrit les garanties réellement tenues. Une interface générique qui révèle toutes les structures internes n'est qu'une base de données déguisée.

Concevoir les défaillances comme des dépendances

Dire qu'un service « dépend » d'un autre signifie aussi qu'il dépend de ses lenteurs, de ses erreurs, de ses limites de débit et de sa récupération. Une dépendance synchrone dans un parcours utilisateur doit déclarer ce qui se passe lorsqu'elle est indisponible : attente, erreur claire, donnée potentiellement ancienne, mise en file, ou fonctionnalité réduite. Ne rien choisir revient souvent à choisir par défaut un timeout long et une erreur vague.

La même rigueur vaut pour les dépendances asynchrones. Publier un événement découple le temps d'exécution ; cela ne supprime ni la responsabilité du producteur, ni le besoin de versionner le contrat, ni la nécessité de traiter les doublons et les retards.

L'indirection n'est pas automatiquement du découplage

Une couche supplémentaire améliore le découplage seulement si elle porte une abstraction stable. Un proxy qui transmet tous les détails, une interface avec une méthode par table ou une façade qui fuit les mêmes erreurs ne font qu'ajouter un endroit où chercher la cause d'un problème.

Anti-patterns

Partager une base de données comme moyen d'intégration durable

L'accès direct paraît efficace : aucune API à construire, aucune latence réseau. Il lie toutefois les lecteurs aux migrations, aux conventions et aux performances internes de l'écrivain. Si ce compromis est nécessaire, il doit être limité par des vues, droits d'accès, schémas de lecture et un propriétaire clair — pas laissé comme raccourci implicite.

Réutiliser des modèles internes comme contrats

Un type partagé réduit du code au début mais synchronise les cycles de changement. Ajouter une propriété ou changer une validation devient risqué pour tous les consommateurs. Préférer des modèles de contrat intentionnels, même s'ils se ressemblent, permet aux deux côtés d'évoluer avec leurs propres contraintes.

Masquer les erreurs d'une dépendance

Transformer toute erreur en valeur vide donne une apparence de disponibilité tout en détruisant le signal nécessaire au diagnostic. Un comportement de repli doit être visible pour l'utilisateur quand il l'affecte, mesuré pour l'équipe et choisi pour une raison métier.

Bonnes pratiques

Établir un contrat d'échec

Pour les dépendances significatives, décrire autant les échecs que le chemin nominal : délais, erreurs attendues, limites de volume, règles de retry, idempotence et comportement de dégradation. Ce contrat permet aux deux côtés de concevoir les tests, les alertes et les évolutions sans se reposer sur des suppositions.

Réduire d'abord le couplage le plus coûteux

Chercher le couplage zéro mène souvent à une architecture sur-abstraite. Identifier plutôt la dépendance qui bloque le plus de changements, génère le plus d'incidents ou exige le plus de coordination. Une amélioration ciblée de contrat, d'observabilité ou de propriété peut avoir davantage d'effet qu'un programme de découplage général.

Checklist — Une dépendance est-elle maîtrisée ?

  • Quel objectif justifie cette dépendance et quel détail interne pourrait-elle exposer inutilement ?
  • Qui possède le contrat, ses versions et les décisions d'évolution ?
  • Que se passe-t-il si la dépendance est lente, indisponible, en retard ou reçoit un doublon ?
  • Les données, valeurs par défaut et sémantiques de temps sont-elles explicites ?
  • Les échecs et dégradations sont-ils détectables par l'équipe et compréhensibles par l'utilisateur ?
  • Cette dépendance réduit-elle un coût réel, ou son indirection ne fait-elle que le cacher ?

À retenir

Les dépendances ne sont pas des impuretés architecturales. Elles sont des engagements à rendre explicites. Un système évolue bien lorsqu'il sait ce qui le relie, quelles garanties ces liens portent et comment ils se comportent lorsque le monde réel ne répond pas comme prévu.