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

A saga with no conductor — each service reacts to domain events and emits its own. A fit for EDI cycles where partners are autonomous, where Saga Orchestration would impose a centralised coordinator.

Problem

An EDI cycle ORDERS → ORDRSP → DESADV → INVOIC → REMADV spans several internal services (Procurement, Logistics, Billing, Treasury) that are autonomous and already decoupled by messaging. Imposing a centralised orchestrator adds a coupling point and a bottleneck: every step must signal completion to the orchestrator, which must decide the next step. Why not let each service react directly to the previous event?

Forces

  • No central service. The saga lives in event interactions; no team owns the full workflow.
  • Loose coupling. Each service only knows the events it consumes and emits, not the overall sequence.
  • But the workflow hides itself. Understanding the end-to-end sequence requires reading the code of every service.
  • Cycles are possible. If A reacts to B, B reacts to C and C reacts to A, an undetected loop can form.

Solution

Each domain service subscribes to relevant events and emits its own in response. Compensations are triggered by inverse events (OrderCancelled, ShipmentRecalled) that each service consumes if it carried out the matching effect. No service owns the full sequence; it emerges from composition.

plaintext saga-choreography-edi.txt
[Buyer service]    receives ORDERS from a partner

    ├─emit─► OrderAccepted(orderId=ORD789) ─► topic: orders

[Supplier service] consumes OrderAccepted
    ├─emit─► ORDRSP sent to EDI partner ─► topic: order-responses

[Logistics service] consumes OrderAccepted, prepares shipment
    ├─emit─► ShipmentDispatched(asn=ASN9001) ─► topic: shipments
    ├─send─► DESADV to partner

[Billing service]  consumes ShipmentDispatched (+ time trigger)
    ├─emit─► InvoiceIssued(invoice=INV-7811) ─► topic: invoices
    ├─send─► INVOIC to partner

[Treasury service] consumes InvoiceIssued + ReceiptConfirmed
    ├─emit─► PaymentRemitted(amount=1240.00) ─► topic: payments
    └─send─► REMADV to partner

Compensation = each service exposes a handler for the inverse events
(OrderCancelled, ShipmentRecalled, InvoiceCredited).

EDI implementation

Concrete EDI case: the orchestration of a PO ↔ ASN ↔ INVOIC ↔ REMADV cycle. The Buyer service receives a partner ORDERS, validates it, publishes an OrderAccepted event on the Kafka topic orders. The Supplier service consumes, emits an ORDRSP and publishes OrderConfirmed. The Logistics service consumes OrderAccepted, prepares the shipment and publishes ShipmentDispatched — it also emits the DESADV to the EDI partner. The Billing service consumes ShipmentDispatched and emits the INVOIC. The Treasury service waits for ReceiptConfirmed from the RECADV reception, triggers payment and emits the REMADV. No component owns the full workflow; it lives in the Kafka subscription graph.

Choreography vs Orchestration

The two Saga variants (Choreography, Orchestration) are complementary:

  • Choreography fits 3-5 step workflows that are stable and well understood, in mature organisations carved into bounded contexts.
  • Orchestration fits complex, changing workflows, or ones with fine conditional branches, where workflow readability trumps decoupling.
  • Often both coexist: choreography between domains, orchestration inside a domain.

Anti-patterns

  • Workflow hidden across 10 services. Without an event map, no one understands the cycle end-to-end after 18 months.
  • Undetected loop. Service A produces X, B reacts by producing Y which causes A to produce X — the chain explodes.
  • Partial compensations. A service forgets to consume OrderCancelled: the order remains cancelled in 4 services out of 5, leaving state inconsistent.
  • No transverse TraceId. Without a correlation identifier propagated in every event, incident investigation becomes a treasure hunt.

Sources