Worker Report Routes — Análise e Recomendação
Contexto
Hoje 100% da computação de relatórios (Summary, Timeline, Sessions, Weekly, Membership, Trend, ClassEngagement, RiskDropout, CallCoverage, PeakFrequency, Audit) é feita no frontend — funções computeXxx() puras em app/src/modules/reports/ que recebem dados do IndexedDB (Dexie) via useLiveQuery. O worker (workers/) não tem uma rota sequer de reports. Esta análise avalia o custo e o benefício de criar GET /api/v1/reports/* no worker que executem essas computações no servidor (D1/SQLite).
Achados da exploração
1. D1 não tem os dados que os reports precisam
Toda função computeXxx() depende de ClassSession (ocorrência concreta de aula) e/ou ClassSlot (aula recorrente). Essas tabelas não existem no D1. A coluna class_session_id em attendance_events também não existe — o seed do worker (workers/src/routes/seed.ts:219) tem um comentário explícito: "coluna ainda não existe no D1".
Para reports server-side funcionarem, seria necessário:
- Criar tabelas
class_slotseclass_sessionsno D1 (migration 0005) - Adicionar
class_session_idemattendance_events(migration 0005) - Popular essas tabelas com dados existentes dos clientes
- Migrar todos os clientes existentes para o novo schema
Isso é semanas de trabalho, não dias.
2. Zero infraestrutura de leitura no worker
O worker tem endpoints de sync singular (POST /sync/event) e batch wrapper (POST /sync/events), além de POST /attendance/events). Não há GET /api/v1/students/:id/attendance ou qualquer endpoint de leitura de presenças. O módulo queries.ts tem apenas 4 funções — todas de auth/idempotency. Não há funções de agregação SQL.
3. Offline-first é conflitante
Relatórios precisam funcionar sem internet — é um requisito do PRD (item 5.3 "Offline continuity"). Se o report vier do worker, ele não funciona offline. A alternativa (cache local dos resultados) dobra a complexidade: precisaria invalidar cache quando novos eventos são criados offline, o que é essencialmente reconstruir o IndexedDB como cache — ou seja, o cenário atual.
4. D1 não é necessariamente mais rápido que IndexedDB
Para o volume atual (~50 alunos, ~30 eventos por aula, 3-5 slots por semana), o IndexedDB local processa todos os reports em <100ms. D1 adiciona latência de rede (30-200ms) mesmo em conexões boas. Só valeria a pena com centenas de alunos e milhares de eventos — cenário que o produto não tem no roadmap.
5. Segurança: ganho marginal
Hoje dados sensíveis (changePayload, justification) são criptografados com AES-GCM no cliente antes de ir para o IndexedDB. Processar reports no worker não mudaria isso — os dados já estão no cliente de qualquer forma. O ganho real de segurança viria de não expor agregações numa API pública sem autenticação, mas isso é verdade para qualquer endpoint.
Recomendação
Não criar rotas de report no worker. O custo de implementação é alto (semanas), o conflito com offline-first é fundamental, e o ganho para o volume atual é zero. Manter a computação 100% client-side.
O que fazer em vez disso
As funções computeXxx() já são puras e testadas. O lugar certo para investir em reports é:
- Testes — o módulo de reports já tem 9 testes (único módulo com cobertura). Expandir para cobrir edge cases.
- Performance — se um dia houver lentidão com muitos dados, otimizar as queries Dexie (índices,
useLiveQuerycomlimit) antes de pensar em worker. - Exportação — a função
computeSummarygera dados que o import/export poderia consumir para gerar CSVs de relatório. Isso é mais útil que server-side rendering.
Quando reconsiderar
- O produto escalar para 500+ alunos e 10k+ eventos/mês
- Surgir um requisito de dashboard compartilhado entre usuários (dados centralizados)
- Um cliente exigir LGPD com dados nunca processados no cliente (cenário improvável para o perfil do produto)
Até lá, o worker continua sendo apenas um sink de sincronia — função que ele cumpre bem.