Implementing Complex Distributed (Business) Transactions with Saga Pattern (and workflows)

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).

Compartilhe este insight:

Elemar Júnior

Sou fundador e CEO da EximiaCo e atuo como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Elemar Júnior

Sou fundador e CEO da EximiaCo e atuo como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Mais insights para o seu negócio

Veja mais alguns estudos e reflexões que podem gerar alguns insights para o seu negócio:

Este post é uma releitura de um que havia escrito, em 2016, e que se perdeu no “reboot” do blog....
Neste post, vou compartilhar como dar os primeiros passos com OpenCV, rapidamente, usando Visual Studio 2017 e VcPkg. O que...
Software em funcionamento é mais relevante que documentação abrangente. Concordo com esse princípio expresso no manifesto ágil. Entretanto, acho que...
Se há algo que aprendi na “escola da vida” é que sempre que temos algo importante para decidir, se não...
I have no idea of how many times I had to write a function do discover the minimum and the...
Sometimes it is not enough to know if two strings are equals or not. Instead, we need to get an...