Bora falar de Sagas?
Garantir consistência de dados em sistemas distribuídos é, sem exagero, um dos maiores perrengues que a gente enfrenta quando entra no mundo dos microsserviços. É tipo tentar manter várias bandas tocando juntas sem maestro — cada uma no seu ritmo, mas tudo tem que soar em harmonia.
E é aí que entra o Saga Pattern: um jeito moderno e bem pragmático de coordenar serviços diferentes, sem perder escalabilidade nem resiliência.
🤔 Por que a gente precisa de Sagas?
Num monolito, a vida é mais simples: tudo roda num banco central, transações ACID cuidam da consistência e tá tudo certo.
Mas em microsserviços... cada domínio tem seu próprio banco. Então, imagina fechar um pedido de e-commerce — você precisa mexer em pedido, pagamento, estoque e entrega, tudo ao mesmo tempo e de forma sincronizada.
Se uma dessas etapas falha, tipo o pagamento passa, mas o estoque não tá disponível... pronto, o sistema fica num estado meio zumbi.
O Saga Pattern resolve exatamente isso: ele evita esses cenários "meio do caminho", garantindo que, se algo der errado, o sistema saiba como se recuperar — compensando as ações e voltando pra um estado consistente.
⚙️ Como o Saga Pattern funciona na prática
A ideia é dividir uma grande transação em várias transações locais.
Cada microserviço faz sua parte (de forma ACID local) e, se alguma etapa der ruim, o sistema executa transações compensatórias pra desfazer o que já foi feito.
Assim, mesmo com falhas ou timeouts, o sistema não quebra — ele se ajusta e segue a vida.
Em resumo:
- Cada serviço cuida da sua transação local;
- A comunicação entre eles acontece por eventos ou comandos;
- Se algo falha, rola compensação e nada fica “órfão” no sistema.
Tipos de Sagas: Coreografia vs Orquestração
Coreografada:
Cada serviço reage a eventos e publica seus próprios — tipo uma dança sincronizada sem maestro.
É leve e funciona bem pra fluxos simples, mas conforme o sistema cresce, vira um caos entender quem chama quem.
Orquestrada:
Aqui entra um “maestro”: o orquestrador.
Ele coordena o fluxo, chama os serviços (síncrono ou assíncrono), espera respostas e dispara compensações quando algo falha.
Esse modelo é ótimo pra cenários mais críticos — tipo financeiro, pagamentos ou e-commerce de grande porte — porque dá rastreabilidade e facilita observabilidade.
🔄 Ciclo de Vida e Estado
Uma boa implementação de saga geralmente usa máquinas de estado pra acompanhar tudo:
Iniciado → Reservando estoque → Pagando → Entregando → Finalizado.
Isso garante:
- Idempotência (se rodar duas vezes, o resultado é o mesmo),
- Resiliência (suporte a retries, timeouts, intervenção manual),
- Auditoria (você sabe exatamente onde algo deu ruim).
🔁 Compensações: o segredo da consistência
Pensa assim: se o pagamento falha, o sistema automaticamente desfaz o que já fez — tipo cancelar o pedido, liberar o estoque e reembolsar o valor.
Essas ações compensatórias são o coração do Saga Pattern e o que garante que o sistema continue íntegro, sem dados zumbis vagando pelo banco.
⚠️ Desafios e Boas Práticas
Alguns pontos pra ficar de olho:
Dual Write: gravar no banco e publicar evento ao mesmo tempo pode dar ruim se rolar falha no meio. Padrões como Outbox ou Change Data Capture ajudam muito aqui.
- Observabilidade: log detalhado, métricas e tracing distribuído são obrigatórios.
- Idempotência: sempre projete handlers que suportem reexecuções sem causar bagunça.
- Compensações bem definidas: se tem CobrarPedido(), precisa ter EstornarCobranca().
Se você tá no ecossistema .NET, existem várias opções pra implementar sagas:
- MassTransit (integra bem com RabbitMQ, Azure Service Bus, etc.);
- NServiceBus;
- Ou até usando Dapr e Azure Durable Functions, se quiser algo mais workflow-oriented.
A escolha entre coreografia e orquestração vai depender do cenário — se o foco é rastreabilidade e controle, orquestração costuma ganhar.
💡 Exemplo prático (pseudocódigo com MassTransit)
public class PedidoSaga : MassTransitStateMachine<PedidoState>
{
public PedidoSaga()
{
InstanceState(x => x.Status);
Initially(
When(PedidoCriado)
.Then(context => context.Instance.PedidoId = context.Data.PedidoId)
.Publish(context => new ReservarEstoque { PedidoId = context.Instance.PedidoId })
.TransitionTo(Reservando)
);
During(Reservando,
When(EstoqueReservado)
.Publish(context => new ProcessarPagamento { PedidoId = context.Instance.PedidoId })
.TransitionTo(Pagando),
When(ReservaFalhou)
.Publish(context => new CancelarPedido { PedidoId = context.Instance.PedidoId })
.Finalize()
);
SetCompletedWhenFinalized();
}
}
No modelo coreografado, cada serviço publicaria eventos e escutaria eventos de outros, agindo de forma independente:
PedidoCriado → Evento:PedidoCriado → Serviço Estoque responde
(EstoqueReservado) / (FalhaReservaEstoque)
Quando (e por que) usar ou evitar uma Saga?
- Use saga quando houver múltiplos serviços e a consistência dos dados entre eles for obrigatória para o negócio;
- Evite em cenários de baixa criticidade ou quando a implementação e manutenção do padrão trouxerem mais complexidade que benefícios.
Conclusão
O Saga Pattern não é bala de prata, mas está entre os padrões mais robustos e pragmáticos para lidar com consistência de dados distribuídos em microsserviços, especialmente em cenários críticos como e-commerce e financeiro. Aplicado de forma consciente, transforma sistemas frágeis em soluções resilientes, auditáveis e evolutivas.
Top comments (0)