Distributed microservices often face the challenge of maintaining data consistency without relying on heavy distributed transactions. The SAGA design pattern is a proven solution: it breaks a long transaction into smaller steps, each with a compensating action if something fails.
While Camunda traditionally relies on BPMN XML files, developers can simplify configuration by using YAML-based workflow definitions. YAML is easier to read and maintain, while still being fully compatible with Camunda’s orchestration engine.
In this blog, we’ll see how to implement a real-world e-commerce order processing saga in Camunda using YAML workflows, complete with compensating transactions.
Why Use Camunda for SAGA Orchestration?
Centralized Orchestration – Camunda acts as a Saga orchestrator across multiple microservices.
Compensation Handling – Built-in support for rollback actions.
Asynchronous Messaging – Works seamlessly with REST, Kafka, and RabbitMQ.
Monitoring – Camunda Cockpit provides real-time tracking of Saga states.
Retries & Timeouts – Configurable per task to ensure robustness.
Real-World Example: E-Commerce Order Saga
Our order processing workflow:
1. Reserve Inventory
2. Process Payment
3. Arrange Shipment
If any step fails, compensating actions are triggered:
Cancel Inventory Reservation
Refund Payment
Cancel Shipment
YAML Workflow Definition for the Saga
Instead of writing verbose XML, we define our process in YAML:
process:
id: orderSaga
isExecutable: true
tasks:
- id: ReserveInventory
type: service
delegate: reserveInventoryDelegate
error: INVENTORY_FAILED
compensation: CancelInventory
- id: ProcessPayment
type: service
delegate: processPaymentDelegate
error: PAYMENT_FAILED
compensation: RefundPayment
- id: ArrangeShipment
type: service
delegate: arrangeShipmentDelegate
error: SHIPMENT_FAILED
compensation: CancelShipment
This YAML is automatically converted into BPMN XML under the hood during deployment.
YAML-to-BPMN Conversion Setup
To use YAML instead of XML in Camunda, you can leverage community plugins and converters:
1. Camunda Modeler YAML Plugin
Allows defining workflows in YAML and converts them to BPMN.
GitHub Project: camunda-yaml-model
2. Maven Plugin Integration
Configure Maven to run YAML → BPMN conversion before packaging your app.
<plugin>
<groupId>io.holunda</groupId>
<artifactId>camunda-bpm-yaml-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>convert</goal>
</goals>
</execution>
</executions>
</plugin>
3. Deployment
Place YAML files under src/main/resources/bpmn/.
During build, they’ll be converted to .bpmn XML and deployed to Camunda automatically.
This way, you write YAML, but Camunda still consumes standard BPMN XML behind the scenes.
Java Delegates for Local Transactions
Each step is implemented as a delegate with compensating logic:
@Component("reserveInventoryDelegate")
public class ReserveInventoryDelegate implements JavaDelegate {
@Autowired private InventoryService inventoryService;
@Override
public void execute(DelegateExecution execution) {
String orderId = execution.getBusinessKey();
if (!inventoryService.reserve(orderId)) {
throw new BpmnError("INVENTORY_FAILED");
}
}
}
@Component("cancelInventoryDelegate")
public class CancelInventoryDelegate implements JavaDelegate {
@Autowired private InventoryService inventoryService;
@Override
public void execute(DelegateExecution execution) {
inventoryService.cancelReservation(execution.getBusinessKey());
}
}
Similarly, implement Payment and Shipment delegates with compensating actions.
Starting the Saga from a REST Controller
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired private RuntimeService runtimeService;
@PostMapping("/place")
public String placeOrder(@RequestParam String orderId) {
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"orderSaga", orderId
);
return "Order saga started with ID: " + instance.getProcessInstanceId();
}
}
Best Practices When Using Camunda with YAML
1. Match Error Codes Correctly
The error in YAML must exactly match the BpmnError thrown in code.
2. Idempotent Compensations
Ensure cancel and refund actions can run multiple times safely.
3. Use Business Keys
Always pass unique identifiers (like orderId) to correlate saga instances.
4. Configure Retries & Timeouts
Define retry cycles for transient errors (e.g., network outages).
5. Monitor in Camunda Cockpit
Use Cockpit to check active/failed sagas and compensation history.
6. Process Versioning
Deploying new YAML creates new BPMN versions—always test compensations in staging.
Conclusion
By using Camunda with YAML configurations, you simplify the way you define and manage complex distributed workflows. In our e-commerce order saga example, YAML made the orchestration much easier to maintain while ensuring data consistency across microservices.
With the right practices—error handling, retries, idempotency, and monitoring—you can confidently implement the SAGA pattern in production-ready systems using Camunda.

0 Comments