Vos services doivent se parler, et vous hésitez sur le tuyau à poser entre eux ? La question revient à chaque extraction, et je vois régulièrement la mauvaise option retenue par pure habitude. Pourtant, dès qu’on part du besoin réel, la frontière est nette.

Un appel synchrone quand on a besoin du résultat tout de suite pour continuer ; un événement quand on signale un fait sans bloquer. On choisit fait par fait, à partir du besoin, jamais par habitude du projet d’avant.

L’appel synchrone : j’ai besoin de ta réponse

A appelle B et attend sa réponse pour continuer. Le bon réflexe quand A a réellement besoin de ce que B renvoie pour répondre à l’utilisateur. Prenez le service commandes : il demande le prix au catalogue avant d’afficher le total. Sans ce prix, rien à afficher.

L’avantage saute aux yeux : le flux est direct, le résultat tombe sur-le-champ, et n’importe qui relit le code sans dérouler un schéma d’événements dans sa tête.

"Service B""Service A""Service B""Service A""A attend pour continuer""demande le prix""renvoie le prix"

Le risque tient en un mot, la chaîne. Si A attend B qui attend C, une panne de C remonte jusqu’à l’utilisateur, et chaque maillon empile sa part de fragilité. Chez un client, une chaîne de cinq services synchrones : tout au bout, un seul mettait 3 secondes les mauvais jours, et c’était la page entière qui ramait. On a serré les délais d’attente, prévu des replis, et surtout raccourci la chaîne.

L’événement asynchrone : je signale un fait

A publie un événement (« commande créée ») et passe à la suite. B réagit quand il peut, et d’autres services aussi. Bon choix quand A n’attend rien en retour pour continuer. Et quand plusieurs services doivent réagir au même fait, franchement c’est le seul qui tienne la route.

Le découplage devient fort : un consommateur tombé rattrape son retard plus tard, personne ne bloque A.

En face, la cohérence n’est plus instantanée. Les réactions arrivent avec un décalage, parfois quelques millisecondes, parfois bien plus quand un consommateur a pris du retard. Il faut aussi rendre le traitement idempotent, c’est-à-dire écrit pour qu’un même événement reçu deux fois produise le même résultat qu’une seule (sinon vous facturez deux fois la même commande), parce qu’un même événement peut effectivement vous être livré en double. Deux contraintes à poser dès la conception, pas après le premier doublon en prod.

La règle de choix

  • Besoin du résultat tout de suite pour continuer → synchrone.
  • Un fait à signaler, des réactions qui peuvent attendre, aucun blocage → événementiel.

L’erreur que je corrige le plus souvent

Du synchrone là où un événement suffirait. Le service commandes appelle directement l’email, la facturation, les statistiques, et il attend chacun. La moindre lenteur de l’un ralentit toute la commande. Or rien de tout ça n’est nécessaire pour confirmer la commande à l’utilisateur. Ces réactions devraient partir en événements, et la commande répondre sans les attendre.

L’erreur inverse se croise aussi, plus rare : tout basculer en événementiel, jusqu’à une demande de prix dont on a besoin sur-le-champ, et se retrouver à gérer des corrélations et des états intermédiaires là où un appel direct faisait l’affaire en trois lignes.

On choisit fait par fait, à partir du besoin, jamais par goût pour une approche ni parce que c’est comme ça qu’on s’y était pris la fois d’avant.

Une réserve, pour finir. Cette grille suppose que vous connaissez vos frontières de domaine. Sur un système encore mal découpé, où l’on ne sait pas qui est responsable de quelle donnée, ni le synchrone ni l’événementiel ne vous sauveront : vous ne ferez que déplacer la confusion sur le réseau. Le choix du tuyau vient après le découpage, pas avant.

À lire aussi