Connecting Handlers with Channels
Learn how to connect message handlers using channels to build workflows
Building business workflows is essential for most applications. Whether you need fully automated processes (like image processing pipelines) or human-interactive flows (like document approval), Ecotone makes it simple by connecting message handlers through channels.
Understanding the Flow: From Handler to Handler
The key concept in Ecotone is that every message handler can be connected to other handlers using channels. Think of channels as pipes that carry messages between different parts of your application.
Core Concept: Each message handler has an input channel (where messages come in) and can have an output channel (where results go out). By connecting these channels, you create workflows.
Step 1: Understanding Single Handlers
Let's start with a simple command handler:
#[CommandHandler('place.order')]
public function place(PlaceOrder $command): void
{
// Place the order logic here
}What happens behind the scenes:
Ecotone creates a channel named
place.orderYour handler listens to this channel
When you use the Command Bus, it sends messages to this channel

Step 2: Connecting Handlers Together
Here's where it gets powerful: any handler can send messages to another handler's channel. This lets you chain handlers together to create workflows.
Let's add order verification before placing the order:

The magic happens with outputChannelName:
How the flow works:
You send
PlaceOrdertoverify.orderchannelThe
verify()method processes it and returns the commandEcotone automatically sends the returned command to
place.orderchannelThe
place()method receives and processes it
Pro Tip: You can use the same command class for multiple handlers in a workflow. This eliminates the need to convert between different classes at each step, making your code simpler and easier to follow.
Making Handlers Internal (Private to Workflow)
Problem: With CommandHandler, anyone can call place.order directly through the Command Bus, bypassing your verification step!
Solution: Use InternalHandler to make handlers private to your workflow:
What this means:
✅
verify.ordercan be called via Command Bus (entry point)❌
place.ordercan only be reached through the workflow🔒 This ensures orders are always verified before being placed
Extending Workflows: You can easily add more steps by adding outputChannelName to any handler. To send messages to multiple handlers, use the Router pattern.
Adding Asynchronous Processing
Sometimes you want parts of your workflow to run asynchronously (in the background). This is perfect for:
Heavy processing that shouldn't block the user
Ensuring messages aren't lost if something goes wrong
Scaling parts of your workflow independently
Example: Keep verification synchronous (fast feedback) but make order placement asynchronous (reliable processing):
What happens now:
verify()runs immediately and returns a responseThe message goes to a queue/background processor
place()runs later in the backgroundIf
place()fails, the message can be retried

The Beauty of Ecotone: You've built a complete workflow with asynchronous processing without extending any framework classes or complex configuration. Everything is declared through simple attributes!
Adding Delays and Timeouts
Asynchronous handlers can also be delayed, which is perfect for business scenarios like:
Giving customers time to complete actions
Implementing timeout behaviors
Scheduling follow-up actions
Example: Give customers 24 hours to pay, then automatically cancel unpaid orders:

Strategy: Use events to trigger delayed actions (events allow multiple handlers to react):
Now add the delayed cancellation handler:
Timeline:
⏰ T+0: Order placed, event published
⏰ T+24h: Cancellation handler runs automatically
Controlling Workflow Flow
Stopping the Workflow
You can stop a workflow from continuing by returning null:
Enriching Messages in Workflows
Sometimes you need to add information as messages flow through your workflow. There are two approaches:
Option 1: Transform the Payload
Return a new/modified object that contains additional data:
When to use: When the additional data is core to the next step's logic.
Option 2: Add Data to Message Headers
Keep the original payload unchanged and add extra data as headers:
When to use: When you want to keep the original payload intact and add supplementary data.
Testing Your Workflows with Ecotone Lite
Testing workflows is crucial for ensuring your business logic works correctly. Ecotone Lite makes testing handler chains simple and straightforward.
Setting Up Tests
Testing Handler Chains
Test complete workflows from start to finish:
Testing Asynchronous Workflows
Test async workflows in synchronous mode for easier testing:
Summary: What You've Learned
You now understand the fundamentals of connecting handlers with channels in Ecotone:
Key Concepts
Channels: Every handler has an input channel, and can send to output channels
Connection: Use
outputChannelNameto chain handlers togetherPrivacy: Use
InternalHandlerto make handlers private to workflowsAsync Processing: Add
#[Asynchronous]for background processingDelays: Use
#[Delayed]for time-based workflowsFlow Control: Return
nullto stop workflowsData Enrichment: Transform payloads or add headers
Last updated
Was this helpful?