I talked about microservices at two conferences last week. In both opportunities, the attendees asked me about how to implement the Saga pattern. So, I decided to blog about it.
What is a Saga?
From the microservices.io:
A saga is a sequence of local transactions. Each local transaction updates the state (database) and publishes a message or event to trigger the next local transaction in the saga. If a local transaction fails because it violates a business rule then the saga executes a series of compensating transactions that undo the changes that were made by the preceding local transactions.
The original idea is pretty old and comes from this article.
The implementation I recommend is known as “Orchestration-based saga.”
Saga as a Flow
I like to think Sagas as a long-running flow. Using BPMN (Business Process Modeling Notation), we could represent the preceding Saga as follows:
The good thing about representing Sagas as workflows is that we can run it using a workflow engine. So the workflow engine can act as the Saga Execution Coordinator.
What workflow engine I should/could use?
Like everything, the correct answer is: “It depends.” There are several options you could use. Some options are potent (and expensive). Also, there are a lot of lightweight and cheap alternatives. The right choice depends on your project.
I have been working (a lot) in a lightweight workflow engine library that you could use in your projects to solve a lot of problems (including “how to implement Sagas,” of course). It is still under development, but it will be ready to use soon.
Here is a preview of how you would configure your saga using my library:
var model = ProcessModel.Create() .AddEventCatcher<CreateNewOrderCommand>("start") .AddActivity<CreateOrderActivity>("CreateOrder") .AddActivity<CancelOrderActitivity>("CancelOrder") .AttachAsCompensationActivity("CancelOrder", "CreateOrder") .AddActivity<CreateOrderActivity>("ReserveProducts") .AddActivity<CancelOrderAcitivity>("CancelReservation") .AttachAsCompensationActivity("ReserveProducts", "CancelReservation") .AddActivity<RequestPaymentActivity>("RequestPayment") .AddActivity<CancelPaymentActivity>("CancelPayment") .AttachAsCompensationActivity("RequestPayment", "CancelPayment") .AddActivity<RequestDeliveryActivity>("RequestDelivery") .AddEventThrower("end") .AddSequenceFlow("start", "CreateOrder", "ReserveProducts", "RequestPayment", "RequestDelivery", "end" ); var models = new InMemoryProcessModelsStore(model); var instances = new InMemoryProcessInstancesStore(); var manager = new ProcessManager(models, instances);
Nice, huh?
Let me know if you are implementing microservices and need help. It would be a pleasure to help your company. Also, I would love to help you to start using my library (again, it is under development and source code is on Github).