ADR-008 — Registries Declarativos em TypeScript
| Campo | Valor |
|---|---|
| Status | ✅ Aceito |
| Data | 2026-04-30 |
| Autores | Equipe OLP |
| Implementação | src/lib/navigation/sections-registry.ts, src/lib/tutorials/tutorials-registry.ts, src/constants/log-actions.ts, src/lib/papeis-vinculaveis.ts, src/lib/templates-constants.ts |
Contexto
Várias entidades do produto são metadados de UI/dominio que não mudam por usuário:
- Seções de sidebar e suas permissões necessárias
- Tutoriais disponíveis e em quais telas aparecem
- Catálogo de ações de log (label PT, modulo, severidade)
- Papéis vinculáveis a cada papel principal
- Tipos/públicos de templates de mensagem
Há duas opções óbvias: tabela no banco ou arquivo TS.
Decisão
Esses registries vivem em arquivos TypeScript versionados, não em tabelas. Cada registry expõe:
- Um array tipado (SSOT)
- Helpers
getById,getByCondicao,filterByPermission - Tipos derivados (
type SectionId = typeof SECTIONS[number]['id'])
// src/lib/navigation/sections-registry.ts
export const SECTIONS = [
{ id: 'dashboard', label: 'Dashboard', icon: Home, permissoes: ['ver_dashboard'] },
{ id: 'alunos', label: 'Alunos', icon: Users, permissoes: ['ver_alunos'] },
// ...
] as const;
export type SectionId = typeof SECTIONS[number]['id'];
export const getSection = (id: SectionId) => SECTIONS.find(s => s.id === id);Critério para "vai pra registry TS, não pra banco"
Vai pra registry TS quando o item:
- Não muda por escola/usuário (mesma sidebar pra todo coordenador, salvo permissões)
- Tem código associado (componente, ícone, handler) que precisa estar no bundle
- Mudança exige revisão de PR (segurança/UX)
Vai pra banco quando o item:
- Muda por escola/contexto (config de mural, séries da escola)
- É criado em runtime por usuário (turmas, aluno)
- Não tem código associado
Alternativas consideradas
A. Tudo no banco
Pros: editável sem deploy. Cons: tipo perdido (precisaria gerar tipos via script); ícones e componentes não cabem em coluna; mudança crítica (ex.: nova permissão de sidebar) sem revisão. Veredicto: rejeitado para metadados de UI.
B. JSON estático servido por edge
Pros: editável sem rebuild. Cons: latência extra; não tipa; perde grep-ability. Veredicto: rejeitado.
C. CMS headless
Pros: equipe não-técnica edita. Cons: dependência externa para o que muda 2x ao ano; sem revisão de código. Veredicto: overkill.
Consequências
Positivas
- Type-safe: errar
idda seção é erro de compilação - Grep-able:
rg 'id-da-secao'mostra onde é referenciado - PR review garante mudança consciente em UI/permissões
- Tree-shaking: items não usados saem do bundle
- SSOT real —
LOG_ACTION_OPTIONSalimenta filtros admin sem duplicação
Negativas
- Mudança exige deploy — não dá pra "ligar/desligar tutorial" em runtime
- Equipe de produto precisa abrir PR para adicionar item
Trade-offs
| Eixo | Custo | Benefício |
|---|---|---|
| Agilidade | Mudança = deploy | Revisão = qualidade |
| Tipagem | Boilerplate as const | Type-safety completa |
| Reuso de código + dado | Acopla código a metadado | É o objetivo (ícones, componentes) |
Conformidade
Como verificar:
- Cada registry tem
as constno array exportado - Tipos derivados (
type FooId = typeof FOOS[number]['id']) presentes - Mudança em registry passa por PR review
Sinais de violação:
- Sidebar/menu lendo lista de seções de tabela do banco
- Catálogo de log_actions duplicado em mais de um lugar (tem que ser apenas
LOG_ACTION_OPTIONS)
Débito técnico conhecido
- Algumas constantes (
templates-constants.ts) ainda misturam "tipo do template" (TS) com "conteúdo do template" (banco) — divisão clara existe mas pode ser refinada.
Referências
src/lib/navigation/sections-registry.tssrc/lib/tutorials/tutorials-registry.tssrc/constants/log-actions.tssrc/lib/papeis-vinculaveis.tsmem://architecture/log-actions-catalog-ssotmem://architecture/feature-documentation-ssot-standard