Pular para o conteúdo

ADR-0029 Email transacional

Padrão da empresa para o email das apps — templates React Email enviados com Nodemailer por SMTP, a partir de um job em segundo plano; o fornecedor SMTP é escolhido por projeto.

Decisão
React Email + Nodemailer por SMTPConstruir a app
Estado
Aceite
Data
Âmbito
Predefinição da empresa para todas as novas apps

Todas as apps enviam email. O Better Auth chama o nosso código para enviar mensagens de verificação, de reposição de palavra-passe e de magic link (ADR-0009), e os produtos acrescentam as suas notificações.

  • O fornecedor varia por projeto: o tenant Microsoft 365 ou Azure de um cliente, um relay que ele já paga, ou um escolhido por nós. Não há um fornecedor da empresa, nem vai haver
  • Fazemos self-host (ADR-0003), pelo que o deployment não traz nenhum serviço de email da plataforma
  • Os templates são código: recebem dados tipados, são revistos e têm de renderizar no Outlook e no Gmail, não só num browser
  • Um envio falhado não pode fazer falhar o pedido que o originou, e tem de ser repetido

O que está em aberto: como se escrevem os templates, como o email sai da app e o que fica para cada projeto.

1. A API HTTP e o SDK de um fornecedor (Resend, Postmark, SES, Azure Communication Services)

Seção intitulada “1. A API HTTP e o SDK de um fornecedor (Resend, Postmark, SES, Azure Communication Services)”

Prós

  • Webhooks de bounces e de queixas, estatísticas de entrega, listas de supressão
  • Sem ligações SMTP para gerir

Contras

  • Um SDK por fornecedor: um projeto que tenha de usar o fornecedor do cliente reescreve o módulo de email
  • Chaves, limites e funcionalidades variam por fornecedor, pelo que nada é um padrão da empresa

Prós

  • Todos os fornecedores falam SMTP: mudar de fornecedor é mudar variáveis de ambiente
  • O Nodemailer não tem dependências de runtime e cobre pooling, OAuth2 e assinatura DKIM
  • O React Email 6 é um só pacote: componentes, render() para HTML e texto simples, e um servidor de pré-visualização. A documentação inclui um guia para o Nodemailer
  • Os templates são React com props tipadas — a linguagem que a equipa já escreve (ADR-0001)

Contras

  • Sem informação de bounces ou queixas por SMTP; isso continua a depender do fornecedor
  • As repetições, os limites de ligações e a renovação de tokens OAuth2 são nossos
  • O React Email é mantido pela Resend, um fornecedor que podemos não usar

Prós: sem React no caminho do email; o MJML é maduro. Contras: uma segunda linguagem de templates, dados sem verificação de tipos e nada que o React Email não cubra já.

React Email para os templates e Nodemailer por SMTP para a entrega são o padrão da empresa. O fornecedor SMTP é escolhido por projeto.

Em concreto:

  • Pacotes: react-email 6.x (6.11.12 à data de escrita) e nodemailer 10.x (10.5.0). Desde a 6.0 o react-email contém os componentes, o render e a CLI — não instalar o @react-email/components
  • Fornecedor por projeto, definido apenas por variáveis de ambiente: host SMTP, porta, credenciais e endereço do remetente, declarados em server/env.ts (ADR-0026). Nada no código nomeia um fornecedor
  • TLS sempre: porta 465 com secure: true, ou 587 com requireTLS: true. Autenticação por palavra-passe, ou OAuth2 (auth.type: "OAuth2") onde o fornecedor o exija — o Microsoft 365 precisa de um token com o scope https://outlook.office.com/SMTP.Send
  • Um só módulo de email, src/server/mail/, é o único código que importa o nodemailer. Cria um transporter com pool e expõe um sendMail tipado. O código das funcionalidades recebe-o como parâmetro (ADR-0027)
  • Os templates vivem em src/server/mail/templates/ como componentes React Email com props tipadas. Todas as mensagens são enviadas com as duas partes: render(<Template />) para HTML e render(<Template />, { plainText: true }) para texto. Só componentes do React Email — os clientes de email não correm o nosso CSS, pelo que o shadcn/ui (ADR-0011) fica de fora. Pré-visualizar com email dev --dir src/server/mail/templates
  • O email é enviado a partir de um job (ADR-0028): o pedido coloca em fila, o worker renderiza e envia, e uma falha de SMTP é repetida com backoff
  • Os callbacks do Better Auth (sendVerificationEmail, sendResetPassword, magic links) colocam em fila e devolvem sem esperar — a documentação avisa que esperar pelo envio expõe o tempo de resposta
  • A entregabilidade configura-se por deployment com o fornecedor: SPF, DKIM e DMARC para o domínio de envio. A opção dkim do Nodemailer assina as mensagens quando o relay não o faz
  • Os logs levam o ID do utilizador e o nome do template, nunca o endereço do destinatário nem o corpo
  • Ler uma caixa de correio é opt-in por produto: IMAP através do imapflow 2.x, a correr num worker
  • Os testes passam um transporter criado com jsonTransport: true: nada sai do processo. SMTP não é HTTP, pelo que a regra do MSW do ADR-0014 não o vê

Os fatores decisivos: um só caminho de email que funciona com o fornecedor que cada projeto trouxer, templates na linguagem que já usamos e repetições a partir da fila que já corremos. Aceitamos tratar dos bounces e dos limites do fornecedor por projeto.

Mais fácil:

  • Mudar um produto para outro fornecedor é uma alteração de configuração
  • O email de autenticação e o email do produto partilham um módulo, um estilo de templates e uma política de repetições
  • Os templates têm verificação de tipos e pré-visualizam-se sem enviar

Mais difícil:

  • Os bounces, as queixas e os limites de envio variam por fornecedor e tratam-se por projeto
  • OAuth2 para o Microsoft 365 significa obter e renovar tokens nós próprios
  • O HTML de email é um ofício à parte: testar os templates em clientes reais, não só na pré-visualização

Do que abdicamos: das estatísticas, dos webhooks e das listas de supressão dos fornecedores como padrão. Aceite — um projeto que precise deles acrescenta a API do seu fornecedor ao lado deste módulo.