Pular para conteúdo

0012 — API CRUD como 2ª camada edge (ao lado de views)

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-07-08 · Decidido em: 2026-07-08

Contexto. O pacote ingest era um pacote-deus: misturava o pipeline de ingestão XADM (XadmController /api/v1/xadm → XadmService → XadmApplyService → parser + helpers) com os 13 controllers CRUD REST por-entidade (/api/v1/estoque, /contratos, …). Duas responsabilidades distintas no mesmo pacote. A decisão 0011 já havia estabelecido views como uma camada edge (apresentação, acima das features-motor, podendo orquestrá-las).

Decisão. Extrair os 13 controllers CRUD para um pacote próprio api, classificado como uma 2ª camada edge — ao lado de views:

  • Camadas edge (views = UI Thymeleaf; api = REST CRUD): ficam acima das features-motor e podem depender delas. Ex.: os controllers de api usam o EscritaPorChaveNatural de ingest.
  • XadmController e o pipeline de ingestão permanecem em ingest (não é CRUD por-entidade; é o endpoint de ingestão em massa, acoplado a XadmService).
  • Guarda ArchUnit: api entra no array FEATURES (portanto core/comum não dependem de api), mas fora de ENGINE_FEATURES — a regra de independência vale só para as 4 features-motor (ingest/powersync/push/export); edges podem depender delas.

Consequências / trade-offs.

  • ingest fica com uma responsabilidade (a ingestão XADM); a API CRUD tem casa própria.
  • Aceita-se api→ingest (o helper EscritaPorChaveNatural fica em ingest) — coerente com a classificação edge. Alternativa preterida: tratar api como feature-motor peer e mover o helper para comum; rejeitada por ser mais movimentação e porque a CRUD por-entidade não "compõe" features como a views faz — é uma superfície de dados, apresentação.
  • As rotas (@Controller("/api/v1/…")) não mudam — a extração é reorganização de pacote Java pura, sem mudança de contrato/comportamento (gate: 572 testes verdes).
  • Surge um segundo caso de "edge depende de engine" não-guardado por regra dedicada (o inverso, engine→edge, hoje não é violado). Uma guarda "engine ⊥ edge" foi deferida (custo×valor).