Saga : chorégraphie vs orchestration
Deux topologies pour exécuter une saga long-running multi-services. La chorégraphie distribue le contrôle dans le réseau d'événements ; l'orchestration le concentre dans un coordinateur explicite. Le choix conditionne 80 % de l'observabilité du flux EDI.
Problème
Une commande B2B typique traverse 5-10 systèmes avant d'être facturée : hub EDI ingère ORDERS, ERP réserve stock, WMS planifie expédition, transporteur reçoit ASN, facturation génère INVOIC, comptabilité réconcilie REMADV. Ces étapes ne peuvent pas tenir dans une transaction XA (cf 2PC) : la durée totale dépasse plusieurs heures voire jours, les ressources sont hétérogènes (Oracle, REST API, SAP, partenaires externes), et chaque participant a son propre cycle de panne. Quelle topologie d'exécution choisir pour qu'en cas d'échec à l'étape 7, les étapes 1-6 puissent compenser ?
Forces
- Visibilité du flux : l'équipe ops doit pouvoir répondre à « où en est la commande 12345 ? » en quelques secondes.
- Couplage : la chorégraphie évite un point de connaissance unique mais augmente le couplage temporel par événements ; l'orchestration centralise mais crée une dépendance forte au coordinateur.
- Évolutivité : ajouter une étape doit être local (changer un service) plutôt qu'invasif (modifier le BPMN coordinator).
- Tolérance aux pannes : chaque étape doit reprendre à partir du point d'arrêt sans rejouer les étapes déjà faites.
- Compensation : chaque étape doit savoir comment annuler son effet métier (Garcia-Molina parlait de compensating transactions).
Solution
Chorégraphie : chaque service consomme des événements et en
publie d'autres, sans coordinateur central. Le hub EDI publie
OrderReceived, l'ERP réagit et publie StockReserved, le
WMS réagit et publie ShipmentScheduled, etc. Les compensations sont
déclenchées par leurs propres événements (OrderCancelled, StockReleased).
Simple à démarrer, devient chaotique au-delà de 6-7 services.
Orchestration : un service dédié (orchestrator) connaît le workflow complet, appelle chaque service via commande et reçoit la réponse, décide de l'étape suivante ou de la compensation. Implémentations modernes : Temporal, Camunda Zeebe, AWS Step Functions, Azure Durable Functions. Le state du workflow est persisté et survit aux pannes.
Structure
CHORÉGRAPHIE (pub/sub décentralisé)
EDI Hub ──OrderReceived──► [Kafka topic]
│
├──► ERP (réserve) ──StockReserved──► [Kafka]
│ │
├──► CRM (notif) │
│ │
└──► WMS (planifie) ◄──────────────────┘
│
└──ShipmentScheduled──► ...
ORCHESTRATION (centralisée)
┌──── Orchestrator ────┐
│ state: state-machine │
│ persistance: DB │
└──────────┬────────────┘
│
┌────────────────┼─────────────────┐
▼ ▼ ▼
call ERP call WMS call Carrier
reserve() schedule() book()
│ │ │
└─────► reply ◄──┴─► reply ◄───────┘
│
Next step? Compensate? Implémentation EDI
Sur un flux EDI typique, choisir selon la complexité. Saga courte (≤ 4 étapes), participants stables, équipes en silos : chorégraphie via Kafka + outbox pattern (cf Transactional Outbox). Code Temporal côté orchestration :
// Orchestration avec Temporal (TypeScript SDK)
export async function orderToInvoiceSaga(orderId: string): Promise<void> {
let stockReserved = false;
let shipmentBooked = false;
try {
await activities.reserveStock(orderId);
stockReserved = true;
await activities.bookShipment(orderId);
shipmentBooked = true;
// Wait up to 7 days for shipment confirmation
const shipped = await wf.condition(
() => shipmentSignaled === true,
'7d'
);
if (!shipped) throw new ShipmentTimeoutError(orderId);
await activities.issueInvoice(orderId);
await activities.sendInvoice(orderId);
} catch (err) {
// Compensations in reverse order
if (shipmentBooked) await activities.cancelShipment(orderId);
if (stockReserved) await activities.releaseStock(orderId);
await activities.notifyCustomer(orderId, 'CANCELLED');
throw err;
}
} Saga longue (≥ 6 étapes), workflow business-critical, exigences auditabilité fortes : orchestration explicite. Avantage : le workflow est lisible comme du code séquentiel, le state est inspecté en console (Temporal UI, Camunda Cockpit), et les compensations sont visibles. Inconvénient : dépendance forte au moteur (lock-in).
Anti-patterns
- Mélanger chorégraphie et orchestration dans le même bounded context — perd la visibilité de la chorégraphie ET la simplicité de l'orchestration.
- Reconstruire un orchestrator à la main en cron+jobs sans persistance d'état — réinvente Temporal mal.
- Pas de timeout par étape dans l'orchestrator — une étape bloquée bloque la saga indéfiniment.
- Compensations non-idempotentes — un crash partiel pendant la compensation crée du data corruption permanent.
- Saga « distributed monolith » en chorégraphie où chaque service écoute tous les événements pour décider — le couplage est pire qu'un orchestrator explicite.
Patterns liés
- Saga Choreography — la variante chorégraphique en détail.
- Saga Orchestration — la variante orchestrée en détail.
- Compensating Transactions — la brique technique commune aux deux.
- Process Manager — pattern EIP de référence pour l'orchestration.
- 2PC — l'alternative qu'on cherche à éviter en EDI.
Sources
- Garcia-Molina H., Salem K. — Sagas, ACM SIGMOD Conference 1987. Le papier fondateur du pattern. cs.cornell.edu
- Richardson C. — Microservices Patterns, Manning 2018, chap. 4 (« Managing transactions with sagas »). La discussion choreography/orchestration de référence.
- Newman S. — Building Microservices, 2e édition O'Reilly 2021, chap. 6 (« Workflow »).
- Temporal Documentation — Workflows and Sagas. docs.temporal.io
- Camunda — Saga Pattern Implementation with Zeebe. camunda.com