architecture

# Architecture

## Papel do serviço

O Communications Service é o motor operacional de comunicações assíncronas da plataforma.

Direção-alvo: o serviço deve evoluir de motor operacional para central de
comunicação da plataforma. Isso inclui inbox, preferências, remetentes,
templates operacionais, delivery ledger, status, replies e enforcement de
supressões. A decisão está registrada em
[`ADR-002`](../adr/ADR-002-communications-domain-and-postgres.md) e detalhada em
[`communications-domain-roadmap.md`](communications-domain-roadmap.md).

Essa direção não muda automaticamente o caminho atual das notificações comuns.
Enquanto não houver estratégia equivalente para leitura/realtime, o fluxo
PocketBase continua sendo o contrato de compatibilidade. Comunicações da própria
Ailian também continuam podendo usar a identidade/domínio da Ailian.

Ele faz:

- ingestão de notificações via API interna
- scan de reminders
- enqueue e processamento de jobs
- retries com backoff
- dispatch de notification deliveries
- envio de push
- observabilidade técnica

## Providers de email

Estado atual: email usa Resend quando `RESEND_API_KEY` está configurada.

Direção-alvo: Resend deve ser tratado como adapter inicial, não como fronteira
de domínio. O Communications deve depender de um `EmailProviderAdapter` e
resolver `ProviderAccount`, `SendingPool` e `SenderIdentity` antes do envio.

Isso permite manter Resend para transacional da Ailian e, no futuro, conectar
SMTP, SMTP2GO, Amazon SES, múltiplas contas Resend ou outro provider sem mudar
o contrato dos produtores.

Implementação atual: `ResendProvider` usa `EmailProviderAdapter` para a chamada
técnica e `EmailSendingIdentityPort` para resolver remetente. Com
`DATABASE_URL`, identidades vêm de `communications.sender_identities`. Sem
Postgres, `EMAIL_SENDING_IDENTITIES` é o adapter estático. `RESEND_DEFAULT_FROM`
continua como fallback para comunicação da própria Ailian. Domínios de cliente
informados em `payload.email.from` precisam existir com `status=verified`,
escopo compatível e finalidade permitida.

Templates também ficam atrás de `EmailTemplateRegistryPort`. Com Postgres, o
catálogo vem de `communications.communication_templates` e versões publicadas.
Os templates locais atuais foram seedados com `artifact.renderer=local`, então
o provider depende da porta e a renderização local segue como engine transitória.

## O que fica no PocketBase

Estado atual: o PocketBase continua como fonte de verdade de domínio:

- `notifications`
- `notification_recipients`
- `notification_deliveries`
- `notification_preferences`
- `push_devices`
- `reminders`
- `reminder_deliveries`

Direção-alvo: esses dados deixam de ser tratados como domínio canônico do
PocketBase principal. O Communications deve passar a persistir seu domínio em
Postgres, mantendo PocketBase apenas como adapter/projeção transitória durante a
migração.

Implementação atual: Postgres é opcional via `DATABASE_URL`. O runtime cria um
client somente quando a variável existe e expõe o estado no `/ready`. A migração
inicial em `migrations/001_communications_domain_foundation.sql` cobre os
subdomínios novos, `migrations/002_seed_local_email_templates.sql` semeia os
templates locais atuais, mas a inbox e os deliveries atuais continuam no
PocketBase.

A primeira entrada de Postgres deve priorizar subdomínios novos ou mal cobertos
pelo PocketBase atual, como sender identities, sender domains, template registry,
delivery ledger ampliado e bulk runs. A inbox só deve migrar depois de uma
decisão explícita sobre realtime e consumo pelo front/BFF.

## O que fica no Redis

O Redis segura apenas o estado efêmero operacional:

- filas BullMQ
- locks de idempotência
- jobs recorrentes

## Fronteira de acesso

- browser não chama o Communications Service diretamente
- o serviço é interno
- rotas administrativas exigem `Authorization: Bearer <SERVICE_AUTH_TOKEN>`
- `n8n` e outros produtores trusted devem preferir `POST /internal/notifications`

## Ingestão de notificações

Contrato recomendado:

- produtor chama a API interna
- o serviço aplica `notification_preferences`
- cria `notifications`, `notification_recipients` e `notification_deliveries`
- enfileira imediatamente o que já estiver elegível

Isso reduz acoplamento com o schema do PocketBase e centraliza:

- defaults
- dedupe
- idempotência
- fan-out
- política de preferência por usuário

## Intervalos padrão

- `REMINDER_SCAN_EVERY_MS = 30000`
- `NOTIFICATION_SCAN_EVERY_MS = 30000`
- `JOB_ATTEMPTS = 5`
- `JOB_BACKOFF_BASE_MS = 5000`