Cursor Pagination / Keyset Plan

Cursor Pagination / Keyset Plan

Contexto

O praxis-metadata-starter hoje publica o endpoint POST /filter/cursor no AbstractCrudController, mas a implementação padrão do service ainda não materializa keyset pagination.

Na prática:

Isso explica por que apps consumidores, como o praxis-api-quickstart, respondem 501 no Render mesmo quando os demais filtros funcionam.

Direção de Plataforma

A solução correta de plataforma é implementar keyset pagination canônica no praxis-metadata-starter, e não deixar cada app consumidor reinventar a estratégia por recurso.

O starter já centraliza:

/filter/cursor deve seguir a mesma lógica: contrato e infraestrutura centralizados, com capacidade explícita e governada.

O Que o Spring Já Oferece

O ecossistema Spring já fornece primitives importantes para uma implementação sólida:

Spring Data Commons

Essas abstrações são a base mais adequada para scroll/keyset no nível do starter. O contrato público do Praxis pode continuar usando CursorPage, mas a implementação interna deve se apoiar nessas primitives.

JpaSpecificationExecutor + Scroll

Na stack atual do starter, o primeiro caminho a testar deve ser o que o Spring Data JPA já expõe via JpaSpecificationExecutor#findBy(..., q -> q.scroll(...)) com Specification.

Isso reduz a chance de criar infraestrutura redundante no v1.

Repository Fragments

Fragments customizados continuam sendo uma opção válida, mas devem entrar só se a prova de conceito com JpaSpecificationExecutor mostrar limitação real para o caso canônico do v1.

Spring Boot AutoConfiguration

O starter já usa auto-configuração para outros recursos. Cursor pagination deve seguir o mesmo padrão, com propriedades de governança e beans internos configuráveis.

Escopo Seguro do V1

O sucesso da implementação depende de restringir o v1 ao caso canônico da plataforma.

Requisitos do v1

Restrições deliberadas

Não tentar no v1:

Arquitetura Recomendada

1. Manter CursorPage como DTO externo

O contrato HTTP do Praxis já usa CursorPage. Isso pode ser preservado.

Internamente, o starter deve converter entre:

2. Executar keyset com a menor camada possível

Prioridade do v1:

Se essa via não bastar, aí sim criar algo como:

3. Normalizar o sort para um sort estável

O starter deve forçar ordenação determinística:

Exemplo:

ORDER BY publicadoEm DESC, id DESC

4. Cursor opaco

O Angular não deve conhecer a estrutura interna do cursor.

O starter deve serializar um payload opaco contendo, no mínimo:

Formato sugerido:

Assinatura/HMAC pode entrar depois, se houver necessidade de blindagem adicional.

5. Capability explícita

Nem todo recurso deve expor suporte real a cursor pagination.

Precisamos de um mecanismo canônico, por exemplo:

Valores possíveis:

No v1, AUTO só habilita quando o recurso for compatível com o contrato seguro.

Papel de Cada Camada

Repository Fragment

Service Base

Controller Base

Governança de Contrato

O v1 deve formalizar:

Exemplos de propriedades:

praxis.cursor.enabled=true
praxis.cursor.max-size=100
praxis.cursor.default-mode=auto

Estratégia de Implementação

Fase 1

Fase 2

Fase 3

Testes Necessários

Starter

Consumidor

Recomendação Final

Sim, implementar keyset canônico no praxis-metadata-starter é uma estratégia sólida, desde que o rollout seja deliberadamente restrito.

A taxa de sucesso é alta se o starter:

O erro seria tentar resolver no v1 “qualquer sort, qualquer join, qualquer projection”. O caminho robusto é um v1 pequeno, determinístico e plataformizado.