Business Workflows
Three shapes of durable workflows in PHP — Stateless Workflows, Sagas, and Orchestrators. Pick the one that matches your process, on the database and broker you already run.
Building durable workflows in PHP? Start with Durable Execution in PHP — the three workflow shapes Ecotone supports, the trade-offs between them, and the code side-by-side.
Three Shapes of Durable Workflows
Every multi-step business process has one of three shapes. Pick the one that matches yours — all three are durable, all three survive crashes and deploys, all three run on the database and broker you already operate.
Stateless Workflow
Linear pipeline; the message carries everything between steps
Channel + outbox redelivery; idempotent handlers
The message in flight
Saga
State must persist across events arriving over time (hours, days, weeks)
Event-keyed persistence (DB), optional event sourcing for full replay
Your DB, rehydrated per #[Identifier] on the next event arrival
Orchestrator (Enterprise)
The step list itself is the workflow — possibly dynamic per input
Routing slip on the channel; per-step channel redelivery
The slip header on the message
Stateless Workflows — when the message is the state
Use when steps are tightly linear (verify → charge → ship → notify), nothing needs to be remembered between steps, and the message itself carries the state from one handler to the next. Handlers are chained through outputChannelName; durability comes from the channel (outbox + redelivery), not from a persisted record.
#[CommandHandler(routingKey: 'order.place', outputChannelName: 'order.verify_payment')]
public function placeOrder(PlaceOrder $command): OrderData { /* ... */ }
#[Asynchronous('async')]
#[InternalHandler(inputChannelName: 'order.verify_payment', outputChannelName: 'order.ship')]
public function verifyPayment(OrderData $order): OrderData { /* ... */ }
#[Asynchronous('async')]
#[InternalHandler(inputChannelName: 'order.ship', outputChannelName: 'order.notify')]
public function ship(OrderData $order): OrderData { /* ... */ }→ Deep dive: Connecting Handlers with Channels
Sagas — when state must persist between events
Use when the process spans events arriving over time and you need to remember what already happened: payment received → wait → ship → notify, with hours or days between steps. A #[Saga] is a plain PHP class with state, an #[Identifier], and event handlers. State is persisted per identifier; on the next event arrival, Ecotone rehydrates the saga from your database.
For full replay semantics — every state transition recorded as an event in your own database, the same durability model Temporal uses internally — use #[EventSourcingSaga] instead. See Durable Execution in PHP for the side-by-side with Temporal.
→ Deep dive: Sagas
Orchestrators — when the step list is the workflow (Enterprise)
Use when the workflow definition belongs in one place that a business stakeholder can read, when steps need to be reusable across multiple workflows, or when the step list is chosen dynamically from input (digital vs physical fulfillment, premium vs standard). The #[Orchestrator] method returns the channel list; each step is an independently testable #[InternalHandler].
→ Deep dive: Orchestrators
How to Choose
Does any state need to survive between message arrivals — does the next step depend on something that happened hours or days earlier? → Saga. (Event-sourced if you want full replay and projections over the workflow's history.)
Is the step list itself the value — written in one place a stakeholder reads, or chosen dynamically per input? → Orchestrator.
Linear pipeline where each step's output is the next step's input, and the message carries everything? → Stateless Workflow.
The three are not exclusive — a Saga can dispatch into a Stateless Workflow, an Orchestrator step can publish events that drive a Saga. Pick the primary shape of the process; compose the rest around it.
Materials
Demo implementation
Links
Durable Execution in PHP — when "durable workflows" or Temporal comes up
Last updated
Was this helpful?