Stats Response Schema Platform Fix Plan

Stats Response Schema Platform Fix Plan

Status do documento

Este arquivo virou registro histórico de uma correção de plataforma já implementada e validada. Ele é útil para auditoria técnica e para reconstruir a causa raiz original do problema de stats/*.

Não use este texto como fonte primária do contrato atual de stats; o contrato vivo está no código, nos testes e nos guias/specs sincronizados do starter.

Status em 2026-03-21

Objetivo

Corrigir em nível de plataforma a inconsistência entre:

A meta não é apenas fazer o frontend funcionar. A meta é restaurar um contrato canônico, verificável e consistente entre a documentação OpenAPI, os schemas derivados e os consumidores metadata-driven.

Diagnóstico Consolidado

Fato 1: os DTOs canônicos já existem

O starter já possui DTOs explícitos e padronizados para stats:

Também já existe o wrapper canônico RestApiResponse<T>.

Ou seja, o problema não é falta de modelagem de domínio.

Fato 2: o vínculo da operação com o schema de resposta está incompleto

Os endpoints de stats em AbstractCrudController publicam exemplos no 200, mas não publicam explicitamente o schema da resposta no conteúdo OpenAPI.

Na prática, isso permite que o OpenAPI final preserve:

mas perca o elo canônico:

Quando esse elo some, /schemas/filtered deixa de conseguir resolver schemaType=response.

Fato 3: a falha é sistêmica na família stats/*

A investigação mostrou que isso não afeta só vw-perfil-heroi/group-by. O padrão é repetido em toda a família stats/*.

Na superfície pública verificada, os casos sem schema de resposta estavam concentrados exclusivamente em stats/*.

Fato 4: /schemas/catalog e /schemas/filtered não estão operando com a mesma garantia contratual

Hoje o catálogo:

mas o filtered:

Isso significa que o catálogo pode expor um link contratual que o endpoint estrutural não consegue honrar.

Fato 5: há divergência na estratégia de resolução do documento OpenAPI

Os dois controladores não resolvem grupo/documento da mesma forma:

Mesmo que isso não fosse a causa raiz principal, isso já é um problema arquitetural porque permite inconsistência entre duas superfícies derivadas do mesmo contrato.

Objetivos da Correção

Objetivo Primário

Garantir que todo endpoint stats/* publique um response schema OpenAPI explícito, estável e resolvível por /schemas/filtered.

Objetivo Secundário

Garantir que /schemas/catalog e /schemas/filtered derivem suas respostas a partir de premissas compatíveis e testadas.

Objetivo de Plataforma

Garantir a seguinte propriedade:

Essa propriedade precisa virar contrato de plataforma, não convenção informal.

Princípios de Solução

1. Corrigir na origem do contrato

A solução correta começa na publicação OpenAPI dos endpoints de stats, não no frontend e não em fallbacks oportunistas do catálogo.

2. Evitar inferência onde o contrato pode ser explícito

findResponseSchema(...) pode ter heurísticas defensivas, mas stats não deve depender de inferência por nome ou estrutura parcial. O contrato deve ser explícito.

3. Tornar as superfícies derivadas coerentes entre si

/schemas/catalog e /schemas/filtered podem ter finalidades diferentes, mas não podem divergir sobre a existência do schema estrutural da mesma operação.

4. Proteger a correção com testes de consistência cruzada

Não basta testar cada endpoint isoladamente. É necessário testar a coerência entre:

Escopo da Correção

Incluído

Excluído do primeiro lote

Plano de Implementação

Fase 0: Congelar o contrato desejado

Definir explicitamente o contrato-alvo para cada operação de stats:

Também registrar a expectativa de documentação:

Saída esperada da fase:

Fase 1: Corrigir a publicação OpenAPI na origem

Ajustar AbstractCrudController para que cada @ApiResponse(responseCode = "200") de stats publique explicitamente o schema do wrapper correto.

Direção recomendada:

Ponto crítico:

Arquivos-alvo:

Fase 2: Endurecer a resolução de response schema em /schemas/filtered

Mesmo com a publicação corrigida, o ApiDocsController deve ficar mais robusto.

Trabalho recomendado:

Objetivo:

Arquivos-alvo:

Fase 3: Unificar a política de resolução entre catálogo e filtered

Hoje os dois controladores podem consultar documentos/grupos diferentes para a mesma operação.

Direção recomendada:

Opção preferida:

Arquivos-alvo:

Fase 4: Endurecer o contrato de /schemas/catalog

O catálogo não deve publicar schemaLinks.response cegamente.

Duas opções:

Recomendação:

Arquivos-alvo:

Fase 5: Cobertura de testes

5.1 Testes de ApiDocsController

Adicionar cenários para:

Também adicionar um teste negativo:

5.2 Testes de DomainCatalogController

Adicionar cenários para:

5.3 Teste de consistência cruzada

Adicionar um teste de integração do starter com esta propriedade:

Esse é o teste mais importante do lote porque fecha a regressão estrutural.

Arquivos-alvo:

Fase 6: Validação downstream no quickstart

Depois da correção no starter:

Validações mínimas:

Arquivos/superfícies-alvo:

Fase 7: Remoção de contingências em consumidores

Se houver fallback temporário no frontend para usar responseSchema do catálogo quando /schemas/filtered falhar, esse fallback deve ser tratado como contingência transitória.

Após a correção de plataforma:

Resultado da auditoria de 2026-03-21:

Backlog Técnico por Arquivo

Starter

Quickstart

Ordem Recomendada de Execução

  1. adicionar testes que reproduzem a falha atual
  2. corrigir a anotação OpenAPI em AbstractCrudController
  3. corrigir/fortalecer ApiDocsController
  4. alinhar DomainCatalogController
  5. adicionar teste de consistência cruzada
  6. validar no quickstart
  7. só então ajustar consumidores se necessário

Essa ordem é importante porque força a correção a nascer no contrato e não no consumidor.

Critérios de Aceite

Contrato OpenAPI

/schemas/filtered

/schemas/catalog

Consistência

Downstream

Riscos

Risco 1: SpringDoc gerar wrappers inesperados

Ao explicitar schema no @ApiResponse, o SpringDoc pode gerar nomes de wrapper diferentes dos atuais.

Mitigação:

Risco 2: perda involuntária de examples

Ao mexer em @Content, os exemplos podem desaparecer ou ser sobrescritos.

Mitigação:

Risco 3: resolver divergência entre docs públicos e internos

Se app.openapi.internal-base-url estiver apontando para uma superfície OpenAPI diferente da pública, a inconsistência pode continuar mascarada.

Mitigação:

Definition of Done

Próxima PR Recomendada

Escopo da primeira PR:

Escopo da segunda PR: