# Decisões arquiteturais

Registro das decisões tomadas na reconstrução deste sistema, incluindo os
pontos em que a implementação se afasta deliberadamente da lista de
tecnologias sugerida na especificação original (seção 6). Cada decisão
inclui o motivo e o que reconsiderar se as premissas mudarem.

## 1. Next.js full-stack, não backend Node.js separado + frontend React Native

**Decisão:** um único app Next.js 16 (App Router) serve tanto a API quanto
a interface web, acessada via navegador (responsiva, funciona bem em
celular) em vez de um app nativo React Native.

**Por quê:** o sistema não precisa de recursos exclusivos de app nativo
(câmera, Bluetooth, background tasks pesadas). Um único deploy, um único
runtime, menos superfície de configuração — mais simples de operar para a
TI do hospital, dado que o requisito real é "acessível via celular", não
"app instalável". A perda é notificação push mais limitada no iOS (ver
decisão 5) e nenhuma distribuição via loja de apps.

**Reconsiderar se:** o hospital exigir presença nas lojas de app, uso
offline robusto, ou integração com hardware do celular (câmera para outra
finalidade, Bluetooth direto) que só um app nativo resolve bem.

## 2. PostgreSQL único, não PostgreSQL + MongoDB

**Decisão:** um único banco PostgreSQL. A tabela `EventoAuditoria` (com
coluna `payload` `JSONB` para dados semi-estruturados) cobre o papel de
"log de eventos em tempo real" que a especificação original cogitava para
o MongoDB.

**Por quê:** volume de chamados de um hospital (mesmo grande) é ordens de
grandeza menor do que os cenários em que um segundo banco NoSQL para
"logs/eventos" se paga em complexidade operacional. Dois bancos = duas
estratégias de backup, duas superfícies de falha, duas tecnologias para a
equipe de TI manter. PostgreSQL sozinho, com os índices em `Chamado`
(`criadoEm`, `[tipo, status, criadoEm]`) e em `EventoAuditoria`
(`criadoEm`), atende as consultas de relatório sem problema nesse volume.

**Reconsiderar se:** o volume de eventos crescer por ordens de grandeza
(múltiplos hospitais, telemetria de alta frequência de muitos
dispositivos IoT) a ponto de as consultas de relatório ficarem lentas — aí
uma tabela de agregados diários materializada resolveria antes de precisar
de um segundo banco.

## 3. Socket.IO (WebSocket), não MQTT

**Decisão:** tempo real entre servidor e dashboards/dispositivos usa
Socket.IO sobre WebSocket (com fallback automático), não um broker MQTT
dedicado.

**Por quê:** os "clientes" deste sistema são navegadores (dashboards) e um
único endpoint HTTP para o botão de emergência (`POST
/api/chamados/emergencia`) — não uma frota de sensores IoT publicando em
alta frequência, que é o caso de uso onde MQTT brilha (pub/sub leve,
QoS configurável, ideal para redes instáveis de muitos dispositivos
batery-powered). Socket.IO integra nativamente com autenticação por
sessão (JWT do Auth.js) e não exige operar um broker adicional.

**Reconsiderar se:** o número de dispositivos IoT crescer para dezenas por
quarto/hospital com necessidade de QoS/offline queuing do lado do
broker — nesse ponto, migrar só a ingestão dos dispositivos (não os
dashboards) para MQTT seria razoável, mantendo Socket.IO para a UI.

## 4. Sem Grafana/Metabase — relatórios in-app

**Decisão:** a tela `/dashboard/supervisor/relatorios` (números por setor,
gráfico, tabela de pendentes) é construída no próprio Next.js com
Recharts, consultando `src/lib/relatorios/queries.ts` diretamente — não há
Grafana/Metabase implantado.

**Por quê:** os requisitos da especificação (chamados por setor, tempo
médio de resposta, pendentes, SLA) são atendidos por algumas queries bem
definidas; implantar e manter uma ferramenta de BI adicional (com seu
próprio banco de conexão, autenticação, deploy) não se justifica só para
isso.

**Reconsiderar se:** o hospital quiser dashboards ad-hoc para
usuários não-técnicos montarem seus próprios relatórios, ou cruzar dados
deste sistema com outros sistemas hospitalares — aí Metabase (mais simples
de operar que Grafana para esse perfil de usuário) conectado ao mesmo
Postgres seria o próximo passo natural, sem precisar mudar o schema.

