The Microservices architecture leads to clearly separated individual services, that are independently developed and deployed. Due to the nature of this distributed architecture, you will need to implement architectural Microservice patterns whenever you need to deal with distributed transactions or want to separate your read and write components. Here we will talk about SAGA and Outbox patterns for handling distributed transactions, and CQRS for handling separation of read and write stores.
Saga is used for long-lived distributed transactions across services. Each service has local ACID transactions on its local database. However, in some cases, you need to combine the multiple local ACID transactions from different services. For this, the first service should fire an event after completing the local ACID transaction. And the second service should read this event and start its own transaction. Thus, a distributed transaction basically requires a publish/subscribe mechanism to handle events across services.
Above you see an example case, where 3 services communicate with events in the choreography approach using Kafka as the event store. Here, multiple Saga steps complete a Saga flow that changes the order state from Pending to Paid and finally to Approved. First, payment service is called to set the order status as Paid, and then the restaurant service is called to complete an Order.
However, an important case in Saga is handling the rollback operation. For this, you need to create compensating transactions. If a Saga step fails, the previous step must undo its changes by applying the compensating transaction. For example, in above case, if the restaurant approval fails, the payment operation must be rollbacked using a compensating transaction.
While applying the Saga pattern, you will have two operations at each step. The local ACID transaction for business logic, and the event publishing.
These two operations cannot be in the same single unit of work as they target separate data sources. One is the local database, and the other is the event store. To perform these operations consistently, you can apply the Outbox pattern. The Outbox pattern relies on having a local outbox table to hold events in the same database where you run the local transactions for business logic. Then you can use these two database tables in the same transaction to perform local ACID transaction for business logic and event publishing. You can then read the events from the outbox table and publish them asynchronously.
CQRS stands for Command Query Responsibility Segregation. It allows to separate read and write operations which provides multiple benefits such as better performance on read and write sides using the correct technology and scaling read and write sides separately.
In CQRS, when the write operation is persisted, an event is stored in event-store. This event is used to update the read store asynchronously. Events can also be replayed multiple times according to requirements to create different types of query stores.
As the read store is updated asynchronously at a later time, it will result in an Eventual consistent system. Eventual consistency provides high availability and better performance by sacrificing the strong consistency. CQRS will also eliminate the requirement for distributed transactions if eventual consistency can be accepted.
In a distributed environment with multiple services, it is not easy to handle distributed transactions. SAGA and Outbox architectural patterns can help to perform long-lived distributed transactions in a consistent way. On the other hand, if eventual consistency is acceptable, CQRS pattern can be used to separate read and write components of an application, which allows scaling both sides separately and leads to better performance.
Source: Medium - Ali Gelenler
The Tech Platform