Plano de Reescrita do Core Resource/Surface/Action

Plano de Reescrita do Core Resource/Surface/Action

Status do documento

Este arquivo é um registro histórico de arquitetura. As fases 1 a 6 descritas aqui já foram implementadas no praxis-metadata-starter.

Leia este plano como:

Não leia este arquivo como backlog ativo de onboarding. Para a narrativa pública atual, use README.md, docs/architecture-overview.md e docs/guides/**.

Contexto

Nao ha compromisso com compatibilidade retroativa. A estrategia correta e substituir o nucleo conceitualmente errado, e nao empilhar uma camada “V2” sobre o legado.

Estado Atual do Starter

As Fases 1 a 6 deste plano ja foram implementadas no praxis-metadata-starter.

Estado consolidado atual:

Residuos conhecidos, nao bloqueantes para abrir o primeiro consumidor externo:

Gate Para Migrar o Primeiro Consumidor Externo

Antes de migrar um host real, o baseline esperado do starter e:

O proximo passo canonico, portanto, nao e mais evolucao interna do starter por fase. E a migracao controlada do primeiro consumidor externo sobre este baseline.

O ponto que permanece canonico no repo e:

O ponto que deve ser substituido e o core baseado em:

Tese Arquitetural

O starter deve passar a ter tres eixos distintos:

  1. resource-oriented Contrato estrutural canonico, query, create, update, partial update por intencao e schemas canonicamente resolvidos.
  2. surface-oriented Discovery semantico de formularios, views parciais, projecoes e UX contextual. Nao define payload; apenas referencia operacao real, schemaId e URL de /schemas/filtered.
  3. action-oriented Workflows e comandos de negocio explicitos, com endpoints tipados e DTOs proprios. Tambem nao define payload inline no catalogo.

Regras de Ouro

Problema Atual

Hoje o starter ainda concentra leitura e escrita no mesmo DTO central D, via AbstractCrudController<E, D, ID, FD> e BaseCrudService<E, D, ID, FD>.

Isso aperta o modelo para:

Estado-Alvo

Resource-oriented

Surface-oriented

Action-oriented

Registro Historico da Implementacao

As secoes abaixo preservam a sequencia historica que levou ao estado atual do starter. Elas nao devem ser lidas como backlog ativo; o backlog ativo para a proxima etapa esta nos guias e checklists pre-piloto.

Fase 1 - Extrair a resolucao canonica de operacao e schema

Objetivo

Criar a fundacao reutilizavel para:

Pacotes

org.praxisplatform.uischema.openapi
org.praxisplatform.uischema.schema

Classes novas

public record CanonicalOperationRef(
    String group,
    String operationId,
    String path,
    String method
) {}
public record CanonicalSchemaRef(
    String schemaId,
    String schemaType,
    String url
) {}
public interface CanonicalOperationResolver {
    CanonicalOperationRef resolve(HandlerMethod handlerMethod, RequestMappingInfo mappingInfo);
    CanonicalOperationRef resolve(String path, String method);
    Optional<CanonicalOperationRef> resolveByOperationId(String operationId);
}
public interface SchemaReferenceResolver {
    CanonicalSchemaRef resolve(
        String path,
        String method,
        String schemaType,
        boolean includeInternalSchemas,
        String tenant,
        Locale locale,
        String idField,
        Boolean readOnly
    );
}
public interface OpenApiDocumentService {
    String resolveGroupFromPath(String path);
    JsonNode getDocumentForGroup(String group);
    String getOrComputeSchemaHash(String schemaId, Supplier<JsonNode> payloadSupplier);
    void clearCaches();
}

Refatoracoes

Estado implementado na Lane 1

Resultado esperado

O repo ganha uma API interna unica para resolver operacao e schema canonicos, sem acoplamento ao controller documental.

Estado implementado no corte A da Fase 2

Este corte troca o boundary de service e mapeamento, separando ResponseDTO, CreateDTO e UpdateDTO, garante findAll() e preservacao de ordem em findAllById(), e move read-only para uma hierarquia query-only real. Os controllers legados ainda nao foram removidos nesta rodada.

Estado implementado no corte B da Fase 2

Este corte sobe o core HTTP novo sobre o boundary resource-oriented, preserva a superficie canonica de query/options/stats/schema, remove a semantica de escrita herdada da variante read-only e adapta o scanning de grupos OpenAPI para reconhecer a nova hierarquia. O legado AbstractCrudController / AbstractReadOnlyController permanece apenas como superficie transitoria enquanto consumidores como o praxis-api-quickstart ainda nao foram migrados.

No encerramento original da Fase 2, os proximos passos previstos eram:

Fase 2 - Reescrever o core resource-oriented

Objetivo

Trocar o boundary legado baseado em DTO unico por um core com separacao explicita entre leitura, criacao e atualizacao.

Pacotes

org.praxisplatform.uischema.mapper.base
org.praxisplatform.uischema.service.base
org.praxisplatform.uischema.controller.base

Classes novas

public interface ResourceMapper<E, ResponseDTO, CreateDTO, UpdateDTO, ID> {
    ResponseDTO toResponse(E entity);
    E newEntity(CreateDTO dto);
    void applyUpdate(E entity, UpdateDTO dto);
    ID extractId(E entity);
}
public interface BaseResourceQueryService<ResponseDTO, ID, FilterDTO extends GenericFilterDTO> {
    ResponseDTO findById(ID id);
    Page<ResponseDTO> filter(FilterDTO filter, Pageable pageable, Collection<ID> includeIds);
    CursorPage<ResponseDTO> filterByCursor(FilterDTO filter, Sort sort, String after, String before, int size);
    OptionalLong locate(FilterDTO filter, Sort sort, ID id);
    Page<OptionDTO<ID>> filterOptions(FilterDTO filter, Pageable pageable);
    List<OptionDTO<ID>> byIdsOptions(Collection<ID> ids);
    GroupByStatsResponse groupByStats(GroupByStatsRequest<FilterDTO> request);
    TimeSeriesStatsResponse timeSeriesStats(TimeSeriesStatsRequest<FilterDTO> request);
    DistributionStatsResponse distributionStats(DistributionStatsRequest<FilterDTO> request);
}
public interface BaseResourceCommandService<ResponseDTO, ID, CreateDTO, UpdateDTO> {
    SavedResult<ID, ResponseDTO> create(CreateDTO dto);
    ResponseDTO update(ID id, UpdateDTO dto);
    void deleteById(ID id);
    void deleteAllById(Collection<ID> ids);
}
public interface BaseResourceService<
    ResponseDTO,
    ID,
    FilterDTO extends GenericFilterDTO,
    CreateDTO,
    UpdateDTO
> extends BaseResourceQueryService<ResponseDTO, ID, FilterDTO>,
        BaseResourceCommandService<ResponseDTO, ID, CreateDTO, UpdateDTO> {
}

Controllers novos

Decisoes

Resultado esperado

O starter passa a suportar ResponseDTO, CreateDTO, UpdateDTO e FilterDTO como fronteiras distintas.

Fase 3 - Escrita parcial por intencao no eixo resource-oriented

Objetivo

Modelar multiplos formularios da mesma entidade sem transformar surface em contrato de escrita.

Pacotes

org.praxisplatform.uischema.annotation
org.praxisplatform.uischema.resource.intent

Anotacao

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ResourceIntent {
    String id();
    String title();
    String description() default "";
    int order() default 0;
}

Regra

Endpoints reais e tipados, por exemplo:

@PatchMapping("/{id}/profile")
@ResourceIntent(id = "employee-profile", title = "Editar perfil")
public ResponseEntity<RestApiResponse<EmployeeResponseDTO>> updateProfile(
    @PathVariable Long id,
    @Valid @RequestBody UpdateEmployeeProfileDTO dto
) { ... }

Resultado esperado

Formularios parciais viram operacoes canonicamente resource-oriented, com DTO nomeado por intencao.

Estado atual no starter

Fase 4 - Catalogo de surfaces (concluida)

Objetivo

Adicionar discovery semantico de formularios, views e projecoes, sempre por referencia a operacao canonica.

Estado atual da implementacao

Pacotes

org.praxisplatform.uischema.annotation
org.praxisplatform.uischema.surface
org.praxisplatform.uischema.controller.docs

Anotacao

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UiSurface {
    String id();
    SurfaceKind kind();
    String title();
    String description() default "";
    String intent() default "";
    SurfaceScope scope() default SurfaceScope.ITEM;
    int order() default 0;
    String[] tags() default {};
}

Modelo

Endpoints

Regras

Fechamento da fase

A Fase 4 esta formalmente encerrada no estado atual do starter.

Criterios de saida atingidos:

O proximo passo canonico deixa de ser hardening adicional de surfaces e passa a ser a Fase 5 de WorkflowAction.

Fase 5 - Catalogo de actions de workflow (concluida)

Objetivo

Catalogar e avaliar workflows de negocio explicitos.

Estado atual da implementacao

Pacotes

org.praxisplatform.uischema.annotation
org.praxisplatform.uischema.action
org.praxisplatform.uischema.controller.docs

Anotacao

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WorkflowAction {
    String id();
    String title();
    String description() default "";
    ActionScope scope() default ActionScope.ITEM;
    int order() default 0;
    String successMessage() default "";
}

Modelo

Endpoints

Execucao

Sempre por endpoint tipado real, por exemplo:

@PostMapping("/{id}/actions/approve")
@WorkflowAction(id = "approve", title = "Aprovar")
public ResponseEntity<RestApiResponse<EmployeeResponseDTO>> approve(
    @PathVariable Long id,
    @Valid @RequestBody ApproveEmployeeDTO dto
) { ... }

Regras canonicas

Fechamento da fase

A Fase 5 esta formalmente encerrada no estado atual do starter.

Criterios de saida atingidos:

O proximo passo canonico deixa de ser hardening adicional de actions e passa a ser a Fase 6 de capabilities unificadas.

Fase 6 - Capabilities unificadas

Objetivo

Expor um snapshot unico do que pode ser feito agora em uma colecao ou instancia.

Pacotes

org.praxisplatform.uischema.capability
org.praxisplatform.uischema.controller.docs

Modelo

Endpoints

Regra

Capabilities agregam:

Sem redefinir payload ou contrato.

Estado atual da implementacao

Fechamento da fase

A Fase 6 esta concluida no starter. O proximo passo canonicamente correto deixa de ser evolucao interna do praxis-metadata-starter e passa a ser a migracao do primeiro consumidor externo sobre o baseline completo resource + surfaces + actions + capabilities.

Estrutura de Pacotes Alvo

org.praxisplatform.uischema
|-- annotation
|-- mapper.base
|-- service.base
|-- controller.base
|-- openapi
|-- schema
|-- resource.intent
|-- surface
|-- action
|-- capability
|-- controller.docs
`-- configuration

Registro Historico do Primeiro Corte Recomendado

Escopo

Motivo

Esse corte tem o menor risco arquitetural e estabelece a fundacao canonica para:

Registro Historico do Segundo Corte Recomendado

Escopo

Motivo

Esse corte remove a principal limitacao estrutural do starter: o DTO central compartilhado entre leitura e escrita.

Divisao Operacional das Tarefas

Regra de Execucao

Lanes Recomendadas

Lane 1 - Fundacao canonica de OpenAPI/schema

Escopo:

Responsabilidade:

Lane 2 - Novo core resource-oriented

Escopo:

Responsabilidade:

Lane 3 - Migracao piloto de recurso

Escopo:

Responsabilidade:

Estado atual no starter:

Lane 4 - Discovery de surfaces

Escopo:

Responsabilidade:

Lane 5 - Discovery de actions

Escopo:

Responsabilidade:

Lane 6 - Capabilities unificadas

Escopo:

Responsabilidade:

Ordem Recomendada de Execucao

  1. Lane 1
  2. Lane 2
  3. Lane 3
  4. Lane 4
  5. Lane 5
  6. Lane 6

As lanes 4, 5 e 6 so devem comecar depois que as lanes 1 e 2 estabilizarem a fundacao canonica.

Protocolo de Subagentes

Agentes de implementacao

Cada agente de implementacao deve receber:

Template operacional minimo:

Responsabilidade desta lane:
- arquivos permitidos: ...
- arquivos proibidos: ...
- objetivo: ...
- validacao minima: ...
- nao criar contratos paralelos
- nao redefinir schema fora de /schemas/filtered
- nao reverter mudancas de outras lanes

Agente de QA obrigatorio ao fim de cada rodada

Toda rodada relevante deve terminar com um agente de QA separado da implementacao.

Perfil esperado:

Checklist obrigatorio do agente de QA:

Prompt minimo recomendado para o agente de QA:

Revise esta rodada como Staff Engineer especialista em Spring Boot.
Priorize:
1. integridade do codigo
2. integridade da logica de negocio
3. inconsistencias entre o plano e a implementacao
4. qualidade e sufiencia da documentacao
5. aderencia a uso corporativo

Procure principalmente:
- shadow API
- regressao de contrato canonico
- duplicacao estrutural
- endpoint generico onde deveria haver operacao tipada
- schema nao derivado de /schemas/filtered
- leitura/escrita ainda acopladas no mesmo DTO sem necessidade
- lacunas de teste e de migracao

Gate de Pronto por Rodada

Uma rodada so pode ser dada como pronta quando:

Remocoes Planejadas

Ao final da migracao do core:

Resultado Esperado

Ao final do plano:

Proximos Passos Canonicamente Corretos

Depois do fechamento das Fases 1 a 6, a trilha correta e:

  1. consolidar o pacote documental e operacional do piloto
  2. escolher um recurso piloto real em um consumidor externo
  3. migrar o recurso no consumidor usando o baseline resource + surfaces + actions + capabilities
  4. so depois decidir se vale abrir e2e-pg ou mais hardening transversal

Frase-Guia

resource define o contrato; surface organiza a experiencia; action expressa a intencao; /schemas/filtered continua sendo a verdade estrutural.