Marcio Cunha

Isolamento de Domínio e Comunicação Assíncrona em Microsserviços com Sagas Coreografadas

Descubra como construir arquiteturas resilientes de microsserviços utilizando sagas coreografadas, isolamento de domínio rigoroso e troca assíncrona de mensagens sem coordenadores centrais.

Marcio Cunha•6 min
Também disponível em:EnglishEspañol
Resumo
  • O isolamento de domínio garante que falhas em um microsserviço não corrompam o estado de outros contextos de negócio.
  • Sagas coreografadas eliminam pontos únicos de falha ao delegar reações locais a eventos publicados em brokers de mensagens.
  • A comunicação assíncrona desacopla os serviços no tempo e no espaço, permitindo picos de tráfego sem queda na disponibilidade.
  • Transações distribuídas exigem compensações explícitas para desfazer operações quando falhas ocorrem no meio do fluxo.
  • A visibilidade operacional torna-se o maior desafio na coreografia, exigindo rastreamento distribuído rigoroso com identificadores de correlação.

O Desafio da Consistência em Sistemas Distribuídos

Quando dividimos um sistema monolítico em vários microsserviços independentes, ganhamos agilidade de deploy e escalabilidade, mas pagamos um preço alto na gestão de dados. Em um monólito tradicional, uma simples instrução de banco de dados chamada transação ACID (um conjunto de operações que ou acontecem todas juntas ou são canceladas por completo) garante que o saldo de uma conta diminua exatamente quando o pedido é confirmado. No mundo dos microsserviços, cada serviço possui seu próprio banco de dados isolado. Na prática, isso significa que não podemos mais usar comandos simples de banco para atualizar dados em dois lugares diferentes ao mesmo tempo.

Para contornar essa limitação sem acoplar os bancos de dados, recorremos a um padrão arquitetural conhecido como Saga. Uma saga é uma sequência de transações locais que atualizam os dados em cada serviço envolvido em um processo de negócio. Se todas as etapas forem bem-sucedidas, o processo termina com sucesso. No entanto, se um serviço falhar no meio do caminho, a saga executa transações compensatórias para desfazer o que foi feito anteriormente, restabelecendo o equilíbrio do sistema. Esse modelo substitui a rigidez das transações síncronas por uma consistência eventual, onde os dados se tornam corretos pouco depois de os eventos acontecerem.

Sagas Orquestradas versus Sagas Coreografadas

Existem duas maneiras principais de implementar o padrão saga: orquestração e coreografia. Na abordagem orquestrada, existe um componente central (o orquestrador) que dita exatamente qual serviço deve fazer o quê e em qual ordem. Embora pareça simples de gerenciar no início, esse orquestrador rapidamente se transforma em um gargalo de desempenho e num ponto único de falha, lembrando perigosamente os velhos monólitos que tentamos abandonar. Na prática, isso significa que se o orquestrador cair, todo o fluxo de negócios para, mesmo que os microsserviços individuais estejam perfeitamente saudáveis.

Por outro lado, a coreografia distribui a inteligência do fluxo entre os próprios microsserviços. Não existe um diretor central de orquestra; cada serviço escuta eventos que acontecem no sistema, realiza sua própria operação e publica um novo evento informando o resultado. Pense nisso como uma dança de salão onde os participantes conhecem os passos e reagem aos movimentos dos outros, em vez de depender de um coreógrafo gritando ordens. Essa abordagem promove o isolamento de domínio absoluto, pois nenhum serviço precisa conhecer os detalhes internos ou os endereços dos demais, bastando que concordem com o contrato das mensagens trocadas.

Isolamento de Domínio Através de Contextos Delimitados

O isolamento de domínio é a prática de garantir que cada microsserviço cuide exclusivamente de suas próprias regras de negócio, sem vazar detalhes técnicos ou estruturas de dados para o restante da aplicação. Na engenharia de software, isso é formalizado pelo conceito de Contexto Delimitado, onde termos comuns (como 'cliente' ou 'produto') ganham significados específicos dependendo de onde são processados. Na prática, o serviço de faturamento enxerga o cliente sob a ótica de dados fiscais e limites de crédito, enquanto o serviço de entrega o enxerga apenas como um endereço postal e um nome para contato.

Quando permitimos que serviços acessem diretamente o banco de dados de outros contextos, criamos um acoplamento invisível e altamente frágil. Se a equipe de inventário decidir alterar a estrutura de uma tabela, o sistema de vendas pode quebrar instantaneamente. Para evitar essa armadilha, a comunicação deve ocorrer estritamente por meio de contratos de mensagens bem definidos, conhecidos como eventos de domínio. O isolamento rigoroso garante que a evolução de um serviço ocorra de forma independente, permitindo que diferentes equipes implementem melhorias sem medo de quebrar o ecossistema inteiro.

