Sistema de notificações multicanal: modelos, tentativas e preferências
Projete um sistema de notificações multicanal para email, SMS e Telegram com modelos, rastreamento do status de entrega, tentativas de reenvio e preferências de usuário que permaneçam consistentes.

O que um sistema de notificações unificado resolve
Quando email, SMS e Telegram são construídos como recursos separados, as falhas aparecem rápido. O mesmo alerta acaba com redação diferente, tempo diferente e regras diferentes sobre quem o recebe. O time de suporte então persegue três versões da verdade: uma no provedor de email, outra no gateway de SMS e outra no log do bot.
Um sistema de notificações multicanal corrige isso ao tratar notificações como um produto único, não três integrações. Um evento acontece (redefinição de senha, fatura paga, servidor fora), e o sistema decide como entregá‑lo pelos canais com base em modelos, preferências do usuário e regras de entrega. A mensagem ainda pode ser formatada de forma diferente por canal, mas permanece consistente em significado, dados e rastreamento.
A maioria das equipes acaba precisando da mesma fundação, independentemente do canal com que começou: templates versionados com variáveis, rastreamento do status de entrega ("enviado, entregue, falhou, por quê"), retentativas e fallbacks sensatos, preferências do usuário com consentimento e horas silenciosas, e uma trilha de auditoria para que o suporte veja o que aconteceu sem adivinhar.
O sucesso parece monótono — no bom sentido. As mensagens são previsíveis: a pessoa certa recebe o conteúdo certo, no momento certo, pelos canais que autorizou. Quando algo dá errado, a solução é direta porque cada tentativa é registrada com um status claro e um código de motivo.
Um alerta de "novo login" é um bom exemplo. Você o cria uma vez, o preenche com os mesmos dados de usuário, dispositivo e localização, e então o entrega como email para detalhes, SMS para urgência e Telegram para confirmação rápida. Se o provedor de SMS expirar, o sistema reenvia conforme agendado, registra o timeout e pode fazer fallback para outro canal em vez de simplesmente perder o alerta.
Conceitos centrais e um modelo de dados simples
Um sistema de notificações multicanal fica manejável quando você separa "por que estamos notificando" de "como entregamos". Isso significa um pequeno conjunto de objetos compartilhados, mais detalhes específicos de canal apenas onde realmente diferem.
Comece por um evento. Um evento é um gatilho nomeado como order_shipped ou password_reset. Mantenha nomes consistentes: minúsculas, underscores e passado quando fizer sentido. Trate o evento como o contrato estável do qual templates e regras de preferência dependem.
A partir de um evento, crie um registro de notification. Este é a intenção voltada ao usuário: para quem é, o que aconteceu e quais dados são necessários para renderizar o conteúdo (número do pedido, data de entrega, código de redefinição). Armazene campos compartilhados aqui, como user_id, event_name, locale, priority e scheduled_at.
Então divida em messages por canal. Uma notification pode gerar de 0 a 3 messages (email, SMS, Telegram). Messages contêm campos específicos do canal, como destino (endereço de email, telefone, chat_id do Telegram), template_id e conteúdo renderizado (assunto/corpo para email, texto curto para SMS).
Por fim, rastreie cada envio como uma delivery attempt. Attempts incluem provider request_id, timestamps, códigos de resposta e um status normalizado. É isso que você inspeciona quando um usuário diz: "Eu não recebi."
Um modelo simples costuma caber em quatro tabelas ou coleções:
- Event (catálogo de nomes de evento permitidos e padrões)
- Notification (uma por intenção do usuário)
- Message (uma por canal)
- DeliveryAttempt (uma por tentativa)
Planeje idempotência desde cedo. Dê a cada notification uma chave determinística como (event_name, user_id, external_ref) para que retentativas de sistemas a montante não criem duplicatas. Se um passo do workflow for reexecutado, a chave de idempotência evita que o usuário receba dois SMS.
Armazene a longo prazo apenas o que precisa para auditoria (evento, notification, status final, timestamps). Mantenha filas de entrega de curto prazo e payloads brutos dos provedores apenas pelo tempo necessário para operar e depurar.
Um fluxo prático de ponta a ponta (passo a passo)
Um sistema de notificações multicanal funciona melhor quando trata "decidir o que enviar" separadamente de "enviar". Isso mantém seu app rápido e facilita lidar com falhas.
Um fluxo prático é assim:
-
Um produtor de eventos cria uma requisição de notification. Pode ser "password reset", "invoice paid" ou "ticket updated". A requisição inclui um user ID, tipo de mensagem e dados de contexto (número do pedido, valor, nome do atendente). Armazene a requisição imediatamente para ter trilha de auditoria.
-
Um roteador carrega preferências do usuário e regras de mensagem. Ele consulta preferências (canais permitidos, opt‑ins, horas silenciosas) e regras de mensagem (por exemplo: alertas de segurança devem tentar email primeiro). O roteador decide um plano de canais, por exemplo Telegram, depois SMS, depois email.
-
O sistema enfila jobs de envio por canal. Cada job contém uma chave de template, canal e variáveis. Jobs vão para uma fila para que a ação do usuário não fique bloqueada pelo envio.
-
Workers de canal entregam via provedores. Email vai para SMTP ou API de email, SMS para gateway de SMS, Telegram via seu bot. Workers devem ser idempotentes, de modo que reexecutar o mesmo job não gere duplicatas.
-
Atualizações de status fluem para um único lugar. Workers registram queued, sent, failed e, quando disponível, delivered. Se um provedor apenas confirma "accepted", registre isso também e trate de forma diferente de delivered.
-
Fallbacks e retentativas executam a partir do mesmo estado. Se o Telegram falhar, o roteador (ou um worker de retry) pode agendar SMS em seguida sem perder o contexto.
Exemplo: um usuário altera a senha. Seu backend emite uma requisição com o usuário e o IP. O roteador vê que o usuário prefere Telegram, mas as horas silenciosas bloqueiam à noite, então agenda email agora e Telegram pela manhã, acompanhando ambos sob o mesmo registro de notification.
Se estiver implementando isso no AppMaster, mantenha as tabelas de request, jobs e status no Data Designer e expresse a lógica de roteamento e retentativa no Business Process Editor, com o envio tratado de forma assíncrona para manter a UI responsiva.
Estrutura de templates que funciona entre canais
Um bom sistema de templates parte de uma ideia: você está notificando sobre um evento, não "enviando um email" ou "enviando um SMS". Crie um template por evento (Password reset, Order shipped, Payment failed) e, então, armazene variantes por canal sob o mesmo evento.
Mantenha as mesmas variáveis em todas as variantes de canal. Se o email usa first_name e order_id, SMS e Telegram devem usar exatamente os mesmos nomes. Isso evita bugs sutis em que um canal renderiza corretamente e outro aparece vazio.
Uma forma simples e repetível de template
Para cada evento, defina um pequeno conjunto de campos por canal:
- Email: subject, preheader (opcional), corpo HTML, fallback em texto
- SMS: corpo em texto simples
- Telegram: corpo em texto simples, mais botões opcionais ou metadados curtos
A única coisa que muda por canal é a formatação, não o significado.
O SMS precisa de regras especiais por ser curto. Decida desde o início o que acontece quando o conteúdo for longo e torne isso consistente: defina um limite de caracteres, escolha uma regra de truncamento (cortar e acrescentar ... ou remover linhas opcionais primeiro), evite URLs longas e pontuação extra, e coloque a ação principal cedo (código, prazo, próximo passo).
Locale sem copiar lógica de negócio
Trate o idioma como um parâmetro, não como um fluxo separado. Armazene traduções por evento e canal, depois renderize com as mesmas variáveis. A lógica de "Order shipped" continua a mesma enquanto assunto e corpo mudam por locale.
Um modo de visualização compensa: renderize templates com dados de amostra (incluindo casos extremos como um nome longo) para que o suporte verifique variantes de email, SMS e Telegram antes de liberá‑las.
Status de entrega em que se pode confiar e depurar
Uma notificação só é útil se você puder responder a uma pergunta depois: o que aconteceu com ela? Um bom sistema multicanal separa a mensagem que você pretendeu enviar de cada tentativa de entrega.
Comece com um pequeno conjunto de status compartilhados que signifiquem a mesma coisa em email, SMS e Telegram:
- queued: aceito pelo seu sistema, aguardando worker
- sending: uma tentativa de entrega está em andamento
- sent: encaminhado com sucesso para a API do provedor
- failed: a tentativa terminou com um erro passível de ação
- delivered: você tem evidência de que chegou ao usuário (quando for possível)
Mantenha esses status no registro principal da message, mas registre cada tentativa em uma tabela de histórico. Esse histórico é o que torna a depuração fácil: tentativa #1 falhou (timeout), tentativa #2 teve sucesso, ou SMS foi bem enquanto email continuou retornando bounce.
O que armazenar por tentativa
Normalize respostas de provedores para poder buscar e agrupar problemas mesmo quando provedores usam palavras diferentes.
- provider_name e provider_message_id
- response_code (um código normalizado como TIMEOUT, INVALID_NUMBER, BOUNCED)
- raw_provider_code e raw_error_text (para casos de suporte)
- started_at, finished_at, duration_ms
- channel (email, sms, telegram) e destination (mascarado)
Planeje sucesso parcial. Uma notification pode criar três mensagens de canal que compartilham o mesmo parent_id e contexto de negócio (order_id, ticket_id, alert_type). Se SMS for enviado mas email falhar, você ainda quer a história completa em um lugar, não três incidentes sem relação.
O que "entregue" realmente significa
"Enviado" não é "entregue". Para Telegram, você pode apenas saber que a API aceitou a mensagem. Para SMS e email, a entrega frequentemente depende de webhooks ou callbacks do provedor, e nem todos os provedores são igualmente confiáveis.
Defina o que significa delivered por canal desde o início. Use entrega confirmada por webhook quando disponível; caso contrário, trate delivered como desconhecido e continue reportando sent. Isso mantém seus relatórios honestos e as respostas ao suporte consistentes.
Retentativas, fallbacks e quando parar de tentar
Retentativas são onde sistemas de notificação frequentemente erram. Retentar muito rápido cria tempestades. Retentar para sempre cria duplicatas e dor de cabeça para o suporte. O objetivo é simples: tentar de novo quando há real chance de sucesso, e parar quando não houver.
Comece classificando falhas. Um timeout do provedor de email, um 502 do gateway de SMS ou um erro temporário da API do Telegram normalmente são retryable. Um endereço de email malformado, número de telefone inválido ou um chat do Telegram que bloqueou seu bot não são. Tratar esses casos da mesma forma desperdiça dinheiro e inunda logs.
Um plano prático de retentativas é limitado e usa backoff:
- Tentativa 1: enviar agora
- Tentativa 2: após 30 segundos
- Tentativa 3: após 2 minutos
- Tentativa 4: após 10 minutos
- Parar após uma idade máxima (por exemplo, 30–60 minutos para alertas)
Parar precisa de um lugar real no seu modelo de dados. Marque a message como dead-letter (ou failed‑permanently) quando exceder limites de retentativa. Guarde o último código de erro e uma mensagem curta para que o suporte aja sem adivinhar.
Evite envios repetidos após sucesso com idempotência. Crie uma chave de idempotência por mensagem lógica (frequentemente notification_id + user_id + channel). Se um provedor responder tardiamente e você reexecutar, a segunda tentativa deve ser reconhecida como duplicada e ignorada.
Fallbacks devem ser deliberados, não pânico automático. Defina regras de escalonamento com base em severidade e tempo. Exemplo: redefinição de senha não deve fazer fallback para outro canal (risco de privacidade), mas um alerta de incidente de produção pode tentar SMS após duas tentativas de Telegram falharem e depois email após 10 minutos.
Preferências do usuário, consentimento e horas silenciosas
Um sistema de notificações parece "inteligente" quando respeita as pessoas. A maneira mais simples é permitir que os usuários escolham canais por tipo de notificação. Muitas equipes dividem tipos em categorias como segurança, conta, produto e marketing porque regras e requisitos legais diferem.
Comece com um modelo de preferências que funcione mesmo quando um canal não estiver disponível. Um usuário pode ter email mas não número de telefone, ou pode não ter conectado o Telegram ainda. Seu sistema multicanal deve tratar isso como normal, não como erro.
A maioria dos sistemas acaba precisando de um conjunto compacto de campos: tipo de notificação (security, marketing, billing), canais permitidos por tipo (email, SMS, Telegram), consentimento por canal (data/hora, origem e prova se necessário), motivo de opt‑out por canal (escolha do usuário, email devolvido, resposta "STOP") e uma regra de horas silenciosas (início/fim mais fuso horário do usuário).
Horas silenciosas são onde sistemas frequentemente quebram. Armazene o fuso horário do usuário (não apenas um offset) para que mudanças de horário de verão não surpreendam ninguém. Quando uma mensagem é agendada durante horas silenciosas, não a falhe. Marque como deferred e escolha o próximo horário permitido para envio.
Padrões importam, especialmente para alertas críticos. Uma abordagem comum: notificações de segurança ignoram horas silenciosas (mas ainda respeitam opt‑outs obrigatórios), enquanto atualizações não críticas seguem horas silenciosas e escolhas de canal.
Exemplo: uma redefinição de senha deve sair imediatamente pelo canal mais rápido permitido. Um digest semanal deve esperar até de manhã e pular SMS a menos que o usuário explicitamente o tenha ativado.
Operações: monitoramento, logs e fluxos de trabalho do suporte
Quando notificações tocam email, SMS e Telegram, times de suporte precisam de respostas rápidas: Enviamos? Chegou? O que falhou? Um sistema multicanal deve parecer um lugar só para investigar, mesmo usando vários provedores por trás.
Comece com uma visão administrativa simples que qualquer um possa usar. Torne pesquisável por usuário, tipo de evento, status e janela de tempo, e mostre a tentativa mais recente primeiro. Cada linha deve revelar o canal, a resposta do provedor e a próxima ação planejada (retentativa, fallback ou parada).
Métricas que pegam problemas cedo
Falhas raramente aparecem como um único erro limpo. Monitore um pequeno conjunto de números e revise com regularidade:
- Taxa de envio por canal (mensagens por minuto)
- Taxa de falha por provedor e código de falha
- Taxa de retentativa (quantas mensagens precisaram de segunda tentativa)
- Tempo para entregar (queued a delivered, p50 e p95)
- Taxa de drop (paradas por preferências do usuário, consentimento ou máximo de retentativas)
Correlacione tudo. Gere um correlation ID quando o evento ocorrer (como "invoice overdue") e passe‑o por template, enfileiramento, chamadas aos provedores e atualizações de status. Nos logs, esse ID vira o fio para seguir quando um evento se desdobra em múltiplos canais.
Replay amigável ao suporte sem surpresas
Replays são essenciais, mas precisam de guardrails para não spammer pessoas ou cobrar duas vezes. Um fluxo de replay seguro geralmente significa: reenviar apenas um message_id específico (não todo o lote do evento), mostrar a versão exata do template e o conteúdo renderizado antes de enviar, requerer um motivo e registrar quem acionou o replay, bloquear replay se a mensagem já foi entregue a menos que forçado explicitamente, e aplicar limites de taxa por usuário e por canal.
Segurança e privacidade básicas para notificações
Um sistema multicanal toca dados pessoais (emails, telefones, chat IDs) e frequentemente abrange momentos sensíveis (logins, pagamentos, suporte). Presuma que todo corpo de mensagem e cada linha de log possa ser vista depois, e projete para limitar o que é armazenado e quem pode ver.
Mantenha dados sensíveis fora dos templates quando possível. Um template deve ser reutilizável e simples: "Seu código é {{code}}" é ok, mas evite embutir detalhes completos de conta, tokens longos ou qualquer coisa que permita tomar controle de uma conta. Se uma mensagem precisar incluir um código único ou token de redefinição, armazene apenas o necessário para verificá‑lo (por exemplo, um hash e um prazo de validade), não o valor em claro.
Ao armazenar ou logar eventos de notification, masque agressivamente. Um atendente de suporte geralmente precisa saber que um código foi enviado, não o código em si. O mesmo vale para números de telefone e emails: armazene o valor completo para entrega, mas mostre uma versão mascarada na maior parte das telas.
Controles mínimos que evitam a maioria dos incidentes
- Acesso baseado em função: apenas um conjunto pequeno de papéis pode ver corpos de mensagem e destinatários completos.
- Separe acesso de debug do acesso de suporte para que depurar não vire vazamento de privacidade.
- Proteja endpoints de webhook: use callbacks assinados ou segredos compartilhados, valide timestamps e rejeite fontes desconhecidas.
- Criptografe campos sensíveis em repouso e use TLS em trânsito.
- Defina regras de retenção: mantenha logs detalhados por pouco tempo e depois guarde apenas agregados ou identificadores hashados.
Um exemplo prático: se um SMS de redefinição de senha falhar e você fizer fallback para Telegram, armazene a tentativa, status do provedor e destinatário mascarado, mas evite guardar o link de redefinição em seu banco de dados ou logs.
Cenário exemplo: um alerta, três canais, resultados reais
Uma cliente, Maya, tem dois tipos de notificação ativados: Password reset e New login. Ela prefere Telegram primeiro, depois email. Ela só quer SMS como fallback para password resets.
Numa noite, Maya solicita uma redefinição de senha. O sistema cria um único registro de notification com um ID estável, depois expande em tentativas por canal com base nas preferências atuais.
O que Maya vê é simples: uma mensagem no Telegram chega em segundos com um código curto e tempo de expiração. Nada mais chega porque o Telegram teve sucesso e não foi necessário fallback.
O que o sistema registra é mais detalhado:
- Notification: type=PASSWORD_RESET, user_id=Maya, template_version=v4
- Tentativa #1: channel=TELEGRAM, status=SENT depois DELIVERED
- Nenhuma tentativa de email ou SMS criada (política: parar após o primeiro sucesso)
Mais tarde na semana, um alerta New login é disparado de um dispositivo novo. As preferências de Maya para esse alerta são Telegram apenas. O sistema envia Telegram, mas o provedor retorna um erro temporário. O sistema reintenta duas vezes com backoff, então marca a tentativa como FAILED e para (não há fallback permitido para esse tipo de alerta).
Agora uma falha real: Maya pede outra redefinição de senha enquanto viaja. Telegram é enviado, mas o fallback por SMS está configurado caso o Telegram não entregue em 60 segundos. O provedor de SMS dá timeout. O sistema registra o timeout, reintenta uma vez e a segunda tentativa tem sucesso. Maya recebe o código por SMS um minuto depois.
Quando Maya contata o suporte, eles pesquisam por usuário e janela de tempo e veem imediatamente o histórico de tentativas: timestamps, códigos de resposta do provedor, contagem de retentativas e o resultado final.
Checklist rápido, erros comuns e próximos passos
Um sistema de notificações multicanal é mais fácil de operar quando você pode responder rápido a duas perguntas: "O que exatamente tentamos enviar?" e "O que aconteceu depois?" Use este checklist antes de adicionar mais canais ou eventos.
Checklist rápido
- Nomes de evento claros e responsabilidade definida (por exemplo,
invoice.overduede responsabilidade do billing) - Variáveis de template definidas uma vez (obrigatórias vs opcionais, padrões, regras de formatação)
- Status acordados desde o início (created, queued, sent, delivered, failed, suppressed) e o que cada um significa
- Limites de retentativa e backoff (máx de tentativas, espaçamento, regra de parada)
- Regras de retenção (por quanto tempo manter corpos de mensagem, respostas de provedores e histórico de status)
Se fizer apenas uma coisa, escreva em palavras simples a diferença entre sent e delivered. Sent é o que seu sistema fez. Delivered é o que o provedor reportou (e pode ser atrasado ou inexistente). Misturar os dois confunde suporte e stakeholders.
Erros comuns a evitar
- Tratar sent como sucesso e inflacionar taxas de entrega
- Deixar templates específicos de canal divergirem até que email, SMS e Telegram se contradigam
- Retentar sem idempotência, causando duplicatas quando provedores timeoutam mas depois aceitam a mensagem
- Retentar para sempre, transformando uma falha temporária em um incidente barulhento
- Armazenar dados pessoais demais em logs e registros de status "só por precaução"
Comece com um evento e um canal principal, depois adicione um segundo canal como fallback (não como envio paralelo). Uma vez que o fluxo esteja estável, expanda evento a evento, mantendo templates e variáveis compartilhadas para que mensagens permaneçam consistentes.
Se quiser construir isso sem programar cada peça, AppMaster (appmaster.io) é uma opção prática para as partes centrais: modele eventos, templates e tentativas de entrega no Data Designer, implemente roteamento e retentativas no Business Process Editor e conecte email, SMS e Telegram como integrações mantendo o rastreamento centralizado.


