Skip to content

ADR-008 — Registries Declarativos em TypeScript

CampoValor
Status✅ Aceito
Data2026-04-30
AutoresEquipe OLP
Implementaçãosrc/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'])
ts
// 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 id da 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 realLOG_ACTION_OPTIONS alimenta 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

EixoCustoBenefício
AgilidadeMudança = deployRevisão = qualidade
TipagemBoilerplate as constType-safety completa
Reuso de código + dadoAcopla código a metadadoÉ o objetivo (ícones, componentes)

Conformidade

Como verificar:

  • Cada registry tem as const no 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.ts
  • src/lib/tutorials/tutorials-registry.ts
  • src/constants/log-actions.ts
  • src/lib/papeis-vinculaveis.ts
  • mem://architecture/log-actions-catalog-ssot
  • mem://architecture/feature-documentation-ssot-standard