Article

Le sens des dépendances : qui doit connaître qui ?

Avant de connecter deux modules, une question décide du couplage : qui connaît qui ? Le sens des dépendances, pour éviter cycles et rigidité.

Deux modules doivent échanger des données. On débat du format, du protocole, de la technologie — presque jamais du sens de la flèche. C'est pourtant lui qui décide du couplage, et donc de ce qu'on pourra encore changer des mois plus tard.
A connaît B : la dépendance va de A vers B.
A connaît B : la dépendance va de A vers B — B, lui, ignore A.

Le problème se pose à toutes les échelles : deux services, deux classes, deux modules qui doivent communiquer. Le besoin est clair — A et B échangent des données — et c'est parce qu'il est clair qu'on saute l'étape qui compte. On tranche le format et le transport, qui sont visibles ; on laisse en suspens la seule décision qui vieillira mal : lequel des deux dépend de l'autre ?

Les symptômes arrivent plus tard, et on ne les rattache pas toujours à leur cause. On ne peut plus modifier B sans casser A. Les deux ne se déploient plus séparément. L'utilitaire placé « au centre » a fini par dépendre de tout le monde et bloque la moindre évolution. C'est chaque fois le même problème — le couplage — et il n'a pas été choisi mais subi : le sens de la dépendance a été laissé au hasard de ce qui était le plus rapide à coder.

Ce que « connaître » veut dire

Fixons le vocabulaire, car « connaître » désigne ici une relation technique précise. A connaît B s'il fait au moins l'une de ces trois choses, dans ce sens :

  • il envoie une commande à B (« fais ceci ») ;
  • il s'abonne à un événement émis par B (« ceci s'est produit ») ;
  • il interroge B (« donne-moi cette information »).
Les trois façons dont A connaît B : commande, événement, requête.
Les trois mécaniques par lesquelles A connaît B. La dépendance pointe toujours vers B, même quand l'événement, lui, voyage de B vers A.

Dès qu'un de ces gestes existe, A dépend de B : la flèche va de celui qui connaît vers celui qui est connu. Commande et requête sont les cas évidents — pour appeler ou interroger B, A doit connaître son existence, son adresse, son contrat. L'événement l'est moins. On le croit découplant ; il ne l'est que d'un côté. L'émetteur ignore ses abonnés et évolue librement ; l'abonné, lui, dépend du contrat de l'événement — son nom, sa charge utile, sa sémantique. Un abonnement est donc une dépendance au même titre qu'un appel, et il compte dans le graphe. L'oublier revient à y masquer une flèche.

Le sens de la flèche est la partie qui dure

Le format et le protocole se remplacent sans douleur. Le sens de la dépendance, non : il est inscrit dans la structure du code, le graphe de build, l'ordre de démarrage. Il obéit à une règle simple : on peut faire évoluer le composant connu sans toucher au composant qui connaît, jamais l'inverse. Le connu ignore le connaissant et peut changer tant qu'il respecte son contrat ; le connaissant, lui, suit son connu.

D'où l'orientation par défaut : le composant le plus stable et le plus métier ne dépend pas du plus volatil et du plus technique. Exemple : un module de facturation doit envoyer un e-mail à chaque vente. Réflexe courant : la facturation appelle le service d'e-mail — facturation connaît e-mail. Mais l'e-mail (gabarit, fournisseur, options) change bien plus souvent que les règles de facturation. La flèche est à l'envers : chaque changement d'un détail d'envoi remonte jusqu'au code comptable. En l'inversant — le service d'e-mail s'abonne à un événement « vente conclue » émis par la facturation — la facturation cesse de connaître l'e-mail : elle publie un fait métier, et c'est au détail volatil de s'y brancher. Quand la flèche pointe du stable vers le changeant, chaque modification du détail se propage vers le cœur : c'est la rigidité, la forme la plus coûteuse du couplage.

Le cas limite : le cycle

Plus grave que la flèche inversée : la flèche refermée. Un cycle, c'est A connaît B et B connaît A — aucun des deux n'évolue ni ne se déploie seul. On croyait avoir deux composants, on n'en a qu'un sous deux noms. Personne ne crée un cycle volontairement ; il apparaît quand on ajoute la flèche manquante parce qu'elle est, sur le moment, la plus pratique. Et il ne se referme pas qu'entre deux : il peut passer par des tiers, de façon bien plus discrète. Ce piège, et les autres formes que prend le couplage caché, font l'objet du prochain article.

Trois vérifications avant d'écrire la connexion

Les éviter ne demande aucun outil. Avant d'écrire la connexion, trois vérifications suffisent :

  • nommer A et B, en distinguant le cas d'usage de la capacité qu'il appelle ;
  • choisir le sens : placer la dépendance du côté qui porte la responsabilité de la relation, pas du côté où le code s'écrit le plus vite ;
  • vérifier l'absence de cycle, direct ou indirect (A → B → C → A compte aussi).

Sauter une de ces questions ne coûte rien sur le moment ; la dette se paie plus tard, en couplage — tout ce qu'on ne peut soudain plus modifier. Et rien, à l'instant où l'on code la flèche, ne signale qu'on vient de la contracter.

Reste à savoir entre quoi on tranche. Entre deux composants qui doivent échanger, il n'existe que quatre façons d'orienter la dépendance, chacune avec son usage et son piège. C'est l'objet du prochain article.

Laisser un commentaire

Vous devez être connecté pour pouvoir laisser un commentaire.