Pourquoi parler par messages
Quand un service en appelle cinq autres pour valider une commande, un seul qui tombe fait tout échouer. Le message asynchrone change la donne : voici pourquoi, et ce qu'il promet vraiment.
Tu cliques sur « Payer ». Derrière ce clic, ton service de commande doit prévenir le paiement, réserver le stock, planifier l’expédition, envoyer un email de confirmation et créditer le programme de fidélité. La question de tout ce cours commence ici : comment ces services se parlent-ils sans tomber ensemble ?
L’appel direct, ou le coup de téléphone
La façon la plus naturelle de faire parler deux services, c’est l’appel direct. Ton service de commande appelle le service de paiement, comme tu passerais un coup de téléphone : tu composes le numéro, et tu attends que l’autre décroche et te réponde avant de raccrocher.
Tant que tout le monde décroche vite, c’est parfait. Mais regarde ce qui se passe quand ta commande doit prévenir les cinq services, un par un, en attendant chaque réponse :
Deux ennuis surgissent. D’abord le temps : si chacun met cent millisecondes, l’utilisateur attend la somme, donc une demi-seconde, juste pour voir « Commande validée ». Ensuite la fragilité : si le service d’email est en panne, l’appel échoue, et toute la commande échoue avec lui, alors que l’email n’est qu’un détail de confort. Un maillon faible casse toute la chaîne.
Le message, ou la boîte aux lettres
Change d’image. Au lieu de téléphoner, ta commande écrit un mot et le dépose dans une boîte aux lettres. Elle repart aussitôt, sans attendre que quiconque l’ait lu. Chaque service relève la boîte quand il peut et traite le mot à son rythme.
C’est ça, un message : une information déposée quelque part, que l’émetteur n’attend pas de voir traitée. La communication asynchrone Communication asynchrone Mode d'échange où l'émetteur dépose un message et poursuit son travail sans attendre que le destinataire l'ait traité. Le message patiente dans une file ou un journal jusqu'à ce qu'un consommateur le prenne. S'oppose à la communication synchrone, où l'appelant reste bloqué jusqu'à la réponse. est ce mode d’échange où l’on dépose et où l’on poursuit, par opposition à l’appel synchrone où l’on reste bloqué jusqu’à la réponse.
L’email en panne ? Son mot patiente dans la boîte. La commande, elle, est déjà validée. Le service d’email traitera son courrier dès qu’il redémarre. Le maillon faible ne casse plus rien : il prend juste du retard, tout seul, dans son coin.
Trois façons d’être couplé
Pour comprendre ce que le message nous fait gagner, il faut nommer ce dont il nous libère : le couplage. Deux services sont couplés quand l’un ne peut pas fonctionner correctement sans une propriété de l’autre. On distingue trois formes utiles.
- Le couplage temporel Couplage temporel Dépendance qui force deux services à être disponibles au même instant pour qu'un échange réussisse. Un appel synchrone direct couple temporellement l'appelant et l'appelé : si l'appelé est en panne ou lent, l'appelant attend ou échoue. Le message asynchrone supprime ce couplage en intercalant une file qui accepte la demande même quand le destinataire est absent. : les deux doivent être disponibles au même instant. C’est le coup de téléphone : si l’autre ne décroche pas maintenant, l’échange échoue.
- Le couplage de disponibilité : la santé de l’appelant dépend de la santé de l’appelé. Si le paiement tombe, la commande tombe.
- Le couplage de localisation : l’appelant doit savoir où joindre l’appelé (son adresse, son port).
L’appel synchrone direct cumule les trois. Le message asynchrone les desserre : il intercale une boîte (une file, un journal) qui accepte le dépôt même quand le destinataire est absent. L’émetteur n’a plus besoin de savoir qui lira, ni quand, ni même si quelqu’un lira.
Commande ou événement : deux intentions, deux messages
Tous les messages ne se ressemblent pas. Deux intentions très différentes se cachent derrière le mot « message », et les confondre est la première erreur classique.
Un message de commande Message de commande Message qui exprime une intention, un ordre adressé à un destinataire précis pour qu'il fasse quelque chose (par exemple « Prélève le paiement »). Il est nommé à l'impératif, possède en principe un seul gestionnaire, et l'émetteur attend qu'un effet se produise. À distinguer du message d'événement, qui constate un fait déjà arrivé. exprime un ordre adressé à un destinataire précis : « Prélève le paiement ». Il se nomme à l’impératif, vise en principe un seul gestionnaire, et l’émetteur attend qu’un effet se produise. C’est encore proche du coup de téléphone, mais déposé dans une boîte.
Un message d’événement Message d'événement Message qui annonce un fait déjà survenu (par exemple « Commande payée »). Il est nommé au passé, diffusé à qui veut l'écouter, et l'émetteur ignore qui le consomme, voire si quelqu'un le consomme. Plusieurs abonnés peuvent réagir au même événement. À distinguer du message de commande, qui demande une action future à un destinataire unique. annonce un fait déjà survenu : « Commande payée ». Il se nomme au passé, il est diffusé à qui veut l’écouter, et l’émetteur ignore qui le consomme, voire si quelqu’un le consomme. Le stock, l’expédition, l’email et la fidélité peuvent tous réagir au même événement, chacun de son côté.
Cette bascule est profonde. Avec des commandes, ta commande sait encore qui appeler : elle pilote. Avec des événements, elle annonce juste « je suis payée » et se désintéresse de la suite : ce sont les autres qui s’abonnent. C’est le degré ultime de découplage, et c’est ce qui permet d’ajouter un sixième service demain sans toucher au service de commande.
Joue avec le couplage
Le composant ci-dessous met les deux mondes côte à côte. Tu passes une commande qui doit prévenir cinq services. Choisis le mode (synchrone ou asynchrone), coupe un service en cliquant dessus pour le mettre en panne, puis observe deux choses : combien de temps l’appelant reste bloqué, et ce qu’il advient de chaque service.
Services à prévenir
- Paiement50 ms
- Stock30 ms
- Expédition20 ms
- Email10 ms
- Fidélité15 ms
Trois questions à te poser en jouant :
- En mode synchrone, que devient le temps de blocage quand tu ajoutes des services sains ? Et en mode asynchrone ?
- Coupe le service d’email. Pourquoi le mode synchrone fait-il échouer toute la commande, alors que le mode asynchrone la valide quand même ?
- En mode synchrone, coupe le paiement (le premier de la liste). Que deviennent les services placés après lui ? Pourquoi restent-ils « non atteints » ?
Côté code : « envoyer un message » dans deux frameworks
L’idée de commande contre événement n’est pas qu’une vue de l’esprit : les frameworks de messaging la rendent explicite jusque dans leurs noms de méthodes. Regarde la même intention dans deux outils, l’un en Rust, l’autre en .NET.
Avec Hexeract, un framework de messaging en Rust, une commande attend un résultat et un événement se contente d’être publié :
// Commande : un ordre adressé, on attend le résultat.
impl Command for ProcessPayment {
type Output = PaymentId;
}
let payment_id = mediator.send(ProcessPayment { order_id, amount_cents }).await?;
// Événement : un fait passé, diffusé. L'émetteur ignore qui écoute.
impl Event for OrderPaid {
const EVENT_TYPE: &'static str = "orders.paid";
}
mediator.publish(OrderPaid { order_id }).await?;
Avec Wolverine, son équivalent en .NET, la distinction se lit dans le choix de la méthode du bus :
// Commande : exécute maintenant et attends le résultat.
var status = await bus.InvokeAsync<PaymentStatus>(new ProcessPayment(orderId, amountCents));
// Événement : publie à qui veut écouter, sans rien attendre.
await bus.PublishAsync(new OrderPaid(orderId));
Le couple est le même des deux côtés : un verbe qui attend un résultat pour la commande (send, InvokeAsync), un verbe qui diffuse sans attendre pour l’événement (publish, PublishAsync). Retiens le motif, pas la syntaxe : c’est l’intention du message qui choisit la méthode, jamais l’inverse.
Exercices
Prends une feuille et un crayon. Les corrigés sont juste en dessous, à ne regarder qu’après avoir essayé.
Exercice 1 : commande ou événement ?
Classe chacun de ces quatre messages en commande ou en événement, en t’appuyant sur le temps du verbe et sur le destinataire : (a) « Prélève le paiement », (b) « Commande payée », (c) « Envoie l’email de confirmation », (d) « Stock réservé ».
Exercice 2 : combien de temps attend l’utilisateur ?
Cinq services doivent être prévenus, avec des temps de traitement de 50, 30, 20, 10 et 15 millisecondes. Combien de temps l’utilisateur attend-il en mode synchrone, où l’appelant traite les services en série ? Et en mode asynchrone, où déposer un message coûte environ 5 millisecondes ?
En une phrase
Parler par messages, c’est remplacer un appel direct, qui couple deux services dans le temps et dans la panne, par un dépôt asynchrone qui les découple : l’émetteur poursuit son travail, et l’on distingue la commande, qui ordonne et attend, de l’événement, qui constate et diffuse.
1. Pourquoi un appel synchrone direct est-il fragile quand un service appelé tombe en panne ?
2. Quelle est la différence essentielle entre un message de commande et un message d'événement ?
3. En mode asynchrone, que devient le message destiné à un service momentanément en panne ?
Vers le chapitre suivant
On a opposé deux familles de messages : la commande qui ordonne, l’événement qui constate. Mais il manque un cas courant : quand un service a seulement besoin de demander une information, sans rien changer ni rien ordonner. « Quel est le solde de fidélité de ce client ? » n’est ni un ordre ni un fait passé. C’est une troisième famille, la requête, et c’est par elle que le chapitre suivant complétera la taxinomie des messages avant qu’on attaque les garanties de livraison.
Sources
- Hohpe, G. & Woolf, B. (2003). Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions. Addison-Wesley. Référence de l’éditeur
- Hohpe, G. & Woolf, B. « Command Message » et « Event Message ». Enterprise Integration Patterns. Command Message, Event Message
- Wolverine. Message Bus Basics: InvokeAsync, SendAsync, PublishAsync. Documentation officielle. wolverinefx.net