## 5. Web Push, não notificação push nativa

**Decisão:** notificações push usam Web Push padrão (VAPID) via service
worker (`public/sw.js`), consequência direta da decisão 1 (sem app
nativo).

**Por quê:** é a opção que existe para apps web. Funciona nativamente em
Android/desktop (Chrome, Edge, Firefox). No iOS Safari, só funciona se o
usuário instalar o site como PWA ("Adicionar à Tela de Início") — uma
limitação da Apple, não deste projeto, e algo a comunicar explicitamente
para a equipe no treinamento (`plano-de-treinamento.md`).

**Reconsiderar se:** a experiência de push no iOS sem instalação de PWA
for um requisito não-negociável — nesse caso, um app nativo (ou wrapper
tipo Capacitor) seria necessário só para push, o que reabriria a decisão 1.

## 6. Sem Kubernetes — Docker Compose + proxy reverso

**Decisão:** deploy via Docker Compose (Postgres + app + Caddy para
HTTPS automático), não Kubernetes.

**Por quê:** a escala real (chamados de um ou poucos hospitais da rede
Unimed NF) não demanda orquestração de containers com autoscaling
horizontal. Compose é suficiente para alta disponibilidade básica (restart
automático de containers) e é muito mais simples de operar para uma
equipe de TI hospitalar sem um time de plataforma dedicado.

**Reconsiderar se:** o sistema for adotado por múltiplas unidades
simultaneamente com necessidade real de autoscaling, deploy multi-região,
ou já existir um cluster Kubernetes na Unimed NF que a TI prefira
reaproveitar — nesse caso, a containerização já feita (Dockerfile)
facilita a migração; não seria um recomeço do zero.

## 7. Auth.js v5 (beta), não uma solução própria de sessão

**Decisão:** autenticação por usuário/senha via Auth.js v5
(`next-auth@5.0.0-beta.32`), sessão JWT.

**Por quê:** apesar de a versão 5 ainda estar em beta no momento desta
reconstrução, é a integração padrão e mais testada para App Router do
Next.js, com suporte a middleware de borda e route handlers. A alternativa
de rolar autenticação própria (JWT manual + cookies) tem mais superfície
de erro em um sistema hospitalar do que depender de uma biblioteca com
grande adoção, mesmo em beta prolongado.

**Ação de acompanhamento:** monitorar o lançamento de uma versão estável
(GA) do Auth.js v5 e atualizar assim que disponível — não é uma decisão
definitiva, é a melhor opção pragmática hoje. Rodar `npm audit`
periodicamente enquanto isso (ver CI, `.github/workflows/ci.yml`).

## 8. WebRTC só com STUN, sem servidor TURN

**Decisão:** o áudio bidirecional paciente-posto (`/chamar/[numeroQuarto]`
↔ tela de atendimento da Enfermagem) usa WebRTC com um único servidor
STUN público (`stun:stun.l.google.com:19302`) pra descoberta de IP —
sem servidor TURN (retransmissão) próprio.

**Por quê:** STUN sozinho resolve a maioria das conexões dentro da
mesma rede Wi-Fi do hospital (o cenário mais comum: paciente e posto de
enfermagem na mesma rede local) sem nenhuma infraestrutura adicional pra
manter. Um TURN é outro serviço rodando 24/7, com seu próprio custo de
banda (todo o áudio passa por ele quando é necessário) — não se
justifica sem antes confirmar que falhas de conexão direta são um
problema real em produção.

**O que quebra sem TURN:** chamadas entre redes com NAT simétrico ou
firewall corporativo agressivo (comum em redes hospitalares
segmentadas/VLANs isoladas) podem não conseguir estabelecer conexão
direta. Sinalização (quem liga pra quem) sempre funciona — é só a
conexão de mídia P2P que pode falhar nesses casos.

**Reconsiderar se:** usuários relatarem chamadas que tocam mas não
conectam — sintoma clássico de STUN insuficiente. Solução: subir um
TURN próprio (ex.: `coturn`, mesmo docker-compose) ou usar um serviço
gerenciado (Twilio STUN/TURN, Cloudflare Calls). É uma mudança só de
configuração no array `ICE_SERVERS`
(`src/hooks/useChamadaAudioPaciente.ts` e
`src/hooks/useChamadaAudioStaff.ts`), não de arquitetura de sinalização.
