Pular para o conteúdo principal

Mensageria

Para quem é esta página

Engenheiros back-end. Decisão de usar RabbitMQ: ADR-004.

Topologia

Exchange: car.events (type: topic)

├── processo.* → fila: processos.analista
├── processo.* → fila: processos.notificacao
├── documento.* → fila: documentos.ocr
├── canal.web.* → fila: canal.web.mensagens ← canal core (interface web própria)
├── *.regular → fila: integracao.sicar
└── # → fila: analytics.metricas

Dead Letter Exchange: car.dlx
└── car.dlq.{nome_da_fila} (após 3 tentativas)

Adapters futuros (desacoplados do core):
└── canal.mensageria.* → fila: adapter.whatsapp / adapter.telegram
(serviços externos consomem esta fila; o core não depende deles)

Retry Policy

Após falha no processamento de uma mensagem:

TentativaDelay
1 segundo
5 segundos
25 segundos
4ª (falhou)→ Dead Letter Queue permanente
Consumers devem ser idempotentes

Como mensagens podem ser entregues mais de uma vez (falha antes do ACK), sempre verifique se o evento já foi processado via event_id antes de executar a lógica.

Outbox Pattern

Garante que um evento nunca é publicado sem o dado correspondente estar no banco:

# Na mesma transação ACID:
async with db.begin():
processo_salvo = await repo.save(processo)
for event in processo.domain_events:
await outbox_repo.save(OutboxMessage(
event_name=event.routing_key,
payload=event.to_dict(),
status="pendente",
))
processo.clear_events()

# Worker separado (Outbox Relay) lê e publica:
# SELECT * FROM outbox WHERE status='pendente' ORDER BY created_at LIMIT 100
# → Publica no RabbitMQ
# → UPDATE outbox SET status='publicado'

Schema de Mensagem

{
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"event_name": "processo.submetido.v1",
"occurred_at": "2026-01-15T10:30:00Z",
"correlation_id": "req-uuid",
"payload": {
"processo_id": "...",
"requerente_id": "...",
"municipio_ibge": "2111300"
}
}

Ver também