Notificações começam como uma linha de código: quando um pedido é despachado, mande um e-mail. Um ano depois, a mesma empresa envia push, e-mail e SMS de uma dúzia de serviços, os usuários recebem o mesmo alerta três vezes, ninguém sabe dizer se um e-mail de redefinição de senha foi entregue e mensagens de marketing acordam as pessoas às 3 da manhã.
Um sistema de notificações resolve isso colocando um único serviço entre o código que decide que algo aconteceu e os provedores que entregam as mensagens. Este artigo projeta esse serviço: a API, as checagens que rodam antes de qualquer envio, a entrega por canal com retries e o rastreio. O diagrama perto do fim mostra o fluxo inteiro.
Requisitos
- Serviços de produto enviam uma notificação com uma única chamada, sem saber quais canais o usuário ativou.
- Push (iOS e Android), e-mail e SMS, com templates por idioma.
- Os usuários controlam canais e assuntos, e o horário de silêncio é respeitado.
- O mesmo evento nunca gera a mesma mensagem duas vezes.
- Toda mensagem tem um status: enviada, entregue, devolvida, com falha.
- Um provedor lento nunca atrasa os outros canais.
A API: eventos, não mensagens
Serviços de produto não devem montar e-mails. Eles enviam um evento com os fatos e uma chave de idempotência:
POST /notifications
{
"idempotencyKey": "order-8812-shipped",
"userId": "u_7",
"type": "order_shipped",
"data": {"orderId": "8812", "carrier": "UPS", "eta": "2026-10-30"}
}
O serviço de notificações cuida de todo o resto: quais canais esse tipo usa, qual template, qual idioma e se o usuário quer receber. Quando um time quer mudar o texto ou adicionar SMS a um tipo de notificação, nada muda no serviço de pedidos.
Antes de enviar qualquer coisa
Quatro checagens rodam na API, nesta ordem, antes que o trabalho chegue a um canal:
- Deduplicar. Guarde a chave de idempotência com um TTL e descarte repetições. Retries em serviços de origem são normais e não devem chegar aos usuários como duplicatas.
- Checar preferências. Consulte os canais do usuário, os cancelamentos por assunto e o horário de silêncio. Alertas de segurança, como redefinições de senha, costumam ignorar cancelamentos de marketing; marketing nunca ignora. Escreva essa regra.
- Limitar o volume. Limite as mensagens por usuário, por canal e por hora. Um bug que dispara 500 eventos não deve virar 500 notificações push.
- Renderizar. Escolha o template pelo tipo de notificação, pelo canal e pelo idioma do usuário, e preencha com os dados do evento.
O horário de silêncio precisa do fuso do usuário. Segure a mensagem e agende-a para o fim da janela em vez de descartá-la, a menos que ela seja urgente.
Uma fila e um pool de workers por canal
Depois das checagens, a API grava um job por canal na fila daquele canal. Filas separadas são a decisão mais importante do design: quando o provedor de SMS fica lento, os jobs de SMS se acumulam na própria fila enquanto push e e-mail continuam fluindo.
Os workers de cada canal chamam os provedores e lidam com as manias deles:
- Push. Dispositivos iOS passam pelo Apple Push Notification service e Android pela API HTTP v1 do FCM. Tokens de dispositivo envelhecem: o APNs responde 410 para um token que não está mais ativo e o FCM retorna um erro unregistered. Apague esses tokens, ou você continua pagando latência por mensagens que ninguém recebe.
- E-mail. Envie por um provedor e reporte devoluções e reclamações. Se você envia e-mail em massa, adicione os cabeçalhos de descadastro em um clique definidos na RFC 8058. As diretrizes para remetentes do Gmail exigem descadastro em um clique nas mensagens de marketing e de assinatura de quem envia mais de 5.000 mensagens por dia.
- SMS. O canal mais caro por mensagem e o mais regulado. Reserve-o para códigos e alertas urgentes, e confira as regras de cada país para onde você envia (este artigo não é aconselhamento jurídico).
Os workers repetem falhas temporárias com backoff exponencial e jitter, e movem um job para uma dead letter queue depois de um número fixo de tentativas. Falhas permanentes, como um endereço inválido, não são repetidas.
Rastreando a entrega
Os provedores informam o que aconteceu por webhooks ou códigos de resposta: aceita, entregue, devolvida, aberta. Grave cada mudança de status em um log de entregas indexado pelo ID da notificação. Assim o suporte responde em segundos se o usuário recebeu o e-mail de redefinição, e você consegue medir as taxas de entrega por canal e provedor.
As atualizações de status chegam fora de ordem e às vezes em dobro. Guarde-as como eventos com timestamp e derive o estado atual a partir deles, em vez de sobrescrever uma coluna de status.
Notificações dentro do app
Muitos produtos também mantêm uma caixa de entrada no app: o sininho com um contador. Trate-a como um quarto canal que grava em um banco de dados em vez de chamar um provedor. Guarde uma linha por usuário e notificação com uma flag de lida, indexe por usuário e data, e deixe o app buscar a página mais recente e a contagem de não lidas. Marque os itens como lidos em lote, para que um usuário que abre a caixa não dispare cinquenta escritas.
Contas de guardanapo
Com um milhão de usuários ativos por dia e cinco notificações por usuário por dia, o sistema envia 5 milhões de notificações por dia, cerca de 58 por segundo em média. O que importa são os picos: uma campanha ou um alerta de incidente pode enviar um milhão de mensagens em poucos minutos. Dimensione as filas para a rajada, deixe os workers esvaziá-las no ritmo que cada provedor permite e conte com os provedores limitando você antes que seus próprios workers fiquem sem capacidade.
O diagrama de arquitetura
Conteúdo do diagrama
- Product services
- Notification API
- Preferences
- Push queue
- Email queue
- SMS queue
- Push workers
- Email workers
- SMS workers
- APNs and FCM
- Email provider
- SMS provider
- Delivery log
- event
- opt-outs, quiet hours
- status
- webhooks
Como ler:
- Esquerda: os serviços de produto enviam eventos com chave de idempotência para a API de notificações. A API lê o armazenamento de preferências e roda as checagens listadas no bloco Markdown.
- Meio: cada canal tem a própria fila e o próprio pool de workers, então um provedor lento só trava o próprio canal.
- Direita: os workers chamam APNs e FCM, o provedor de e-mail e o provedor de SMS. As atualizações de status voltam ao log de entregas como arestas pontilhadas, porque chegam depois e de forma assíncrona.
O que colocar no diagrama
- As checagens e a ordem delas. Deduplicação, preferências, limites e renderização são a lógica real do sistema.
- Uma raia por canal. Se todos os canais dividem uma fila no seu diagrama, provavelmente dividem em produção também.
- O comportamento de retry e dead letter. Anote o backoff e o limite de tentativas em cada worker.
- O caminho de volta dos status. Sem ele, ninguém consegue depurar uma mensagem que sumiu.
Para o lado de filas e consumidores em mais detalhes, veja o artigo sobre arquitetura orientada a eventos. O diagrama de arquitetura de microsserviços mostra onde um serviço compartilhado como este fica entre os outros, e vale ler o artigo sobre mapeamento de dados para o GDPR antes de guardar dados de contato e logs de entrega.
Monte no Archboard
- Entre com Google ou GitHub e crie um diagrama.
- Adicione um Contêiner do pack Modelo C4 para os serviços de produto e outro para a API de notificações, e um Banco de dados abaixo da API para as preferências.
- Adicione três nós Fila do pack Dados, um por canal, e um Contêiner para os workers de cada canal.
- Adicione três nós Sistema externo do pack Modelo C4 para os provedores e um Banco de dados para o log de entregas.
- Desenhe as arestas de status pontilhadas e adicione um bloco Markdown do pack Básico com as checagens antes do envio.
O exemplo usa 14 nós de diagrama e fica dentro do plano Free.
Perguntas frequentes
Por que não deixar cada serviço enviar as próprias notificações? Porque aí preferências, horário de silêncio, deduplicação e rastreio de entregas precisam ser construídos em cada serviço, e eles divergem. Um único serviço de notificações aplica tudo do mesmo jeito em todo lugar.
Como evito notificações duplicadas? Exija uma chave de idempotência em toda requisição e guarde-a por um tempo. Uma chave repetida é confirmada e descartada.
Push, e-mail e SMS devem dividir uma fila? Não. Filas e pools de workers separados impedem que um provedor lento ou com falha atrase os outros canais.
Como lido com o horário de silêncio? Guarde o fuso e a janela de silêncio de cada usuário, e agende as mensagens não urgentes para o fim da janela em vez de enviá-las ou descartá-las.
Como sei que uma notificação foi entregue? Registre as respostas e os webhooks dos provedores em um log de entregas indexado pelo ID da notificação. “Entregue” significa coisas diferentes em cada canal, então registre os nomes de status do próprio provedor.