Skip to content

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:

  1. Criar tabelas class_slots e class_sessions no D1 (migration 0005)
  2. Adicionar class_session_id em attendance_events (migration 0005)
  3. Popular essas tabelas com dados existentes dos clientes
  4. 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 é:

  1. Testes — o módulo de reports já tem 9 testes (único módulo com cobertura). Expandir para cobrir edge cases.
  2. Performance — se um dia houver lentidão com muitos dados, otimizar as queries Dexie (índices, useLiveQuery com limit) antes de pensar em worker.
  3. Exportação — a função computeSummary gera 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.

Distribuído sob licença MIT.