O Padrão Saga: Transações Distribuídas na Arquitetura de Microsserviços
Em aplicativos monolíticos tradicionais, manter a consistência dos dados em diversas entidades é simples. Os mecanismos de banco de dados relacional fornecem garantias ACID (Atomicidade, Consistência, Isolamento, Durabilidade) agrupadas em transações SQL locais. Se uma colocação de pedido, dedução de pagamento ou reserva de estoque falhar no meio, chamar ROLLBACK reverte todas as modificações do banco de dados instantaneamente.
No entanto, ao migrar para uma Arquitetura de microsserviços moderna, o gerenciamento de dados muda fundamentalmente. Para garantir autonomia de domínio e escalabilidade independente, cada microsserviço possui seu banco de dados privado. Uma única operação de negócios – como o processamento de uma finalização de compra de comércio eletrônico – agora abrange vários limites de serviço e mecanismos de banco de dados (por exemplo, PostgreSQL para pedidos, DynamoDB para pagamentos, Redis para inventário).
Como os microsserviços distribuídos não podem depender de uma única transação de banco de dados, manter a consistência dos dados entre os limites da rede torna-se um dos problemas mais desafiadores na engenharia de sistemas distribuídos.
Para resolver isso sem sacrificar a disponibilidade ou o desempenho do sistema, os arquitetos de software contam com o Saga Pattern.
Neste mergulho profundo, exploraremos por que as transações distribuídas tradicionais falham, detalharemos a mecânica central do Padrão Saga, compararemos Coreografia vs. Orquestração, analisaremos contramedidas de isolamento, examinaremos implementações de código de produção em Go e Java e aprenderemos como lidar com reversões de falhas do mundo real com segurança.
A raiz do problema: por que o Two-Phase Commit (2PC) falha em microsserviços
Antes de adotar o Padrão Saga, os engenheiros costumam perguntar: Por que não podemos usar o tradicional Two-Phase Commit (2PC/XA) em nossos microsserviços?
A Mecânica do 2PC
O Two-Phase Commit coordena uma transação distribuída em vários nós de banco de dados usando um Transaction Manager central em duas fases:
- Fase de preparação: O Transaction Manager solicita que todos os nós do banco de dados participantes preparem e bloqueiem as linhas necessárias. Os participantes votam em
YESouNO. - Fase de Commit: Se todos os participantes votaram em
YES, o Manager emite um comandoCOMMITpara todos os nós. Se algum nó votouNOou expirou, ele emite umROLLBACK.
Client ----> Transaction Manager
|
+-------------+-------------+
| (Prepare) | (Prepare) | (Prepare)
v v v
Order DB Payment DB Inventory DB
Por que 2PC é um antipadrão para microsserviços
Embora o 2PC garanta forte consistência, ele falha em ambientes de microsserviços nativos da nuvem devido a diversas falhas arquitetônicas:
- Bloqueio e contenção de recursos: as linhas do banco de dados permanecem bloqueadas durante o handshake de rede em vários estágios. Se a latência da rede aumentar ou um serviço ficar lento, os bloqueios serão mantidos abertos, consumindo rapidamente os pools de conexões e causando falha em cascata do sistema.
- Gargalo de disponibilidade: Em 2PC, a disponibilidade do sistema é limitada pelo produto de todas as disponibilidades dos participantes ($A_{total} = A_1 \times A_2 \times \dots \times A_n$). Se qualquer nó de serviço ou banco de dados ficar offline durante a fase de preparação, toda a transação global será bloqueada indefinidamente.
- Perda de autonomia de serviço: 2PC força os serviços a expor protocolos XA em nível de banco de dados através de APIs de rede, acoplando seus mecanismos de banco de dados diretamente.
- Restrições do Broker: Brokers de mensagens de alto rendimento, como Apache Kafka ou RabbitMQ, não participam nativamente de transações XA 2PC tradicionais em bancos de dados relacionais.
De acordo com o Teorema CAP, os sistemas distribuídos devem escolher entre Consistência Forte (C) e Alta Disponibilidade (A) em Partições de Rede (P). Os sistemas modernos de microsserviços priorizam Disponibilidade e Tolerância de Partição, trocando consistência instantânea por Consistência Eventual (BASE: Basicamente Disponível, Estado Suave, Consistência Eventual).
Qual é o Padrão Saga?
O Padrão Saga foi originalmente proposto por Hector Garcia-Molina e Kenneth Salem em 1987 como um mecanismo para lidar com transações de longa duração em sistemas de gerenciamento de banco de dados. Nos microsserviços modernos, uma Saga representa uma sequência de transações locais discretas.
Em vez de agrupar todo o fluxo multisserviço dentro de um único bloqueio global, um Saga executa um processo de negócios como uma série de transações de banco de dados locais independentes ($T_1, T_2, \dots, T_n$).
Cada transação local atualiza dados no banco de dados de um único microsserviço e publica um evento ou mensagem de domínio. Este evento aciona a próxima transação local ($T_{i+1}$) no serviço downstream.
[Order Service] [Payment Service] [Inventory Service]
Local Tx T1 -------------> Local Tx T2 -------------> Local Tx T3
(Create Order) (Process Payment) (Reserve Stock)
Execução Adiante vs. Compensação Atrasada
Se todas as transações locais forem bem-sucedidas, a Saga será concluída com êxito (Execução Avançada).
No entanto, se uma transação local falhar no meio (por exemplo, $T_3$ falhar porque um item está fora de estoque), a Saga não pode simplesmente chamar um banco de dados ROLLBACK para etapas anteriores ($T_1, T_2$) porque essas transações locais já foram confirmadas em seus respectivos bancos de dados.
Para desfazer transações locais já confirmadas, o Saga deve executar Transações Compensadoras ($C_{n-1}, \dots, C_1$) na ordem inversa.
Forward Flow: T1 (Create Order) ---> T2 (Charge Card) ---> T3 (Reserve Inventory - FAILS)
|
Backward Rollback: C1 (Cancel Order) <--- C2 (Refund Card) <----------+
Classificação das transações da Saga
Para projetar um fluxo de trabalho Saga robusto, cada etapa da sequência da transação deve ser categorizada em um dos três tipos estruturais:
| Tipo de transação | Descrição | Requisito de idempotência e reversão |
|---|---|---|
| Transações Compensáveis | Etapas executadas antes do ponto sem retorno. Eles podem ser desfeitos ou revertidos se uma etapa posterior falhar. | Deve ter uma Transação de Compensação correspondente ($C_i$). |
| Transação dinâmica | A etapa definitiva da Saga. Se o Pivot for bem-sucedido, a Saga terá o fim garantido. Se falhar, a Saga é revertida. | Nem compensável nem recuperável; marca o limite entre a reversão e a conclusão do avanço. |
| Transações recuperáveis | Etapas executadas após a transação Pivot. Eles têm garantia de sucesso eventualmente e não exigem compensação. | Devem ser estritamente idempotentes, pois serão repetidas automaticamente até serem bem-sucedidas. |
Análise de exemplo de check-out de comércio eletrônico
Considere uma finalização de pedido de comércio eletrônico que consiste em quatro etapas:
- $T_1$: Criar pedido pendente (Compensável) $\to$ Desfazer via $C_1$: Cancelar pedido.
- $T_2$: Autorizar pagamento (Compensável) $\to$ Desfazer via $C_2$: Reembolsar pagamento.
- $T_3$: Reserva de estoque (Transação dinâmica) $\to$ Se a alocação de estoque for bem-sucedida, o pedido será finalizado. Se falhar, acione $C_2$ e $C_1$.
- $T_4$: Solicitação de Envio de Despacho (Retriable) $\to$ Executado após o pivô; tentei novamente até entregar.
Estilos arquitetônicos da saga: coreografia vs. orquestração
Existem dois estilos arquitetônicos principais para implementar o padrão Saga em sistemas distribuídos: Coreografia (Descentralizada) e Orquestração (Centralizada).
Estilo 1: Coreografia (Descentralização Orientada a Eventos)
Em uma Saga baseada em coreografia, não há controlador central ou coordenador. Em vez disso, os microsserviços se comunicam de forma assíncrona, ouvindo eventos de domínio publicados em um barramento de eventos central (como Apache Kafka, NATS ou RabbitMQ).
Mecânica do Fluxo de Trabalho
- Serviço de Pedido executa $T_1$ (cria uma ordem pendente) e emite um evento
OrderCreatedpara Kafka. - Serviço de pagamento escuta
OrderCreated, executa $T_2$ (cobra cartão de crédito) e emite um eventoPaymentCompleted(ouPaymentFailed). - Serviço de Inventário escuta
PaymentCompleted, executa $T_3$ (reserva itens). Se os itens estiverem fora de estoque, emiteInventoryReservationFailed. - Serviço de pagamento escuta
InventoryReservationFailede executa $C_2$ (emite reembolso). - Serviço de pedido escuta
PaymentRefundede executa $C_1$ (marca o pedido como cancelado).
Vantagens da Coreografia
- Loose Coupling: Os serviços assinam apenas tópicos de eventos; eles não sabem sobre outras implementações de serviço.
- Alto rendimento e descentralização: publicação/assinatura direta de eventos elimina gargalos do orquestrador central.
- Simplicidade para fluxos de trabalho curtos: Fácil de configurar para fluxos de trabalho simples de 2 a 3 etapas.
Desvantagens da Coreografia
- Risco de dependência cíclica: os serviços podem acabar ouvindo os eventos uns dos outros, criando dependências cíclicas complexas.
- Rastreabilidade de código difícil: compreender o processo de negócios de ponta a ponta requer lógica de rastreamento em vários repositórios de base de código.
- Tempestade de eventos e complexidade: à medida que o número de etapas aumenta (por exemplo, mais de 10 serviços), o gerenciamento de casos extremos de erro leva à explosão do estado do evento.
Estilo 2: Orquestração (gerenciamento centralizado de fluxo de trabalho)
Em uma Saga baseada em orquestração, um microsserviço dedicado conhecido como Saga Orchestrator controla todo o ciclo de vida da transação distribuída. O orquestrador atua como um coordenador central, emitindo comandos explícitos para os microsserviços participantes e ouvindo seus eventos de resposta.
Mecânica do Fluxo de Trabalho
- O cliente envia uma solicitação de pedido ao Saga Orchestrator.
- O Orchestrator envia um comando
CreateOrderpara Order Service. O serviço de pedido retornaOrderCreated. - O Orchestrator atualiza sua máquina de estado e envia um comando
ProcessPaymentpara Serviço de Pagamento. O serviço de pagamento retornaPaymentSuccessful. - O Orchestrator envia um comando
ReserveInventorypara Inventory Service. O Serviço de Inventário retornaInventoryFailed (Out of Stock). - O orquestrador detecta falha e inicia o fluxo de compensação:
- Envia o comando
RefundPaymentpara Serviço de Pagamento. - Envia o comando
CancelOrderpara Serviço de Pedido.
- Envia o comando
- O Orchestrator marca a execução do Saga como
FAILED.
Vantagens da Orquestração
- Lógica de negócios centralizada: o estado do fluxo de trabalho e a lógica de negócios são localizados em um único serviço de orquestrador ou máquina de estado.
- Sem dependências cíclicas: os microsserviços respondem aos comandos do orquestrador; eles não dependem nem conhecem outros serviços downstream.
- Monitoramento e depuração claros: o estado da transação ponta a ponta é explicitamente armazenado no armazenamento de estado do orquestrador (por exemplo, PostgreSQL ou mecanismos de fluxo de trabalho como Temporal/Camunda).
- Tratamento de erros mais fácil: a adição de novas etapas ou a alteração de regras de reversão são gerenciadas inteiramente dentro do orquestrador.
Desvantagens da orquestração
- Complexidade do orquestrador: risco de colocar muita lógica de domínio no orquestrador, transformando-o em um antipadrão “orquestrador inteligente, serviço burro”.
- Potencial ponto único de falha: o orquestrador deve ser altamente disponível e com estado.
Matriz Comparativa: Coreografia vs. Orquestração
| Recurso | Coreografia | Orquestração |
|---|---|---|
| Estrutura de Controle | Descentralizado (Evento Pub/Sub) | Centralizado (Coordenador de Saga / Máquina de Estado) |
| Acoplamento | Extremamente baixo (eventos de consumo de serviços) | Médio (Serviços aceitam comandos do Coordenador) |
| Visibilidade do Processo | Baixo (distribuído entre arquivos de log) | Alto (armazenamento de estado único visualiza fluxo de trabalho) |
| Mais adequado para | Fluxos de trabalho simples (2 a 4 etapas de serviço) | Fluxos de trabalho empresariais complexos (mais de 5 etapas, lógica de ramificação) |
| Ferramentas/Estruturas | Kafka, RabbitMQ, NATS, AWS EventBridge | Temporal.io, AWS Step Functions, Camunda, Axon |
Desafios e contramedidas de isolamento (lidando com “ACID menos I”)
Como as transações locais em um Saga são confirmadas imediatamente em seus bancos de dados locais, o padrão Saga carece de Isolamento (I) das garantias ACID tradicionais.
Se um cliente lê uma linha do banco de dados modificada por $T_1$ enquanto o Saga ainda está em execução, ele está lendo estado intermediário não confirmado. Se uma etapa downstream falhar e acionar a compensação ($C_1$), o cliente executou uma Leitura Suja.
Anomalias Comuns Causadas pela Falta de Isolamento
- Atualizações perdidas: Saga A atualiza um registro. Antes da Saga A ser concluída, a Saga B sobrescreve o mesmo registro. Se a Saga A falhar e executar a compensação, ela substituirá a atualização da Saga B.
- Dirty Reads: um cliente lê o estoque disponível atualizado pela Saga A ($T_1$). Saga A falha no downstream ($T_3$) e restaura o estoque ($C_1$), mas o cliente já fez um pedido com base em dados obsoletos.
- Leituras não repetíveis: um serviço lê dados na etapa $T_1$ e os lê novamente na etapa $T_3$, mas outro Saga simultâneo modificou os dados no meio.
Contramedidas e estratégias de mitigação
Para manter a integridade dos dados apesar da falta de isolamento, os arquitetos de software implementam padrões de design de isolamento específicos:
1. Bloqueio semântico (estado pendente/sinalizado)
Quando a transação local $T_1$ atualiza um registro do banco de dados, ela define um campo de status para PENDING ou APPROVAL_REQUIRED (por exemplo, ORDER_PENDING_PAYMENT).
Outros Sagas simultâneos que leiam este registro devem verificar o sinalizador de bloqueio semântico e bloquear ou alterar seu comportamento até que o estado mude para COMMITTED ou CANCELLED.
2. Confirmando pedido
Projete a sequência de transações locais para que operações de alto risco ou irreversíveis ocorram no final da execução do Saga, minimizando a janela de vulnerabilidade.
3. Validação de releitura (controle de simultaneidade otimista)
Antes de executar uma etapa crítica ou compensação, releia o registro do banco de dados de destino e verifique os carimbos de data/hora da versão (version_id) para garantir que nenhuma modificação simultânea ocorreu.
4. Visão Pessimista
Reordene as etapas de uma Saga para minimizar a exposição econômica (por exemplo, coloque a autorização de pagamento o mais próximo possível da transação central).
Fluxo de sequência de produção: transações de compensação
Abaixo está o diagrama de fluxo de sequência completo que ilustra uma falha de execução direta e a execução de compensação retroativa resultante:
Implementações práticas de código
Vamos explorar exemplos de implementação prontos para produção para coreografia (no Go) e orquestração (no Java Spring Boot).
Implementação 1: Saga baseada em coreografia em Go
Neste exemplo Go, demonstramos um Serviço de Pedido que trata da criação de pedidos e escuta eventos de falha de pagamento em um corretor de eventos para executar uma lógica de reversão compensatória.
package saga
import (
"context"
"encoding/json"
"fmt"
"log"
"time"
)
// Event definitions
type OrderCreatedEvent struct {
OrderID string `json:"order_id"`
CustomerID string `json:"customer_id"`
Amount float64 `json:"amount"`
}
type PaymentFailedEvent struct {
OrderID string `json:"order_id"`
Reason string `json:"reason"`
}
// OrderRepository handles local DB operations
type OrderRepository interface {
CreateOrder(ctx context.Context, orderID string, amount float64) error
UpdateOrderStatus(ctx context.Context, orderID string, status string) error
}
// EventBus abstraction for message broker (e.g., Kafka / NATS)
type EventBus interface {
Publish(topic string, payload []byte) error
Subscribe(topic string, handler func(payload []byte)) error
}
type OrderSagaChoreographer struct {
repo OrderRepository
eventBus EventBus
}
func NewOrderSagaChoreographer(repo OrderRepository, bus EventBus) *OrderSagaChoreographer {
c := &OrderSagaChoreographer{repo: repo, eventBus: bus}
c.registerSubscriptions()
return c
}
// Step 1: Forward Transaction (T1)
func (s *OrderSagaChoreographer) StartOrderSaga(ctx context.Context, orderID, customerID string, amount float64) error {
// Execute local database transaction
err := s.repo.CreateOrder(ctx, orderID, amount)
if err != nil {
return fmt.Errorf("failed local DB transaction T1: %w", err)
}
// Emit domain event for downstream Payment Service
event := OrderCreatedEvent{OrderID: orderID, CustomerID: customerID, Amount: amount}
bytes, _ := json.Marshal(event)
log.Printf("[SAGA][T1] Order %s created. Publishing OrderCreatedEvent...", orderID)
return s.eventBus.Publish("orders.created", bytes)
}
// Register subscription for compensating events
func (s *OrderSagaChoreographer) registerSubscriptions() {
_ = s.eventBus.Subscribe("payments.failed", func(payload []byte) {
var event PaymentFailedEvent
if err := json.Unmarshal(payload, &event); err != nil {
log.Printf("[ERROR] Corrupt payment event: %v", err)
return
}
// Execute Compensating Transaction (C1)
s.handlePaymentFailed(context.Background(), event)
})
}
// Step C1: Compensating Transaction
func (s *OrderSagaChoreographer) handlePaymentFailed(ctx context.Context, event PaymentFailedEvent) {
log.Printf("[SAGA][C1] Payment failed for Order %s (Reason: %s). Rolling back local order...", event.OrderID, event.Reason)
// Revert order status to CANCELLED in local DB
err := s.repo.UpdateOrderStatus(ctx, event.OrderID, "CANCELLED_PAYMENT_FAILED")
if err != nil {
log.Printf("[CRITICAL] Failed to execute compensating transaction C1 for Order %s: %v", event.OrderID, err)
// Trigger alert or write to Dead Letter Queue (DLQ)
return
}
log.Printf("[SAGA][SUCCESS] Order %s successfully compensated and cancelled.", event.OrderID)
}
Implementação 2: Saga baseada em orquestração em Java (Spring Boot)
Neste exemplo Java, construímos um Saga Orchestrator usando um padrão de máquina de estado para coordenar comandos de encaminhamento e executar reversões de compensação quando uma etapa downstream falha.
package com.ghaznix.saga.orchestrator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.UUID;
public enum SagaState {
STARTED,
ORDER_CREATED,
PAYMENT_PROCESSED,
INVENTORY_RESERVED,
COMPLETED,
COMPENSATING_PAYMENT,
COMPENSATING_ORDER,
FAILED
}
@Service
public class OrderSagaOrchestrator {
private static final Logger log = LoggerFactory.getLogger(OrderSagaOrchestrator.class);
private final OrderServiceClient orderClient;
private final PaymentServiceClient paymentClient;
private final InventoryServiceClient inventoryClient;
public OrderSagaOrchestrator(OrderServiceClient orderClient,
PaymentServiceClient paymentClient,
InventoryServiceClient inventoryClient) {
this.orderClient = orderClient;
this.paymentClient = paymentClient;
this.inventoryClient = inventoryClient;
}
public boolean executeOrderSaga(String customerId, String productId, double amount, int quantity) {
String sagaId = UUID.randomUUID().toString();
log.info("[SAGA {}] Starting Order Saga Workflow...", sagaId);
SagaState currentState = SagaState.STARTED;
String orderId = null;
String paymentId = null;
try {
// Step 1: Forward Local Tx T1 - Create Order
log.info("[SAGA {}][T1] Sending CreateOrder command...", sagaId);
orderId = orderClient.createOrder(customerId, productId, amount);
currentState = SagaState.ORDER_CREATED;
// Step 2: Forward Local Tx T2 - Process Payment
log.info("[SAGA {}][T2] Sending ProcessPayment command for Order {}...", sagaId, orderId);
paymentId = paymentClient.chargePayment(orderId, customerId, amount);
currentState = SagaState.PAYMENT_PROCESSED;
// Step 3: Forward Local Tx T3 (Pivot Step) - Reserve Inventory
log.info("[SAGA {}][T3] Sending ReserveInventory command for Product {}...", sagaId, productId);
boolean stockReserved = inventoryClient.reserveStock(productId, quantity);
if (!stockReserved) {
throw new InventoryAllocationException("Stock allocation failed: Item out of stock.");
}
currentState = SagaState.INVENTORY_RESERVED;
log.info("[SAGA {}][SUCCESS] Saga completed successfully!", sagaId);
return true;
} catch (Exception ex) {
log.error("[SAGA {}][FAILURE] Step failed during state {}. Triggering Compensation...", sagaId, currentState, ex);
rollbackSaga(sagaId, currentState, orderId, paymentId);
return false;
}
}
private void rollbackSaga(String sagaId, SagaState failedState, String orderId, String paymentId) {
log.info("[SAGA {}] Initiating backward compensating transactions from state: {}", sagaId, failedState);
// Compensate Step 2 if payment was processed
if (failedState == SagaState.PAYMENT_PROCESSED || failedState == SagaState.INVENTORY_RESERVED) {
try {
log.info("[SAGA {}][C2] Executing Payment Refund compensation for Payment {}...", sagaId, paymentId);
paymentClient.refundPayment(paymentId);
} catch (Exception e) {
log.error("[CRITICAL][SAGA {}] Payment refund C2 failed! Manual intervention or DLQ required.", sagaId, e);
}
}
// Compensate Step 1 if order was created
if (failedState != SagaState.STARTED && orderId != null) {
try {
log.info("[SAGA {}][C1] Executing Order Cancel compensation for Order {}...", sagaId, orderId);
orderClient.cancelOrder(orderId);
} catch (Exception e) {
log.error("[CRITICAL][SAGA {}] Order cancellation C1 failed!", sagaId, e);
}
}
log.info("[SAGA {}] Saga compensation workflow finished. Final State: FAILED.", sagaId);
}
}
Princípios básicos de produção: emparelhando Saga com o padrão de caixa de saída transacional
Tanto na Coreografia quanto na Orquestração, a execução de uma transação local ($T_i$) requer a publicação de um evento ou comando de domínio na rede.
Se o seu serviço atualizar seu banco de dados SQL e depois publicar uma mensagem no Kafka, uma falha de rede após a confirmação do banco de dados causará perda silenciosa de eventos. Por outro lado, publicar a mensagem antes da confirmação do banco de dados leva ao processamento de eventos fantasmas.
Para resolver isso, as Sagas devem ser emparelhadas com o Padrão Transacional de Caixa de Saída:
[Service Database Transaction Boundary]
+-----------------------------------------------------+
| 1. INSERT INTO business_table (orders/payments) |
| 2. INSERT INTO outbox_table (event_payload) |
+-----------------------------------------------------+
|
(CDC / Polling Message Relay)
|
v
[Message Broker / Kafka]
Ao persistir a carga útil do evento em uma tabela outbox dentro da mesma transação de banco de dados local, a atomicidade é garantida. Um processo assíncrono em segundo plano (como Debezium ou um polling relay) lê a tabela de caixa de saída e publica eventos no Kafka de maneira confiável.
Além disso, cada consumidor de serviço downstream deve implementar Idempotência (usando idempotency_key exclusivo ou cabeçalhos de desduplicação de mensagens) para que entregas duplicadas de mensagens durante novas tentativas não acionem cobranças duplicadas ou alocações de inventário.
Lista de verificação de decisões arquitetônicas
Use esta matriz de decisão prática ao projetar transações distribuídas para aplicativos de microsserviços:
Do you need cross-service data consistency?
|
+----------------+----------------+
| No | Yes
v v
Standard Single Service Can you accept Eventual
Local Database Consistency (BASE)?
|
+----------------+----------------+
| No | Yes
v v
Use Monolithic Core Adopt Saga Pattern
with Single ACID DB |
|
How complex is the workflow?
|
+-----------------+-----------------+
| Simple (2-3 steps) | Complex (4+ steps/branches)
v v
Choreography Saga Orchestration Saga
(Event-Driven Bus) (Temporal/Custom State Machine)
Conclusão e principais conclusões
O Padrão Saga é um padrão de arquitetura essencial para gerenciar transações distribuídas entre limites de microsserviços sem bloquear recursos ou sacrificar a disponibilidade do sistema.
Lista de verificação resumida:
- Abandone 2PC/XA em microsserviços nativos da nuvem: o Two-Phase Commit causa bloqueio rígido, alta latência e graves gargalos de disponibilidade.
- Dividir as transações em etapas locais: Divida as operações globais em transações locais ($T_1 \dots T_n$) emparelhadas com transações de compensação reversa ($C_1 \dots C_{n-1}$).
- Escolha o estilo arquitetônico correto:
- Use Coreografia para fluxos simples orientados por eventos de 2 a 3 etapas com acoplamento fraco.
- Use a Orquestração para fluxos de trabalho de negócios complexos que exigem visibilidade centralizada, ramificação e rastreamento de máquinas de estado.
- Implementar contramedidas de isolamento: Proteja contra leituras sujas e atualizações perdidas usando bloqueios semânticos (sinalizadores
PENDING) e bloqueio otimista de releitura. - Garantir mensagens confiáveis: sempre emparelhe as implementações do Saga com o Padrão de caixa de saída transacional e aplique Consumidores idempotentes para lidar com novas tentativas com segurança.
Ao implementar o padrão Saga cuidadosamente, você pode criar microsserviços altamente disponíveis e escalonáveis que permanecem resilientes e consistentes mesmo quando as redes falham.