Publicação e assinatura
Um evento, vários consumidores independentes, e o desacoplamento que isso traz.
Um fato, vários interessados
Quando um pedido é criado, vários serviços precisam saber: o estoque reserva os itens, o serviço de e-mail envia a confirmação, a área de análise registra a venda e o programa de fidelidade soma pontos.
O serviço de pedidos poderia chamar cada um. Mas aí ele precisa conhecer todos, e cada reação nova (um serviço antifraude, por exemplo) exige mudar o código dele e publicar uma versão nova.
Na publicação e assinatura (publish/subscribe, ou pub/sub), o serviço de pedidos só anuncia o fato: "o pedido 981 foi criado". Ele publica num tópico e não sabe quem está ouvindo. Cada serviço interessado cria uma assinatura (subscription) nesse tópico e recebe a sua própria cópia de cada evento.
┌─▶ assinatura "estoque" ─▶ serviço de estoque
serviço de pedidos ─▶ tópico ────┼─▶ assinatura "email" ─▶ serviço de e-mail
(publicador) "pedidos" └─▶ assinatura "analise" ─▶ serviço de análiseO serviço antifraude, quando chegar, cria a assinatura dele. O serviço de pedidos não muda.
Ponto a ponto e publicação e assinatura
O tópico anterior tratou da fila de tarefas, em que cada mensagem vai para um consumidor. Publicação e assinatura é o caso em que cada mensagem vai para todos os interessados.
| Ponto a ponto (fila) | Publicação e assinatura | |
|---|---|---|
| Cada mensagem é entregue a | Um consumidor | Cada assinatura |
| O publicador conhece o destino | Sim, publica na fila daquele trabalho | Não, publica no tópico |
| A mensagem costuma ser | Um comando: "envie este e-mail" | Um evento: "o pedido foi criado" |
| Acrescentar um consumidor novo | Divide o trabalho com os atuais | Recebe tudo, sem afetar os outros |
A diferença entre comando e evento ajuda a escolher. Um comando tem um dono, que vai executá-lo, e pode falhar ("não consegui enviar"). Um evento descreve algo que já aconteceu; ninguém pode recusá-lo, cada assinante só decide o que fazer com ele. Por isso eventos são nomeados no passado: pedido.criado, pagamento.aprovado.
Assinatura e instância não são a mesma coisa
Este é o ponto que mais confunde. O serviço de estoque roda em três instâncias, para aguentar a carga. Quando o evento "pedido 981 criado" chega, o estoque precisa ser baixado uma vez, não três.
Por isso, quase todo sistema de pub/sub combina os dois modelos:
- Entre assinaturas, cada uma recebe todas as mensagens.
- Dentro de uma assinatura, as instâncias do mesmo serviço dividem as mensagens, como numa fila de tarefas. São os consumidores concorrentes (competing consumers).
┌─▶ assinatura "estoque" ─┬─▶ estoque, instância 1
tópico "pedidos" ─────────┤ ├─▶ estoque, instância 2
│ └─▶ estoque, instância 3
└─▶ assinatura "email" ───▶ e-mail, instância 1A regra prática: uma assinatura por serviço, não por instância. Cada sistema dá um nome diferente para a assinatura:
| Sistema | O que faz o papel da assinatura |
|---|---|
| RabbitMQ | Uma fila ligada à troca (exchange) do tópico |
| Kafka | Um grupo de consumidores (group.id) |
| Amazon SNS | Uma fila SQS inscrita no tópico |
| Google Pub/Sub | A assinatura (subscription) |
| Azure Service Bus | A assinatura do tópico |
Errar aqui produz dois defeitos opostos. Se cada instância tem a sua assinatura, o evento é processado três vezes. Se dois serviços diferentes usam a mesma assinatura, eles dividem os eventos entre si, e cada um vê só uma parte.
Durável ou efêmera
Uma assinatura durável guarda as mensagens enquanto o assinante está fora do ar. Quando o serviço de e-mail volta de uma manutenção, os eventos da última hora estão esperando por ele. É o comportamento de uma fila durável no RabbitMQ, de um grupo de consumidores no Kafka e das assinaturas dos serviços gerenciados.
Uma assinatura efêmera só existe enquanto o assinante está conectado. Quem não está ouvindo no momento da publicação perde a mensagem, para sempre. É o caso do Redis Pub/Sub e de filas exclusivas e temporárias no RabbitMQ.
| Durável | Efêmera | |
|---|---|---|
| Assinante fora do ar | As mensagens esperam | As mensagens se perdem |
| Assinante lento | A fila cresce | Recebe o que der; no Redis, é desconectado quando o buffer de saída estoura |
| Bom para | Regras de negócio: estoque, cobrança, e-mail | Avisos que perdem valor rápido: atualizar telas abertas, invalidar caches locais |
Quem chega depois
Uma assinatura só recebe o que for publicado depois de ela existir. No RabbitMQ, uma mensagem publicada numa troca sem nenhuma fila ligada é descartada, sem erro. No SNS e no Google Pub/Sub, a assinatura nova começa vazia.
O Kafka é a exceção, porque guarda o log: um grupo de consumidores novo pode começar do início do tópico (auto.offset.reset=earliest) e ler todo o histórico retido. O padrão, porém, é latest, que começa pelas mensagens novas.
Isso tem duas consequências práticas:
- A ordem de implantação importa. Se o publicador sobe antes de a assinatura ser criada, os primeiros eventos se perdem. Crie as assinaturas antes, na infraestrutura, e não só quando o consumidor inicia.
- Um serviço novo precisa de uma carga inicial. Se ele precisa conhecer os pedidos antigos, eles vêm de outra fonte (uma consulta à API, uma exportação), a menos que o histórico esteja num log como o Kafka.
Filtrar o que cada um recebe
Nem todo assinante quer todos os eventos. O serviço de fidelidade só se interessa por pedidos pagos; o de logística, só pelos do Brasil. Os intermediários permitem filtrar na assinatura, para que a mensagem nem chegue ao assinante:
| Sistema | Como filtrar |
|---|---|
| RabbitMQ | Troca do tipo topic, com padrões na chave de roteamento: pedido.* (uma palavra) ou pedido.# (zero ou mais) |
| Amazon SNS | Política de filtro sobre os atributos ou o corpo da mensagem |
| Google Pub/Sub | Filtro sobre os atributos, definido na criação da assinatura |
| Azure Service Bus | Filtros SQL ou de correlação na assinatura |
| Kafka | Não filtra no intermediário; o consumidor lê e descarta, ou se separam os eventos em tópicos diferentes |
O que se ganha e o que se paga
| Ganho | Custo |
|---|---|
| O publicador não conhece os assinantes | Ninguém sabe, olhando o publicador, o que acontece depois de um evento |
| Reações novas sem mudar o publicador | O formato do evento vira um contrato com assinantes que você não vê |
| Cada assinante falha e escala sozinho | Cada um fica consistente no seu tempo; os dados divergem por um intervalo |
| Um assinante lento não atrasa os outros | Cada assinatura precisa de monitoramento próprio |
O primeiro custo é o mais traiçoeiro. Numa chamada direta, o código mostra a sequência. Com eventos, a sequência está espalhada entre serviços, e entender "por que o cliente não recebeu o e-mail" exige olhar cada assinatura. Rastreamento distribuído e um catálogo de quem assina o quê ajudam.
O evento é um contrato
Quando o serviço de pedidos muda o formato do evento, ele pode quebrar assinantes que nem sabe que existem. Mudanças seguras e inseguras:
| Mudança | Assinantes antigos |
|---|---|
| Acrescentar um campo opcional | Continuam funcionando, se ignorarem campos desconhecidos |
| Remover ou renomear um campo | Quebram |
| Mudar o tipo de um campo (número para texto) | Quebram |
| Mudar o significado de um campo, mantendo o nome | Continuam rodando, com resultados errados |
A regra é a mesma do versionamento de APIs: só acrescentar. Quando uma mudança incompatível é inevitável, publica-se um evento novo (pedido.criado.v2, ou um campo versao) ao lado do antigo, até todos os assinantes migrarem.
Do lado do assinante, vale o princípio do leitor tolerante (tolerant reader): ler só os campos de que precisa e ignorar o resto.
Por que isso importa quando a IA escreve o código
Um assistente monta um publicador e um assinante em minutos, e o exemplo funciona na primeira execução. Os defeitos aparecem quando o serviço ganha réplicas, reinicia ou evolui:
- O assinante copia o exemplo do tutorial do RabbitMQ, com uma fila exclusiva de nome gerado pelo servidor. Com três instâncias, cada evento é processado três vezes; quando uma instância reinicia, a fila dela some com os eventos pendentes.
- No Kafka, o
group.idleva o nome da máquina ou um valor aleatório, e cada instância vira um grupo que recebe tudo. Ou o contrário: dois serviços copiam o mesmogroup.ide dividem os eventos sem perceber. - O Redis Pub/Sub é usado para eventos de negócio, e tudo o que é publicado durante uma reinicialização do assinante se perde.
- A assinatura é criada pelo próprio consumidor ao iniciar, e os eventos publicados antes da primeira implantação dele não chegam.
- O assinante falha ao encontrar um campo desconhecido, e uma mudança compatível no publicador derruba o consumo.
- O publicador manda comandos disfarçados de evento ("enviar-email"), e o desacoplamento some: ele continua decidindo o que os outros fazem.
O próximo nível compara os intermediários para publicação e assinatura, o conteúdo dos eventos e como evoluir o formato sem quebrar ninguém.
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Publicação e assinatura em cinco minutos
Explique em no máximo cinco minutos de leitura o que é publicação e assinatura (pub/sub) no backend, para alguém que já entende o básico de filas de mensagens. Cubra só o essencial: a diferença entre uma fila ponto a ponto e um tópico com assinaturas, a diferença entre evento e comando, por que as instâncias de um mesmo serviço dividem uma assinatura, e a diferença entre assinatura durável e efêmera. Use o exemplo de uma loja em que estoque, e-mail e análise reagem ao evento "pedido criado". No final, me faça três perguntas curtas para eu verificar se entendi, e corrija as minhas respostas.
Assinatura, serviço e instância
Quero entender de vez a relação entre tópico, assinatura, serviço e instância em publicação e assinatura. Aja como um mentor de backend. Pergunte primeiro qual é a minha experiência e qual intermediário de mensagens eu conheço melhor. Use o exemplo de um tópico "pedidos" com dois serviços assinantes, estoque (três instâncias) e e-mail (uma instância). Conduza assim, uma etapa por vez: 1. Pergunte quantas vezes o evento "pedido 981 criado" deve ser processado por cada serviço. 2. Mostre o que acontece se cada instância tiver a sua própria assinatura. 3. Mostre o que acontece se estoque e e-mail compartilharem a mesma assinatura. 4. Mostre o desenho correto e explique os consumidores concorrentes dentro de uma assinatura. 5. Traduza o desenho para o RabbitMQ (troca e filas), o Kafka (grupos de consumidores) e um serviço gerenciado (SNS com SQS ou Google Pub/Sub). Espere a minha resposta em cada etapa antes de continuar.
Durável, efêmera e quem chega depois
Quero entender o que acontece com os eventos quando um assinante está fora do ar, e quando uma assinatura é criada depois que os eventos foram publicados. Explique: 1. A diferença entre assinatura durável e efêmera, com o Redis Pub/Sub e uma fila durável do RabbitMQ como exemplos. 2. O que acontece com um assinante lento em cada caso. 3. Por que, no RabbitMQ e na maioria dos serviços gerenciados, uma assinatura nova não recebe os eventos anteriores a ela. 4. Por que o Kafka é diferente, e o que fazem as opções earliest e latest de auto.offset.reset. 5. Por que a ordem de implantação entre publicador e assinante importa. Depois me apresente cinco situações, uma por vez, e pergunte se a assinatura deve ser durável ou efêmera: invalidar um cache local, baixar o estoque, enviar e-mail de confirmação, atualizar uma tela aberta no navegador e registrar a venda para análise. Corrija cada resposta.
O evento como contrato
Quero entender por que o formato de um evento vira um contrato entre serviços e como evoluí-lo sem quebrar ninguém. Pergunte em qual linguagem eu escrevo os assinantes. Explique: 1. Por que o publicador pode quebrar assinantes que ele nem conhece. 2. Quais mudanças são compatíveis e quais não são: acrescentar, remover, renomear, mudar o tipo e mudar o significado de um campo. 3. O princípio do leitor tolerante e como a biblioteca de JSON da minha linguagem trata campos desconhecidos por padrão. 4. A diferença entre um evento de notificação e um evento com o estado, e como cada um afeta o acoplamento. 5. Como publicar uma versão nova de um evento ao lado da antiga. Depois me mostre quatro mudanças num evento "pedido.criado" e pergunte, para cada uma, se ela quebra os assinantes. Corrija as minhas respostas.