Architectural · Service Comms · DWG NO. 006

Orchestration

Put one coordinator in charge of a multi-step workflow, so the sequence and failure handling live in a single, readable place.

TypeArchitectural
ScopeMultiple services
ComplexityMedium–High
Common inOrder fulfillment, distributed sagas, CI pipelines

A business process, scattered across services, with no one owning the sequence

Placing an order might mean: reserve inventory, charge payment, schedule shipping. If each service calls the next one directly (choreography), the overall sequence isn't written down anywhere — you have to read every service to reconstruct the flow, and if step two fails, nothing coordinates undoing step one.

A conductor, not a chain

An orchestrator calls each step in order, waits for the result, and decides what happens next — including running compensating actions in reverse if a later step fails. The workflow's logic lives in one place instead of being implied by which service happens to call which.

Orchestrator 1. reserve 2. charge 3. ship Inventory Payments Shipping compensate on failure

A saga-style order workflow

Each step exposes a matching `undo`. If a step throws, the orchestrator walks backwards through the steps it already completed and compensates them in reverse order.

order-orchestrator.js
const inventory = {
  reserve: async (id) => console.log(`inventory: reserved ${id}`),
  release: async (id) => console.log(`inventory: released ${id}`),
};
const payments = {
  charge: async (id) => {
    console.log(`payments: charging order ${id}`);
    if (id === "bad-order") throw new Error("card declined");
  },
  refund: async (id) => console.log(`payments: refunded order ${id}`),
};
const shipping = {
  schedule: async (id) => console.log(`shipping: scheduled for ${id}`),
  cancel: async (id) => console.log(`shipping: cancelled for ${id}`),
};

async function placeOrder(orderId) {
  const completed = [];

  const steps = [
    { name: "inventory", run: () => inventory.reserve(orderId), undo: () => inventory.release(orderId) },
    { name: "payments", run: () => payments.charge(orderId), undo: () => payments.refund(orderId) },
    { name: "shipping", run: () => shipping.schedule(orderId), undo: () => shipping.cancel(orderId) },
  ];

  try {
    for (const step of steps) {
      await step.run();
      completed.push(step);
    }
    console.log(`order ${orderId}: completed`);
  } catch (err) {
    console.log(`order ${orderId}: failed at a step (${err.message}), compensating...`);
    for (const step of completed.reverse()) {
      await step.undo();
    }
  }
}

await placeOrder("order-123");
await placeOrder("bad-order");

Trade-offs