Implémenter saga et orchestration dans une architecture microservices
Le découpage d’une application en microservices améliore l’autonomie des équipes, la capacité de déploiement et l’élasticité des composants. En contrepartie, une opération métier qui s’exécutait autrefois dans une seule transaction SQL doit maintenant traverser plusieurs bases de données, plusieurs API et parfois plusieurs environnements cloud. La cohérence des données devient alors un problème distribué.
Le pattern Saga apporte une réponse pragmatique à cette situation. Il remplace la transaction globale par une suite de transactions locales, associées à des actions compensatoires en cas d’échec. Lorsqu’un orchestrateur pilote cette séquence, les responsabilités, les erreurs et les reprises peuvent être explicitées dans un flux centralisé, particulièrement utile pour les processus métier complexes.
Comprendre la transaction distribuée
Une saga découpe un processus en étapes indépendantes. Chaque microservice exécute sa propre transaction locale, valide son état, puis publie un événement ou répond à une commande. Si toutes les étapes aboutissent, le processus est terminé. Si une étape échoue, des compensations annulent les effets déjà produits, sans restaurer techniquement une transaction ACID unique.
Prenons une commande en ligne. Le service Commande crée la commande, le service Stock réserve les articles, le service Paiement capture les fonds et le service Livraison programme l’expédition. Une panne après le paiement ne permet pas de faire un simple rollback global : il faut libérer le stock et rembourser la transaction. Cette logique métier doit être conçue explicitement, car une compensation n’est pas toujours l’inverse parfait de l’action initiale.
Avant de définir les événements, cartographiez les invariants importants : une commande ne doit pas être expédiée sans paiement validé, une réservation doit expirer, et un remboursement doit être traçable. Des ressources consacrées à l’intégration applicative peuvent aider à relier ces contraintes aux systèmes existants et aux flux interservices.
Choisir entre chorégraphie et orchestration
Dans une saga en chorégraphie, chaque service écoute des événements et décide localement de l’action suivante. Le service Commande émet CommandeCréée, le service Stock réagit avec StockRéservé ou StockIndisponible, puis le service Paiement poursuit le processus. Cette approche limite le couplage direct et s’appuie bien sur Kafka, RabbitMQ, NATS ou un autre broker d’événements.
La chorégraphie devient toutefois difficile à comprendre lorsque le nombre de participants augmente. Le flux réel est dispersé dans les consommateurs, les événements et les règles de reprise. L’absence de coordinateur peut aussi provoquer des dépendances implicites. Elle convient surtout aux processus courts, stables et composés de services peu nombreux.
Avec l’orchestration, un orchestrateur possède l’état de la saga et envoie des commandes aux microservices. Il sait quelle étape est active, quelle compensation appeler et combien de tentatives effectuer. Cette centralisation facilite l’observabilité et la modification du processus. Des plateformes d’automatisation des flux peuvent compléter cette architecture lorsqu’il faut connecter des applications internes, des services tiers et des systèmes historiques.
Concevoir un orchestrateur fiable
Un orchestrateur ne doit pas devenir un « super service » contenant toute la logique métier de l’entreprise. Son rôle consiste à coordonner les étapes, conserver l’état du workflow et interpréter les résultats. Les règles propres au stock restent dans le service Stock, celles du paiement dans le service Paiement. Cette séparation protège les frontières métier et limite les dépendances.
Un état de saga peut être représenté par un identifiant unique, un statut global, l’étape courante, les résultats intermédiaires et l’historique des tentatives. Par exemple, les statuts EN_COURS, PAYEE, EXPEDIEE, ECHOUEE et COMPENSATION_EN_COURS rendent le parcours lisible. Les transitions doivent être valides et persistées avant ou avec l’émission des commandes, afin qu’un redémarrage ne fasse pas perdre la progression.
Pour éviter le double traitement, chaque commande doit porter un identifiant d’idempotence. Le service destinataire enregistre les identifiants déjà consommés et renvoie le même résultat lorsqu’un message est livré plusieurs fois. Le pattern Transactional Outbox est particulièrement utile : le service écrit sa modification métier et l’événement à publier dans la même base, puis un relais transmet l’événement au broker. On réduit ainsi le risque de valider une opération sans publier la notification correspondante.
Gérer les erreurs, délais et compensations
Les erreurs transitoires ne doivent pas déclencher immédiatement une compensation. Une indisponibilité réseau ou une réponse HTTP 503 peut être traitée par des tentatives limitées, avec temporisation exponentielle et décalage aléatoire. Chaque appel doit cependant posséder un délai maximal. Sans timeout, une saga peut rester bloquée indéfiniment et retenir des ressources métier.
Les erreurs permanentes suivent un autre chemin. Un paiement refusé peut annuler la réservation de stock, alors qu’un échec de livraison peut nécessiter une nouvelle tentative ou une intervention humaine. Il faut distinguer l’échec technique, l’échec fonctionnel et l’impossibilité de compenser. Dans ce dernier cas, la saga doit passer dans une file de traitement manuel avec un contexte complet, plutôt que masquer le problème derrière un statut générique.
L’idempotence concerne aussi les compensations. Une demande de remboursement peut être répétée après un timeout, sans provoquer plusieurs remboursements. Les opérations financières doivent donc utiliser une clé métier stable et interroger le prestataire avant toute nouvelle action. Pour les flux temps réel, les techniques présentées dans ce guide sur un système de vote temps réel illustrent l’intérêt d’un état partagé, d’événements rapides et d’une diffusion maîtrisée des changements.
Observer et tester le processus distribué
Une saga sans traçabilité devient rapidement impossible à diagnostiquer. Propager un identifiant de corrélation dans chaque commande, événement et journal permet de reconstruire le parcours complet. Les métriques doivent mesurer la durée par étape, le taux de compensation, le nombre de retries et les sagas bloquées. Les traces distribuées avec OpenTelemetry donnent une vue utile sur les appels synchrones et les messages asynchrones.
Les tests doivent couvrir les chemins heureux, les doublons, les pertes de messages, les réponses tardives et les redémarrages de l’orchestrateur. Les tests de contrat vérifient que les événements restent compatibles entre producteurs et consommateurs. Des tests d’intégration avec une base et un broker réels révèlent souvent des problèmes que les mocks ne reproduisent pas, notamment les conditions de course et l’ordre de livraison.
Pour les processus sensibles, un modèle d’état formel ou une simulation des transitions aide à repérer les cycles impossibles. On peut aussi analyser l’influence des paramètres sur les décisions métier : une lecture consacrée à la valeur de Shapley fournit une perspective intéressante pour comprendre la contribution de variables dans un système décisionnel, même si cette méthode relève davantage de l’IA explicable que de la coordination de transactions.
Déployer progressivement une saga
Commencez par un processus borné, avec peu de participants et des compensations compréhensibles. Une commande standard constitue souvent un meilleur candidat qu’un workflow administratif comportant de nombreuses exceptions. Définissez d’abord les contrats de commandes et d’événements, puis documentez les transitions, les délais et les propriétaires de chaque étape.
Le choix de l’orchestrateur dépend du contexte technique. Un service maison peut suffire pour un flux simple, tandis que Temporal, Camunda, Zeebe ou une solution cloud apporteront persistance d’exécution, reprise automatique et visualisation des workflows. Le choix ne doit pas reposer uniquement sur le nombre de fonctionnalités : examinez les compétences disponibles, la stratégie d’hébergement, les coûts opérationnels et la capacité à restaurer un processus après incident.
Enfin, prévoyez la compatibilité des versions. Un événement peut être consommé par plusieurs versions d’un service pendant un déploiement progressif. Utilisez des schémas versionnés, évitez les changements destructifs et conservez un historique exploitable. La migration d’une transaction synchrone vers une saga doit se faire par étapes, avec des indicateurs permettant de comparer les erreurs et les délais avant et après le changement.
Pour passer de la théorie à un flux opérationnel, modélisez un premier parcours, identifiez ses compensations, ajoutez l’idempotence et instrumentez chaque transition. Un prototype volontairement limité permettra de valider les contrats et la stratégie de reprise avant d’étendre l’orchestration à l’ensemble du domaine métier.