Passer une commande touche trois services : le stock réserve les articles, le paiement autorise la carte, l'expédition crée un envoi. Dans un monolithe, c'est une seule transaction de base de données. Une fois découpé en services dotés de leurs propres bases, aucune transaction ne couvre les trois, et un commit à deux phases entre services est rarement envisageable en pratique.
Le pattern saga est la réponse standard. Cet article explique comment fonctionne une saga, la différence entre orchestration et chorégraphie, comment s'articulent compensations et étapes pivots, et les problèmes d'isolation que les sagas ne résolvent pas à votre place. Le diagramme montre une saga de commande orchestrée.
Ce qu'est une saga
Une saga est une suite de transactions locales. Chaque étape est validée dans la base de son propre service et déclenche la suivante. Si une étape échoue, la saga exécute des transactions de compensation qui annulent les effets des étapes déjà validées.
L'idée est plus ancienne que les microservices : Hector Garcia-Molina et Kenneth Salem ont décrit les sagas en 1987 dans un article sur les transactions de base de données de longue durée. Le pattern est revenu parce que les frontières entre services ont recréé le même problème.
Deux conséquences façonnent tout le reste :
- Cohérence à terme. Entre la première et la dernière étape, le système se trouve dans un état intermédiaire que d'autres requêtes peuvent voir. La commande est PENDING, le stock est réservé, la carte est autorisée mais pas encore débitée.
- Une compensation n'est pas un rollback. On ne peut pas « désenvoyer » un e-mail ni annuler un débit en silence. Une compensation est une nouvelle action métier, comme annuler une autorisation ou émettre un remboursement, qui inverse sémantiquement l'action précédente.
Étapes compensables, pivots et rejouables
Classez chaque étape avant de dessiner quoi que ce soit. Le guide du pattern saga de l'Azure Architecture Center en distingue trois types :
| Type | Signification | Exemple |
|---|---|---|
| Compensable | Peut être annulée par une autre transaction | Réserver le stock ; autoriser le paiement |
| Pivot | Le point de non-retour : une fois qu'elle réussit, la saga doit aller au bout | Créer l'expédition |
| Rejouable | Vient après le pivot et doit finir par réussir ; idempotente | Envoyer l'e-mail de confirmation |
L'ordre compte. Placez d'abord les étapes compensables, puis le pivot, et seulement des étapes rejouables après lui. Si le pivot échoue, compensez ; une fois qu'il a réussi, rejouez tout ce qui suit jusqu'à ce que cela réussisse. Une saga avec une étape compensable après le pivot ne peut pas atteindre un état cohérent quand cette étape échoue.
Orchestration ou chorégraphie
Il y a deux façons de coordonner les étapes.
Orchestration : un orchestrateur de saga, souvent une machine à états avec un état durable, dit à chaque service quoi faire et attend sa réponse. Il gère la séquence, les nouvelles tentatives, les timeouts et les compensations.
Chorégraphie : il n'y a pas de coordinateur. Chaque service écoute des événements et réagit : le stock reçoit OrderCreated et réserve les articles, le paiement reçoit StockReserved et autorise, et ainsi de suite. Les échecs sont aussi des événements, et chaque service sait quelle compensation exécuter.
| Orchestration | Chorégraphie | |
|---|---|---|
| Où vit le flux | À un seul endroit : l'orchestrateur | Réparti dans les gestionnaires d'événements des services |
| Ajouter une étape | Modifier l'orchestrateur | Changer qui écoute quel événement |
| Voir l'état d'une commande | Interroger le journal de la saga | Le reconstituer à partir des événements |
| Couplage | Les services connaissent les commandes de l'orchestrateur | Les services connaissent les événements des autres |
| Cas adapté | Beaucoup d'étapes, des branchements, un ordre strict | Deux ou trois étapes, des flux simples |
La chorégraphie est agréable pour les flux courts et pénible quand quelqu'un demande « pourquoi la commande 8812 est-elle bloquée ? ». L'orchestration donne à cette question un seul endroit où chercher. Des moteurs de workflow comme Temporal, AWS Step Functions ou Camunda fournissent une orchestration durable, pour que vous n'ayez pas à construire la machine à états vous-même. La page de Chris Richardson sur le pattern saga propose des exemples détaillés des deux styles.
Isolation : la partie que les sagas vous laissent
Une transaction de base de données cache les états intermédiaires aux autres transactions. Une saga, non. Pendant qu'une saga de commande s'exécute, une autre requête peut lire le stock réservé, ou une deuxième saga peut modifier le même enregistrement. Les parades habituelles :
- Verrou sémantique. Marquez les enregistrements comme en cours (une commande en PENDING, du stock en RESERVED) pour que les autres opérations sachent qu'elles doivent attendre ou refuser.
- Mises à jour commutatives. Concevez les opérations pour qu'elles donnent le même résultat dans n'importe quel ordre, par exemple en incrémentant et en décrémentant des compteurs au lieu de les fixer.
- Relire avant d'écrire. Vérifiez qu'une valeur n'a pas changé depuis que la saga l'a lue, et échouez ou recommencez si c'est le cas.
Rendez chaque étape et chaque compensation idempotente, avec l'ID de la saga et l'étape comme clé. Les messages seront livrés deux fois, et un « annuler l'autorisation » rejoué ne doit pas annuler un autre paiement.
Timeouts et sagas bloquées
Chaque étape a besoin d'une échéance. Un service de paiement qui ne répond jamais laisse la saga attendre indéfiniment et le stock réservé. Donnez un timeout à chaque étape, traitez un timeout comme un échec avant le pivot (compenser) et comme une raison de réessayer après, et déclenchez une alerte sur les sagas qui restent dans un état plus longtemps que prévu. Un petit tableau de bord des sagas par étape en cours répond à lui seul à la plupart des questions du support. Enregistrez aussi pourquoi chaque saga s'est terminée.
Le diagramme d'architecture
Contenu du diagramme
- Order service
- Saga orchestrator
- Saga log
- Inventory service
- Payment service
- Shipping service
- All steps succeeded?
- Order APPROVED
- Compensate in reverse order
- Order REJECTED
- start
- persist each step
- 1. reserve stock
- 2. authorize payment
- 3. create shipment
- yes
- no
- void, release
Comment le lire :
- À gauche : le service de commandes crée la commande en PENDING et lance la saga.
- Au milieu : l'orchestrateur exécute les étapes dans l'ordre et enregistre chacune dans le journal de la saga, pour qu'un redémarrage reprenne là où il s'était arrêté.
- À droite : les trois services, chacun annoté de son numéro d'étape et de son type. Le stock et le paiement sont compensables ; l'expédition est le pivot.
- En bas : si toutes les étapes réussissent, la commande passe en APPROVED. Si une étape avant le pivot échoue, l'orchestrateur compense dans l'ordre inverse (annuler l'autorisation, puis libérer le stock) et la commande passe en REJECTED.
- Le tableau Markdown associe chaque action à sa compensation ; c'est la première chose qu'un relecteur devrait vérifier.
Ce qu'il faut mettre sur un diagramme de saga
- L'ordre des étapes et le type de chacune. Compensable, pivot ou rejouable doit figurer sur la boîte.
- Chaque compensation. Une étape sans compensation est soit le pivot, soit rejouable, soit un bug.
- L'endroit où vit l'état de la saga. Une saga qui ne survit pas à un crash à mi-parcours n'est pas terminée.
- Les états intermédiaires visibles. PENDING et RESERVED font partie de la conception ; d'autres fonctionnalités les verront.
Les sagas vont naturellement de pair avec le pattern transactional outbox, car chaque étape doit mettre à jour sa base et envoyer un message sans perdre ni l'un ni l'autre. Le diagramme d'architecture microservices montre où se situent ces services, et le diagramme d'intégration d'une passerelle de paiement détaille les étapes d'autorisation et de capture.
Le construire dans Archboard
- Connectez-vous avec Google ou GitHub et créez un diagramme.
- Ajoutez des nœuds Conteneur du pack Modèle C4 pour le service de commandes, l'orchestrateur de saga et les trois services participants, ainsi qu'une Base de données pour le journal de la saga.
- Indiquez le numéro et le type de l'étape (compensable ou pivot) dans le champ technologie de chaque service.
- Ajoutez une Décision, un Processus et deux nœuds Début / Fin du pack Logigramme pour les chemins de succès et de compensation.
- Ajoutez un bloc Markdown du pack Base avec un tableau des actions et de leurs compensations.
L'exemple utilise 11 nœuds de diagramme et reste dans les limites de l'offre Free.
FAQ
Quelle est la différence entre une saga et une transaction distribuée ? Une transaction distribuée (commit à deux phases) verrouille des ressources dans plusieurs bases jusqu'à ce que tout le monde valide. Une saga valide chaque étape localement et annule les étapes précédentes par des compensations si une étape suivante échoue, en échangeant l'isolation contre la disponibilité.
Faut-il choisir l'orchestration ou la chorégraphie ? L'orchestration pour les flux avec beaucoup d'étapes, des branchements ou un ordre strict, parce que l'état de chaque saga vit à un seul endroit. La chorégraphie pour les flux courts où deux ou trois services réagissent aux événements des autres.
Qu'est-ce qu'une transaction pivot ? L'étape après laquelle la saga ne peut plus être compensée. Tout ce qui précède doit être compensable ; tout ce qui suit doit être rejouable jusqu'à réussir.
Comment gérer les échecs d'une compensation ? En la rejouant. Les compensations doivent être idempotentes et finir par réussir ; si l'une n'y parvient pas, la saga a besoin d'une procédure de résolution manuelle et d'une alerte.
Les sagas m'apportent-elles l'isolation ? Non. D'autres requêtes peuvent voir les états intermédiaires. Utilisez des verrous sémantiques comme les états PENDING, des mises à jour commutatives et des relectures pour éviter les anomalies qui comptent pour votre métier.