DEV Community

Cover image for Saga Pattern: Consistência de Dados em Microsserviços de Verdade
Filipi Firmino
Filipi Firmino

Posted on

Saga Pattern: Consistência de Dados em Microsserviços de Verdade

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)