6 minutes
Desvendando o Padrão Saga com .NET 9, MassTransit e RabbitMQ: Orquestrando Microserviços com Confiança
Introdução: A Dor da Inconsistência
No mundo dos microserviços, cada serviço tem seu próprio banco de dados, sua própria fonte da verdade. Essa autonomia é fantástica para a escalabilidade e o desacoplamento, mas nos apresenta um desafio monumental: como garantir a consistência de um processo de negócio que se estende por vários serviços?
Imagine um fluxo de e-commerce simples:
- O
Serviço de Pedidoscria um novo pedido. - O
Serviço de Pagamentosprocessa o cartão de crédito. - O
Serviço de Estoquedebita o produto do inventário.
Se o pedido é criado e o pagamento é aprovado, mas o Serviço de Estoque falha (talvez o item tenha acabado no último segundo), o que acontece? O cliente foi cobrado por um produto que não receberá. O estado geral do sistema ficou inconsistente. Tentar resolver isso com chamadas HTTP diretas e blocos try-catch complexos é o caminho para um código frágil e um pesadelo de manutenção.
É para curar essa dor que o Padrão Saga existe. Ele nos fornece um modelo para gerenciar a consistência em transações de longa duração, garantindo que um processo de negócio ou conclua com sucesso, ou seja totalmente desfeito de forma limpa e auditável.
O que é o Padrão Saga?
Uma Saga é uma sequência de transações locais orquestradas para executar um trabalho maior. A regra de ouro é: para cada ação executada, deve haver uma ação de compensação correspondente, que é responsável por desfazer a ação original.
Pense na analogia de planejar uma viagem:
- Ação 1: Reservar um voo. (Compensação: Cancelar a reserva do voo).
- Ação 2: Reservar um hotel. (Compensação: Cancelar a reserva do hotel).
- Ação 3: Alugar um carro. (Compensação: Cancelar o aluguel do carro).
Se a reserva do voo e do hotel forem bem-sucedidas, mas o aluguel do carro falhar, a saga não para. Ela inicia o processo de compensação em ordem inversa: cancela a reserva do hotel e, em seguida, cancela a reserva do voo. Ao final, o sistema volta a um estado consistente — nenhuma viagem agendada, nenhum dinheiro gasto.
Orquestração vs. Coreografia
Existem duas formas principais de implementar uma Saga:
- Orquestração (a que veremos aqui): Um orquestador central (o “maestro”) é responsável por dizer a cada serviço o que fazer. Se uma etapa falha, o orquestrador comanda as ações de compensação. É explícito e mais fácil de depurar. O MassTransit brilha nesse modelo com suas Máquinas de Estado (State Machines).
- Coreografia: Não há um orquestrador central. Cada serviço publica eventos, e os outros serviços reagem a eles. É mais desacoplado, mas o fluxo geral pode ser difícil de rastrear.
Mão na Massa: Saga de Pedidos com .NET 9 e MassTransit
Vamos construir uma Saga de Orquestração simples para o nosso fluxo de e-commerce. Usaremos o MassTransit, um framework de Service Bus para .NET que torna a implementação de Sagas incrivelmente elegante.
Passo 1: Definindo os Contratos (As Mensagens)
Em um projeto compartilhado, definimos os comandos e eventos que conduzirão nossa saga. O CorrelationId é a chave que liga todas as mensagens de um mesmo processo.
public record SubmitOrder(Guid CorrelationId, string Item, int Quantity);
public record OrderSubmissionAccepted(Guid CorrelationId);
public record OrderSubmissionFaulted(Guid CorrelationId, string Reason);
public record ProcessPayment(Guid CorrelationId, decimal Amount);
public record RefundPayment(Guid CorrelationId);
Passo 2: O Cérebro - A Máquina de Estados da Saga
Este é o nosso orquestrador. Ele define os estados (Submetido, Aceito, Falha) e as transições.
Primeiro, a classe que guardará o estado de cada saga em andamento:
using MassTransit;
public class OrderState : SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public string CurrentState { get; set; }
public string? Item { get; set; }
public int? Quantity { get; set; }
}
Agora, a Máquina de Estados que define o fluxo:
using MassTransit;
using My.Commerce.Contracts;
public class OrderStateMachine : MassTransitStateMachine<OrderState>
{
public State Submitted { get; private set; }
public State Accepted { get; private set; }
public State Faulted { get; private set; }
public Event<SubmitOrder> SubmitOrder { get; private set; }
public Event<OrderSubmissionAccepted> OrderAccepted { get; private set; }
public Event<OrderSubmissionFaulted> OrderFaulted { get; private set; }
public OrderStateMachine()
{
// 2. Mapeia a propriedade que armazena o estado atual
InstanceState(x => x.CurrentState);
// 3. Define o fluxo da Saga
Initially(
// O processo começa quando o evento SubmitOrder é recebido
When(SubmitOrder)
.Then(context => // Armazena dados do evento no estado da saga
{
context.Saga.Item = context.Message.Item;
context.Saga.Quantity = context.Message.Quantity;
})
.SendCommand(context => // Envia um comando para processar o pagamento
new ProcessPayment(context.Message.CorrelationId, 100.0m)) // Valor fixo para o exemplo
.TransitionTo(Submitted) // Muda o estado para "Submitted"
);
During(Submitted,
// CENÁRIO FELIZ: O que fazer quando o evento de sucesso chega
When(OrderAccepted)
.TransitionTo(Accepted) // Muda o estado para "Aceito"
.Finalize(), // Finaliza a saga com sucesso
// CENÁRIO DE FALHA: O que fazer quando o evento de falha chega
When(OrderFaulted)
.Then(context => Console.WriteLine($"Pedido {context.Message.CorrelationId} falhou: {context.Message.Reason}"))
// Envia o comando de COMPENSAÇÃO para estornar o pagamento
.SendCommand(context => new RefundPayment(context.Message.CorrelationId))
.TransitionTo(Faulted) // Muda o estado para "Falha"
.Finalize() // Finaliza a saga
);
// Remove a instância da saga do banco de dados quando ela é finalizada.
SetCompletedWhenFinalized();
}
}
Passo 3: Configurando no Program.cs
Finalmente, registramos o MassTransit e nossa Saga na configuração de serviços da nossa aplicação.
// Program.cs
using MassTransit;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMassTransit(x =>
{
// Adiciona a Máquina de Estados da Saga
x.AddSagaStateMachine<OrderStateMachine, OrderState>()
.InMemoryRepository(); // Para produção, use .EntityFrameworkRepository() ou outro repositório persistente
// Configura o transporte (RabbitMQ)
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host("localhost", "/", h => {
h.Username("guest");
h.Password("guest");
});
// Configura os endpoints para sagas e consumidores automaticamente
cfg.ConfigureEndpoints(context);
});
});
// ... resto do Program.cs
Quando Usar (e Quando Não Usar) o Padrão Saga?
Use-o quando:
- Você precisa garantir a consistência de dados através de múltiplos microserviços.
- O processo de negócio é de longa duração e envolve várias etapas.
- Ações de compensação são necessárias para reverter falhas parciais.
- A resiliência do sistema é uma prioridade.
Pode ser excessivo quando:
- A operação envolve apenas um único serviço e pode ser resolvida com uma transação local.
- O processo de negócio não exige compensação em caso de falha.
- Você está construindo um CRUD simples ou uma prova de conceito.
Conclusão: Da Inconsistência à Resiliência Orquestrada
O Padrão Saga, especialmente quando implementado com uma ferramenta poderosa como o MassTransit, transforma o caos potencial dos processos distribuídos em um fluxo de trabalho orquestrado, resiliente e transparente. Ele nos força a modelar explicitamente não apenas o “caminho feliz”, mas também os fluxos de falha e compensação, que são a essência da construção de sistemas robustos.
Ao adotar Sagas, você não está apenas escrevendo código; você está modelando a resiliência do seu negócio diretamente na sua arquitetura, garantindo que, mesmo quando as coisas dão errado, seu sistema saiba exatamente como se recuperar com confiança.
saga pattern .net masstransit rabbitmq microservices arquitetura de software resiliência sistemas distribuídos
1122 Words
2025-09-13 17:00