How do you handle business processes and workflows in an asynchronous event-driven architecture? You can use event choreography and orchestration with compensating actions to overcome the lack of distributed transactions or two-phase commits in a distributed system. Here’s how Event Choreography & Orchestration work and examples using NServiceBus.
Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.
Choreography is driven by events from the various services within a system. Each service consumes events, performs an action, and publishes events. There is no centralized coordination or logic.
Because there is no centralized coordination, it can be difficult to conceptualize the actual workflow.
Here’s an example of the workflow for an order being placed.
The first step is the Sales service publishes OrderPlaced to the message broker.
Next he Billing service consumes the OrderPlaced and bills the customer.
Billing service then publishes a OrderBilled event to the message broker
Warehouse service consumes the OrderBilled event and creates a shipping label.
Warehouse publishes a ShippingLabelCreated event to the message broker
Sales consumes ShippingLabelCreated to update the order status to ReadyToShip
As you can see from the event flow above, there’s no centralized cooridnator or logic. Events are published to a message broker that other services consume to create the business process.
Orchestration is a way to centralize the workflow of logic for a business process. It coordinates the workflow by sending commands to the appropriate service, consuming the resulting events. In contrast to choreography, Orchestration tells other services what action/command to perform rather than those services being reactive to other events in the system.
Just like choreography, orchestration is started by the OrderPlaced event in the Sales service
The Orchestrator will then send a BillOrder command to which is consumed by the Billing service.
The Billing service then publishes a OrderBilled event which the orchestrator consumes.
Orchestrator upon consuming the OrderBilled event sends a CreateShippingLabel for the Warehouse service to consume.
The Warehouse service then publishes a ShippingLabelCreated event that the Orchestrator consumes to complete the saga.
Orchestrators also store state to know which steps of the workflow have occurred. Because of this, if there is a failure in the workflow, it can perform compensating actions to recover from a failure. In the example above, a compensating action could be to refund the order if the warehouse fails to create the shipping label.
Let’s say the warehouse could not create the shipping label because it has business logic to check for quantities on hand. If there isn’t available quantity, it Warehouse service publishes a BackOrdered event.
The Orchestrator would then send a RefundOrder command which would be consumed by Billing to refund the customer.
I’ve created a sample using NServiceBus Sagas. The sample does not require any additional infrastructure. You can get the source over on my GitHub repo.
You can use both approaches where it makes the most sense.
Benefits & drawbacks of Choreography
- No centralized logic: this can be good and bad
- Useful for small/simple workflows
- Difficult to conceptualize if a lot of services are involved.
- Hader to debug & test if a lot of services are involved
Benefits & drawbacks of Orchestration
- Centralized logic: this can be good and bad
- Easier to understand the workflow since its defined in a central location
- Full control over the workflow steps via commands
- Point of failure
- Easier to debug and test