PoC de envio em massa de mensagens no WhatsApp: arquitetura e resiliência
Como construí uma PoC para envio automatizado de mensagens via WhatsApp usando Node.js, BullMQ, Redis e Next.js, enfrentando desafios com Baileys e implementando Clean Architecture.
Esta PoC nasceu de uma necessidade que tive: automatizar o envio de mensagens no WhatsApp de forma segura, resiliente e com observabilidade desde o primeiro momento. A ideia era simples, mas a execução exigiu estudo e adaptações de algumas libs como, o próprio Baileys.
Objetivo
O objetivo principal foi criar um sistema de envio de mensagens automáticas via WhatsApp, garantindo:
- Processamento assíncrono com filas
- Resiliência a falhas com retry e backoff exponencial
- Logs estruturados
- Escalabilidade horizontal
- Segurança na comunicação e controle por API Key
- Interface simples e funcional para usuários não técnicos
Segurança
Por se tratar de uma PoC não gastei muito tempo criando uma boa segurança para essa API, apenas adicionei o básico para validação:
- Validação de todas as variáveis de ambiente com Zod
- Middleware para controle de acesso por API Key
- Sanitização e validação de dados de entrada, como telefone e CSV
- Tratamento centralizado de erros e exceções com monitoramento no Sentry
Stack utilizada
Backend:
- Node.js com NX (Monorepo)
- BullMQ + Redis (fila de mensagens)
- RabbitMQ (mensageria entre workers)
- PostgreSQL + Drizzle ORM (persistência de logs)
- Sentry + Pino (observabilidade e logging)
- Zod (validação)
- Artillery (teste de carga)
Frontend:
- Next.js + Tailwind v4 + Shadcn UI
- Tanstack Query (integrações reativas)
- Dropzone (Upload de CSV)
Arquitetura
A arquitetura adotada segue os princípios da Clean Architecture, com influências de DDD e Hexagonal Architecture, favorecendo a separação de responsabilidades e a testabilidade. Mas claro é uma PoC, tem muita coisa pra ajustar:
- Camada de Domínio: (em evolução) agrupa regras e serviços puramente de negócio, independente de tecnologia
- Use Cases: orquestram a lógica de negócio e se comunicam com portas de entrada e saída (ex: enfileirar e publicar mensagens)
- Infraestrutura: implementa os detalhes (BullMQ, RabbitMQ, Redis, Drizzle, etc) conectando-se às interfaces
- Adapters/Ports: permitem plugar novos meios de entradas/saídas sem afetar regras de negócio
- Monorepo com Nx: facilita a organização entre frontend e backend
Essa abordagem permite escalar e manter o projeto organizado, mesmo com o crescimento de features e integrações.
Desafios com o Baileys
Um dos maiores desafios foi trabalhar com o baileys integrado ao Redis para salvar os authStates e permitir escalabilidade vertical e horizontal se necessário. Até existe uma lib baileys-store-redis mas não é mais mantida e o repositório está arquivado e também não utiliza ioredis, o que acabou limitando as possibilidades (como pipeline, reconexão inteligente e cache TTL).
Depois de muita pesquisa e erros, resolvi fazer um fork interno da lógica da store e reescrever a parte que usava redis@^3 para que funcionasse com ioredis. Essa versão foi embutida no projeto e customizada com melhorias:
- Cache de mensagens com limite controlado (Map interno)
- Registro de presença, reações e receives com TTL
Essa personalização abriu espaço para observabilidade e resiliência, algo que a versão oficial não entregava.
Interface web com Next.js + Shadcn
A interface está em desenvolvimento, mas já tentei fazer o mais básico possível inspirada diretamente na experiência do ChatGPT:
Tudo foi dividido em componentes reutilizáveis (ChatBubble, ChatInput, CsvDropzone, QRCodeBox, ChatWindow, etc), o que facilita a manutenção e o reuso em outros contextos.
Testes de carga com Artillery
Os testes de carga foram pensados para simular o uso real do sistema sob diferentes cenários. A estratégia que fiz foi:
- Warm-up: simular um pequeno volume de acessos iniciais
- Ramp-up: aumento progressivo de usuários para avaliar elasticidade
- Sustained Load: carga constante para avaliar estabilidade
- Spike: pico abrupto para testar resiliência
- Cooldown: redução gradual para encerrar
Também foram usados dados de um CSV simulando perfis (básico, pro, enterprise) de clientes, onde cada um teria linkado a seu plano uma quantidade de requests por minuto que poderia usar, garantindo que o sistema conseguisse responder com sucesso a cada um.
As métricas observadas incluíram taxa de erro, tempo médio de resposta e logs em tempo real no banco.
Conquistas até aqui
- Mensagens sendo enviadas com sucesso via WhatsApp
- Reconexão automática com regeneração de QR Code
- Retry com backoff exponencial no BullMQ
- Registro de logs no banco com status success ou error
- Worker isolado e leve, pronto para escalar
- Base em Clean Architecture com crescimento saudável
- Mínimo possível de segurança aplicada com Zod e controle por API Key
- Frontend desacoplado e moderno
- Organização via Nx Monorepo trazendo uma boa DevX
Próximos passos
Essa PoC ainda está em evolução. Tenho algumas ideias para as próximas implementações:
- Adicionar autenticação de verdade com API Key por cliente/ OAuth
- Criar sistema de template de mensagens
- Criar painel de métricas com mensagens por hora, falhas, entregas, etc.
- Persistir contatos vindos do CSV
- Validar os números do CSV antes de enfileirar
- Criar UI para reenvio de mensagens com erro
- Usar SSE/WebSocket para exibir status de envio em tempo real
Conclusão
Essa PoC começou com a necessidade de um sistema simples de disparo de mensagens via WhatsApp. Aos poucos, o projeto foi crescendo, enfrentando desafios com dependências e arquitetura, e está virando uma solução interessante e “robusta” para envio em escala, com resiliência e boas práticas.
A interface está ficando cada vez mais amigável, o backend sólido, e o projeto está só começando.
Vou continuar evoluindo e documentando cada passo.
Repositório: https://github.com/EliseuSantos/ezap-bulk-sender
Este artigo documenta como construí uma PoC para envio de mensagens em massa via WhatsApp. Para discussões técnicas ou contribuições, me encontre no LinkedIn ou GitHub.