Comunicação Assíncrona com Brokers de Mensagens

A troca síncrona de dados, como chamadas diretas via HTTP ou gRPC, acopla os serviços no tempo: o remetente é obrigado a esperar que o destinatário responda imediatamente. Se o serviço de destino estiver lento ou fora do ar, a chamada falha e o erro se propaga em cascata. Para evitar esse comportamento catastrófico, adotamos a comunicação assíncrona utilizando brokers de mensagens, que funcionam como intermediários robustos capazes de armazenar e entregar mensagens com segurança, mesmo se o serviço consumidor estiver temporariamente indisponível.

Ferramentas como Apache Kafka, RabbitMQ ou AWS SQS atuam como os corretores dessa infraestrutura. Quando um evento de negócio ocorre, como a criação de um novo pedido, o serviço de origem publica uma mensagem no barramento e encerra seu trabalho sem precisar saber quem vai consumi-la. Os serviços interessados leem essa mensagem no próprio ritmo, processam a informação e seguem adiante. Na prática, isso significa que picos repentinos de acesso são absorvidos pela fila, transformando picos de estresse em um fluxo constante e gerenciável de trabalho.

Implementação Prática de uma Coreografia de Sagas

Para ilustrar o funcionamento de uma saga coreografada, imagine um fluxo de comércio eletrônico composto por três serviços independentes: Pedido, Estoque e Pagamento. Quando um cliente finaliza uma compra, o serviço de Pedido salva os dados localmente com o status 'Pendente' e publica um evento chamado `PedidoCriado`. O serviço de Estoque consome esse evento, tenta reservar os produtos e, caso consiga, publica o evento `EstoqueReservado`. Se faltar produto, ele publica `EstoqueInsuficiente`, sinalizando que o fluxo precisa ser interrompido e compensado.

O serviço de Pagamento, por sua vez, escuta o evento `EstoqueReservado`. Ele processa a cobrança no cartão de crédito e, se tudo correr bem, emite o evento `PagamentoAprovado`. Caso a operadora recuse o pagamento, o serviço emite `PagamentoRecusado`. Veja o trecho de código abaixo demonstrando como um consumidor em Node.js gerencia esses eventos utilizando uma estrutura simples de escuta em um barramento de mensagens:

const { eventBus } = require('./messageBus');

const handleInventoryReserved = async (event) => {
  try {
    const { orderId, amount, customerId } = event.payload;
    console.log(`Processando pagamento para o pedido ${orderId}`);
    
    const paymentSuccess = await chargeCreditCard(customerId, amount);
    
    if (paymentSuccess) {
      await eventBus.publish('PagamentoAprovado', { orderId });
    } else {
      await eventBus.publish('PagamentoRecusado', { orderId });
    }
  } catch (error) {
    console.error('Erro ao processar pagamento:', error);
    await eventBus.publish('PagamentoComErro', { orderId: event.payload.orderId });
  }
};

eventBus.subscribe('EstoqueReservado', handleInventoryReserved);

Neste exemplo prático, cada microsserviço reage a eventos específicos sem que haja um coordenador central controlando o relógio. Se o pagamento falhar, o evento `PagamentoRecusado` será capturado pelo serviço de Estoque, que executará uma transação compensatória para devolver os itens reservados às prateleiras virtuais. Para complementar essa implementação em produção, vale conferir também nossas análises técnicas sobre Isolamento de Domínio e Comunicação Assíncrona com Domain-Driven Design e Outbox Pattern e Comunicação Assíncrona em Microsserviços com Filas Priorizadas baseadas em Redis e Scripts Lua.

Considerações Finais e Monitoramento Distribuído

Adotar o isolamento de domínio e a comunicação assíncrona com sagas coreografadas resolve problemas críticos de escalabilidade e resiliência, mas introduz novos desafios operacionais. Como o fluxo de trabalho está espalhado por vários serviços e mensagens, depurar um erro exige ferramentas avançadas de rastreamento distribuído, conhecidas como distributed tracing. Atribuir um identificador único a cada requisição na entrada do sistema e propagá-lo por todas as mensagens subsequentes é o único modo de auditar o caminho percorrido por uma transação.

Em última análise, arquiteturas baseadas em eventos exigem uma mudança cultural na equipe de engenharia. É preciso abandonar o conforto das transações monolíticas imediatas e abraçar a complexidade inerente à consistência eventual. Quando bem projetada, essa abordagem recompensa a organização com sistemas altamente desacoplados, capazes de evoluir de forma independente e de resistir a falhas parciais sem derrubar a aplicação inteira.