Sagas: Workflows That Remember
Learn how to build long-running workflows that remember state using Sagas
While connecting handlers with channels works great for simple workflows, some business processes need to remember what happened before. This is where Sagas come in.
When Do You Need Sagas?
Use Sagas when your workflow needs to:
π³ Branch and merge: Split into multiple paths that later combine β³ Wait for humans: Pause for manual approval or external actions π Track progress: Monitor long-running processes (hours, days, weeks) π Handle complexity: Make decisions based on accumulated state
Examples:
Order processing (payment β shipping β delivery)
Loan approval (application β verification β decision)
User onboarding (signup β verification β welcome)
Think of Sagas as: A workflow coordinator that remembers what happened and decides what to do next based on that history.
Creating Your First Saga
A Saga is like a persistent coordinator that remembers its state between events. Let's build an order processing saga step by step.
Step 1: Define the Saga Structure
#[Saga]
final class OrderProcess
{
private function __construct(
#[Identifier] private string $orderId, // π Unique ID to find this saga
private OrderStatus $status, // π Current state
private bool $isPaid = false, // π Remember payment status
private bool $isShipped = false, // π Remember shipping status
) {}
}Key parts:
#[Saga]- Tells Ecotone this is a stateful workflow#[Identifier]- Unique ID to find and update this specific saga instancePrivate properties - The state that gets remembered between events
Step 2: Start the Saga
Sagas begin when something important happens (usually an event):
What happens:
OrderWasPlacedevent occursEcotone creates a new
OrderProcesssaga instanceThe saga is saved to storage (database, etc.)
Now it can react to future events for this order
Storage
Sagas are automatically stored using Repositories. You can use Doctrine ORM, Eloquent, or Ecotone's Document Store - no extra configuration needed!
Saga vs Aggregate: Both can handle events and commands, but use Sagas for business processes (workflows) and Aggregates for business rules (data consistency).
Step 3: React to Events and Take Actions
Now let's make the saga react to events and coordinate the workflow:
Triggering Commands
When payment succeeds, we want to start shipping:
The flow:
PaymentWasSuccessfulevent arrivesSaga updates its internal state
Saga returns
ShipOrdercommandCommand goes to
shipOrderchannelShipping handler processes the order
Alternative: Using Command Bus
You can also send commands directly:
Step 4: Publishing Events and Timeouts
Sagas can also publish events to trigger other parts of your system or set up timeouts.
Publishing Events
Setting Up Timeouts
Cancel orders that aren't paid within 24 hours:
Timeline:
β° T+0: Order placed, saga starts, timeout scheduled
β° T+24h: If still unpaid, automatically cancel
Advanced Patterns
Conditional Event Handling
Sometimes you only want to handle events if the saga already exists. Use dropMessageOnNotFound:
How it works:
β If saga exists: Event is processed
β If saga doesn't exist: Event is ignored (dropped)
π― Use case: Features that depend on previous conditions
Querying Saga State
Expose saga state to your application (great for status pages, dashboards):
Using in your controllers:
Perfect for:
Order tracking pages
Progress indicators
Admin dashboards
Customer support tools
Handling Unordered Events
Real-world events don't always arrive in order. Ecotone handles this elegantly with method redirection:
How Ecotone decides:
π― Same event, different behavior based on saga state
π Saga doesn't exist β Calls static factory method
β Saga exists β Calls action method
Benefits:
No complex if/else logic in your code
Handles event ordering issues automatically
Clean separation of initialization vs. processing logic
Pro tip: This pattern is perfect for handling duplicate events or events that can arrive at different workflow stages.
Saga Identification and Correlation
Finding the Right Saga Instance
Every event/command needs to find the correct saga instance. Ecotone uses Identifier Mapping for this:
Using Correlation IDs
For complex workflows that branch and merge, use correlation IDs:
Benefits of correlation IDs:
Track workflows across multiple services
Handle branching and merging flows
Automatic propagation through message chains
Testing Sagas with Ecotone Lite
Testing sagas is essential for ensuring your stateful workflows behave correctly. Ecotone Lite makes saga testing straightforward and comprehensive.
Setting Up Saga Tests
Testing Saga State Changes
Test how sagas respond to events and update their state:
Summary: When to Use Sagas
β Use Sagas when you need to:
Remember state between events
Coordinate long-running processes
Handle branching/merging workflows
Implement timeouts and cancellations
Track progress of complex operations
β Don't use Sagas for:
Simple linear workflows (use handler chaining)
Stateless transformations
Key insight: Sagas are workflow coordinators that remember what happened and decide what to do next. They're perfect for orchestrating complex business processes that span across time and multiple services.
Last updated
Was this helpful?