
Era quarta-feira, meio do expediente. O time de suporte começou a receber reclamações: pedidos estavam demorando horas para serem enviados aos fornecedores.
O problema parecia intermitente. Alguns pedidos fluíam normalmente, outros ficavam "presos" sem nenhum motivo aparente. O time de infraestrutura já tinha reiniciado os servidores duas vezes, mas o problema voltava.
Depois de algumas horas investigando os logs, a causa ficou clara: um dos nossos principais fornecedores estava com o gateway de integração lento.
Nota: A história contada acima é fictícia e foi criada apenas para se ter um entendimento melhor do assunto de forma prática.
O Problema em Poucas Linhas
O fluxo funcionava assim:
Sistema principal → chamada síncrona → Fornecedor A (rápido)
Sistema principal → chamada síncrona → Fornecedor B (rápido)
Sistema principal → chamada síncrona → Fornecedor C (lento ou fora do ar)
A decisão de qual fornecedor atenderia o pedido só era tomada após todas as respostas.
Resultado: o mais lento define o ritmo de todos.
E pior: quando um falha, todo o fluxo quebra.
Esse é um problema clássico de sistemas acoplados de forma síncrona.
O Impacto Real
- Pedidos travados por minutos (ou horas)
- Aumento de timeout e retrabalho
- Sobrecarga no suporte
- Experiência ruim para o usuário final
E o mais perigoso: o problema não estava no nosso sistema diretamente, mas em uma dependência externa.
A Virada de Chave
A pergunta que mudou tudo foi:
"Por que estamos esperando fornecedores externos para continuar nosso fluxo interno?"
A resposta era simples: não deveríamos estar.
A Solução: Mensageria com Filas
Redesenhamos o fluxo com um objetivo claro: desacoplar o sistema principal das integrações externas.
Novo fluxo:
- O pedido é registrado imediatamente
- O sistema publica mensagens para cada fornecedor
- Cada fornecedor é processado de forma independente
- Falhas não bloqueiam o fluxo principal
Agora, o sistema principal não depende mais do tempo de resposta dos fornecedores.
Como Funciona na Prática
- Produtor: publica o pedido nas filas
- Fila: armazena as mensagens de forma resiliente
- Consumidor: processa cada fornecedor separadamente
- Retry: reprocessa falhas com backoff
Se o Fornecedor C estiver lento, apenas o consumidor dele será impactado.
O restante do sistema continua funcionando normalmente.
Estratégia de Retentativas (Retry)
Implementamos uma política simples e eficiente:
- Até 5 tentativas
- Delay progressivo (backoff)
- Após o limite → fila de falhas (dead-letter)
Isso evita:
- Sobrecarga no fornecedor
- Loop infinito de erro
- Perda silenciosa de pedidos
Modelos de Mensageria
1. Fila (Point-to-Point)
Uma mensagem é consumida por apenas um consumidor.
Use quando:
- Há responsabilidade única
- O processamento não pode ser duplicado
Exemplos:
- Envio de pedidos
- Geração de boletos
- Processamento de arquivos
2. Pub/Sub (Publish/Subscribe)
Uma mensagem é distribuída para múltiplos consumidores.
Use quando:
- Vários sistemas precisam reagir ao mesmo evento
Exemplos:
- Pedido criado → estoque, faturamento, logística
- Eventos de domínio
Ferramenta Utilizada
RabbitMQ
Por que escolhemos:
- Simples de começar
- Extremamente estável
- Suporte a múltiplos padrões de mensageria
Exemplo Prático (Node.js)
// Producer
const amqp = require('amqplib');
async function enviarPedidoParaFornecedor(pedido, fornecedorId) {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = `fornecedor_${fornecedorId}`;
const message = {
pedidoId: pedido.id,
itens: pedido.itens,
tentativa: 1
};
await channel.assertQueue(queue, { durable: true });
channel.sendToQueue(queue, Buffer.from(JSON.stringify(message)));
console.log(`✅ Pedido ${pedido.id} enfileirado`);
await channel.close();
await connection.close();
}
// Consumer
async function processarFilaFornecedor(fornecedorId) {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = `fornecedor_${fornecedorId}`;
await channel.assertQueue(queue, { durable: true });
channel.consume(queue, async (msg) => {
const pedido = JSON.parse(msg.content.toString());
try {
await axios.post(`https://api.fornecedor${fornecedorId}.com/pedidos`, pedido, {
timeout: 5000
});
channel.ack(msg);
} catch (error) {
if (pedido.tentativa < 5) {
pedido.tentativa++;
setTimeout(() => {
channel.sendToQueue(queue, Buffer.from(JSON.stringify(pedido)));
}, 5000 * pedido.tentativa);
} else {
await channel.sendToQueue('pedidos_falhos', Buffer.from(JSON.stringify(pedido)));
}
channel.ack(msg);
}
});
}
Uma Analogia Simples
Imagine que você está em um restaurante e faz um pedido que depende de três cozinhas diferentes:
- Entrada → cozinha A (rápida)
- Prato principal → cozinha B (rápida)
- Sobremesa → cozinha C (lenta)
Agora imagine que o garçom só pode te entregar o pedido completo quando todas as cozinhas terminarem.
Se a sobremesa atrasar, você fica sem comer — mesmo com o resto pronto.
Esse era exatamente o nosso cenário.
Com mensageria, o modelo muda:
Cada cozinha recebe seu pedido separadamente e trabalha no seu tempo.
O restaurante não trava por causa de uma sobremesa atrasada.
👉 O sistema continua fluindo, mesmo com partes lentas.
Diagrama do Fluxo
Antes (Síncrono)
[ Sistema Principal ]
↓
┌───────────────┬───────────────┬───────────────┐
↓ ↓ ↓
Fornecedor A Fornecedor B Fornecedor C (lento)
(rápido) (rápido) (problema)
⛔ Espera todos → sistema bloqueado
---
Depois (Assíncrono com Filas)
[ Sistema Principal ]
↓
(Publica mensagens)
↓
┌───────────────┬───────────────┬───────────────┐
↓ ↓ ↓
Fila A Fila B Fila C
↓ ↓ ↓
Consumer A Consumer B Consumer C
↓ ↓ ↓
Fornecedor A Fornecedor B Fornecedor C (lento)
✅ Cada fluxo é independente
✅ Falhas não bloqueiam o sistema
✅ Retry isolado por fornecedor
Benefícios Imediatos
1. Sistema mais resiliente
Falhas externas não afetam mais o fluxo principal.
2. Melhor experiência do usuário
Pedidos são processados imediatamente, sem travar.
3. Time mais produtivo
Menos interrupções e menos retrabalho.
4. Integrações mais fáceis de manter
Cada fornecedor tem seu próprio consumidor, isolado dos outros.
Quando Usar Mensageria?
Use quando:
- Você tem integrações com sistemas externos
- Precisa processar tarefas em segundo plano
- O sistema precisa continuar funcionando mesmo com falhas
- Você precisa desacoplar serviços
Não use quando:
- O sistema é muito simples
- Não há necessidade de processamento assíncrono
- A latência de milissegundos é crítica
Conclusão
A mensageria resolve problemas reais de sistemas distribuídos. No nosso caso, transformou um fluxo frágil em uma arquitetura resiliente.
Se você enfrenta problemas com timeouts, integrações lentas ou sistemas acoplados, considere usar filas.
É uma das ferramentas mais poderosas do ecossistema de software moderno.