ediverse Explore the platform

Spotlight PEPPOL BIS Billing 3.0 The EU e-invoicing mandate is here — France Sept 2026, Belgium Jan 2026, Germany 2025.

Saga: choreography vs orchestration

Two topologies to execute a long-running multi-service saga. Choreography distributes control through the event network; orchestration concentrates it in an explicit coordinator. The choice drives 80% of an EDI flow's observability.

Problem

A typical B2B order crosses 5-10 systems before being invoiced: EDI hub ingests ORDERS, ERP reserves stock, WMS schedules shipment, carrier receives ASN, invoicing generates INVOIC, accounting reconciles REMADV. These steps do not fit in an XA transaction (see 2PC): total duration spans hours or days, resources are heterogeneous (Oracle, REST APIs, SAP, external partners), each participant has its own failure cycle. Which execution topology to pick so that, if step 7 fails, steps 1-6 can compensate?

Forces

  • Flow visibility: ops must answer "where is order 12345?" in seconds.
  • Coupling: choreography avoids a single knowledge point but increases temporal coupling through events; orchestration centralises but creates a hard dependency on the coordinator.
  • Evolvability: adding a step must be local (change a service) rather than invasive (modify the BPMN coordinator).
  • Fault tolerance: each step must resume from the failure point without replaying already done steps.
  • Compensation: each step must know how to undo its business effect (Garcia-Molina called these compensating transactions).

Solution

Choreography: each service consumes events and publishes others, without a central coordinator. The EDI hub publishes OrderReceived, the ERP reacts and publishes StockReserved, the WMS reacts and publishes ShipmentScheduled, etc. Compensations are triggered by their own events (OrderCancelled, StockReleased). Easy to start, becomes chaotic beyond 6-7 services.

Orchestration: a dedicated service (orchestrator) knows the full workflow, calls each service through commands and receives replies, decides next step or compensation. Modern implementations: Temporal, Camunda Zeebe, AWS Step Functions, Azure Durable Functions. Workflow state is persisted and survives crashes.

Structure

CHOREOGRAPHY (decentralised pub/sub)

EDI Hub ──OrderReceived──► [Kafka topic]
                                │
                                ├──► ERP (reserves) ──StockReserved──► [Kafka]
                                │                                        │
                                ├──► CRM (notif)                         │
                                │                                        │
                                └──► WMS (schedules) ◄───────────────────┘
                                              │
                                              └──ShipmentScheduled──► ...

ORCHESTRATION (centralised)

                  ┌──── Orchestrator ────┐
                  │  state: state-machine │
                  │  persistence: DB      │
                  └──────────┬────────────┘
                             │
            ┌────────────────┼─────────────────┐
            ▼                ▼                 ▼
       call ERP         call WMS         call Carrier
       reserve()        schedule()       book()
            │                │                 │
            └─────► reply ◄──┴─► reply ◄───────┘
                             │
                  Next step? Compensate?

EDI implementation

On a typical EDI flow, pick by complexity. Short saga (≤4 steps), stable participants, siloed teams: choreography via Kafka + outbox pattern (see Transactional Outbox). Orchestration code with Temporal:

// Orchestration with 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;
  }
}

Long saga (≥6 steps), business-critical workflow, strong auditability requirements: explicit orchestration. Pro: the workflow reads like sequential code, state is inspectable in a console (Temporal UI, Camunda Cockpit), compensations are visible. Con: strong dependency on the engine (lock-in).

Anti-patterns

  • Mixing choreography and orchestration in the same bounded context — you lose choreography's visibility AND orchestration's simplicity.
  • Hand-rolling an orchestrator in cron+jobs without state persistence — reinventing Temporal badly.
  • No timeout per step in the orchestrator — a blocked step blocks the saga indefinitely.
  • Non-idempotent compensations — partial crash during compensation creates permanent data corruption.
  • "Distributed monolith" saga in choreography where each service listens to all events to decide — coupling is worse than an explicit orchestrator.

Sources

  • Garcia-Molina H., Salem K. — Sagas, ACM SIGMOD Conference 1987. The founding paper. cs.cornell.edu
  • Richardson C. — Microservices Patterns, Manning 2018, ch. 4 ("Managing transactions with sagas"). The reference choreography/orchestration discussion.
  • Newman S. — Building Microservices, 2nd ed. O'Reilly 2021, ch. 6 ("Workflow").
  • Temporal Documentation — Workflows and Sagas. docs.temporal.io
  • Camunda — Saga Pattern Implementation with Zeebe. camunda.com