Interface BaseCrudService<E,D,ID,FD extends GenericFilterDTO>

Type Parameters:
E - tipo da entidade
D - tipo do DTO
ID - tipo do identificador
FD - tipo do DTO de filtro
All Known Implementing Classes:
AbstractBaseCrudService, AbstractReadOnlyService

public interface BaseCrudService<E,D,ID,FD extends GenericFilterDTO>
Interface base para operacoes CRUD, filtro, options e stats do starter.

Esta interface define o boundary canônico entre controllers genericos e implementacoes de dominio. Os controllers base assumem que conversoes entity -> DTO, resolucao de ordenacao padrao, options e capacidades estatisticas sejam fornecidas neste nivel, e nao espalhadas em adapters locais de frontend ou em controllers customizados.

Em projetos com JPA e associacoes LAZY, a forma suportada de manter esse boundary e herdar de AbstractBaseCrudService, que aplica semantica transacional adequada aos fluxos usados pelos controllers publicos da plataforma.

Capacidades cobertas

  • CRUD e listagem com ordenacao padrao.
  • Filtragem paginada e por cursor.
  • Projecoes leves para OptionDTO.
  • Option-sources e surfaces estatisticas canonicas.
  • Method Details

    • getRepository

      BaseCrudRepository<E,ID> getRepository()
    • getSpecificationsBuilder

      GenericSpecificationsBuilder<E> getSpecificationsBuilder()
    • getEntityClass

      Class<E> getEntityClass()
    • getOptionMapper

      default OptionMapper<E,ID> getOptionMapper()
      Retorna um OptionMapper padrão que projeta entidades para OptionDTO.

      Implementações podem sobrescrever para customizar o mapeamento. Quando não sobrescrito, o mapper padrão usa extractId(Object) para o id e computeOptionLabel(Object) para o label. O campo extra permanece null para manter o payload leve.

      Observação de performance: a computação do label utiliza reflexão apenas durante a projeção de opções (endpoints de options). Anotar a propriedade com @OptionLabel evita a varredura por heurísticas.

    • getDatasetVersion

      default Optional<String> getDatasetVersion()
      Retorna uma string representando a versão atual do dataset.

      Implementações podem sobrescrever este método para expor um valor que permita ao cliente invalidar caches quando os dados mudarem. Em cenários corporativos, uma implementação típica calcula um hash ou timestamp a partir da coluna updatedAt para que o frontend possa detectar mudanças sem precisar refazer consultas pesadas.

      
       // Exemplo de implementação
       @Override
       public Optional<String> getDatasetVersion() {
           return repository.maxUpdatedAt().map(Instant::toString);
       }
       
      Returns:
      versão do dataset, quando disponível
    • getGroupByStatsSupportMode

      default StatsSupportMode getGroupByStatsSupportMode()
      Informa se o recurso suporta a superficie canonica de group-by stats.
      Returns:
      modo de suporte; por padrao StatsSupportMode.DISABLED
    • getTimeSeriesStatsSupportMode

      default StatsSupportMode getTimeSeriesStatsSupportMode()
      Informa se o recurso suporta a superficie canonica de time-series stats.
      Returns:
      modo de suporte; por padrao StatsSupportMode.DISABLED
    • getDistributionStatsSupportMode

      default StatsSupportMode getDistributionStatsSupportMode()
      Informa se o recurso suporta a superficie canonica de distribution stats.
      Returns:
      modo de suporte; por padrao StatsSupportMode.DISABLED
    • getStatsFieldRegistry

      default StatsFieldRegistry getStatsFieldRegistry()
      Retorna o registro canonico de campos elegiveis para stats.
      Returns:
      registro de campos suportados pelo recurso
    • getOptionSourceRegistry

      default OptionSourceRegistry getOptionSourceRegistry()
      Retorna o registro de option-sources disponiveis para a entidade do recurso.
      Returns:
      registro de fontes derivadas; por padrao vazio
    • hasOptionSource

      default boolean hasOptionSource(String sourceKey)
      Verifica se existe uma option-source registrada para a entidade do recurso.
      Parameters:
      sourceKey - chave da fonte derivada
      Returns:
      true quando a fonte existe para a entidade atual
    • resolveOptionSource

      default OptionSourceDescriptor resolveOptionSource(String sourceKey)
      Resolve o descritor de uma option-source registrada para a entidade do recurso.
      Parameters:
      sourceKey - chave da fonte derivada
      Returns:
      descritor canonico da fonte
      Throws:
      UnknownOptionSourceException - quando a chave nao estiver registrada
    • findByIdMapped

      default <R> R findByIdMapped(ID id, Function<E,R> mapper)
      Recupera uma entidade pelo identificador e projeta o resultado dentro do contexto do service.
      Type Parameters:
      R - tipo projetado
      Parameters:
      id - identificador da entidade
      mapper - função de projeção
      Returns:
      resultado projetado
    • findAll

      default List<E> findAll()

      📋 Lista Todas as Entidades com Ordenação Padrão

      Retorna todos os registros da entidade aplicando automaticamente a ordenação definida via anotações DefaultSortColumn.

      🔄 Comportamento:

      • Aplica getDefaultSort() automaticamente
      • Se nenhum @DefaultSortColumn definido, retorna sem ordenação específica
      • Ideal para listagens completas onde ordem consistente é importante

      📋 Exemplo de Uso:

      
       // No Service:
       List<Funcionario> funcionarios = findAll();
       
       // SQL gerado (se @DefaultSortColumn(priority=1) em 'nome'):
       SELECT * FROM funcionarios ORDER BY nome ASC
       
      Returns:
      Lista de todas as entidades ordenadas conforme @DefaultSortColumn
      See Also:
    • findAllMapped

      default <R> List<R> findAllMapped(Function<E,R> mapper)
      Lista todas as entidades e projeta o resultado dentro do contexto do service.
      Type Parameters:
      R - tipo projetado
      Parameters:
      mapper - função de projeção
      Returns:
      lista projetada
    • findById

      default E findById(ID id)
      Recupera uma entidade pelo identificador.
      Parameters:
      id - identificador da entidade
      Returns:
      entidade encontrada
      Throws:
      jakarta.persistence.EntityNotFoundException - quando não encontrada
    • findAllById

      default List<E> findAllById(Collection<ID> ids)
      Recupera múltiplas entidades pelos seus identificadores.

      Retorna uma lista vazia quando a coleção de IDs é null ou vazia, evitando consultas desnecessárias ao banco de dados.

      Parameters:
      ids - coleção de identificadores a serem buscados
      Returns:
      lista de entidades correspondentes aos IDs fornecidos
    • findAllByIdMapped

      default <R> List<R> findAllByIdMapped(Collection<ID> ids, Function<E,R> mapper)
      Recupera múltiplas entidades pelos seus identificadores e projeta o resultado dentro do contexto do service.
      Type Parameters:
      R - tipo projetado
      Parameters:
      ids - coleção de identificadores
      mapper - função de projeção
      Returns:
      lista projetada
    • extractId

      default ID extractId(E entity)
    • save

      default E save(E entity)
      Persiste uma nova entidade.
      Parameters:
      entity - instância a ser salva
      Returns:
      entidade salva (com ID)
    • saveMapped

      default <R> R saveMapped(E entity, Function<E,R> mapper)
      Persiste uma entidade e projeta o resultado dentro do contexto do service.
      Type Parameters:
      R - tipo projetado
      Parameters:
      entity - instância a ser salva
      mapper - função de projeção
      Returns:
      resultado projetado
    • saveResultMapped

      default <R> BaseCrudService.SavedResult<ID,R> saveResultMapped(E entity, Function<E,R> mapper)
      Persiste uma entidade e retorna, no mesmo boundary do service, o ID persistido e a projeção correspondente.
      Type Parameters:
      R - tipo projetado
      Parameters:
      entity - instância a ser salva
      mapper - função de projeção
      Returns:
      resultado contendo o ID persistido e o corpo projetado
    • mergeUpdate

      default E mergeUpdate(E existing, E update)
      Permite combinar o estado atual com o payload de atualizacao antes da persistencia.

      O comportamento padrao devolve existing, mas implementacoes podem sobrescrever para copiar campos mutaveis, preservar relacionamentos ou aplicar regras de merge de dominio.

      Parameters:
      existing - entidade atual carregada do repositório
      update - entidade derivada do DTO de entrada
      Returns:
      entidade efetivamente usada no fluxo de update
    • computeOptionLabel

      default String computeOptionLabel(E entity)
      Tenta computar um label textual amigável para a entidade informada.

      Estratégia de resolução:

      1. Se existir método ou campo anotado com uma anotação chamada OptionLabel (qualquer pacote), usa seu valor convertido para String. Métodos são verificados antes de campos. Suporta herança.
      2. Caso contrário, tenta invocar (case-insensitive, na ordem) os getters getLabel, getNomeCompleto, getNome, getDescricao, getTitle.
      3. Se nada encontrado ou valor nulo/vazio, faz fallback para String.valueOf(id).

    • update

      default E update(ID id, E entity)
      Atualiza uma entidade existente.
      Parameters:
      id - identificador da entidade a atualizar
      entity - dados a serem mesclados e persistidos
      Returns:
      entidade atualizada
      Throws:
      jakarta.persistence.EntityNotFoundException - quando a entidade não existe
    • updateMapped

      default <R> R updateMapped(ID id, E entity, Function<E,R> mapper)
      Atualiza uma entidade e projeta o resultado dentro do contexto do service.
      Type Parameters:
      R - tipo projetado
      Parameters:
      id - identificador da entidade a atualizar
      entity - dados a serem mesclados e persistidos
      mapper - função de projeção
      Returns:
      resultado projetado
    • deleteById

      default void deleteById(ID id)
      Exclui uma entidade pelo identificador (ignora quando inexistente).
      Parameters:
      id - identificador da entidade a excluir
    • deleteAllById

      default void deleteAllById(Iterable<ID> ids)
      Exclui todos os registros correspondentes aos IDs fornecidos.
      Parameters:
      ids - Coleção de identificadores a serem removidos
    • findAll

      default org.springframework.data.domain.Page<E> findAll(org.springframework.data.domain.Pageable pageable)

      📄 Paginação com Ordenação Padrão Inteligente

      Retorna uma página de entidades aplicando ordenação padrão automaticamente quando nenhuma ordenação é especificada no Pageable.

      🧠 Lógica Inteligente:

      1. Se Pageable tem ordenação: Usa a ordenação especificada
      2. Se Pageable sem ordenação: Aplica getDefaultSort() automaticamente
      3. Se sem @DefaultSortColumn: Usa ordenação do banco (pode ser imprevisível)

      📋 Exemplos de Comportamento:

      
       // Caso 1: Com ordenação especificada
       Pageable pageable = PageRequest.of(0, 10, Sort.by("salario").descending());
       Page<Funcionario> page = findAll(pageable);
       // → SQL: ... ORDER BY salario DESC LIMIT 10
       
       // Caso 2: Sem ordenação (aplica @DefaultSortColumn automaticamente)
       Pageable pageable = PageRequest.of(0, 10);
       Page<Funcionario> page = findAll(pageable);  
       // → SQL: ... ORDER BY nome ASC LIMIT 10 (se @DefaultSortColumn no campo nome)
       

      🔗 Uso em Controllers:

      
       // O controller genérico delega a projeção ao service:
       @GetMapping("/all")
       public ResponseEntity<List<FuncionarioDTO>> getAll() {
           return ResponseEntity.ok(service.findAllMapped(this::toDto));
       }
      
       // URLs suportadas:
       GET /api/funcionarios/all?page=0&amp;size=10                    // Usa @DefaultSortColumn
       GET /api/funcionarios/all?page=0&amp;size=10&amp;sort=nome,asc      // Usa ordenação específica
       GET /api/funcionarios/all?page=0&amp;size=10&amp;sort=salario,desc  // Usa ordenação específica
       
      Parameters:
      pageable - Parâmetros de paginação e ordenação opcional
      Returns:
      Página de entidades com ordenação aplicada
      See Also:
    • filter

      default org.springframework.data.domain.Page<E> filter(FD filterDTO, org.springframework.data.domain.Pageable pageable)

      🔍 Filtro Avançado com Ordenação Padrão Inteligente

      Aplica filtros dinâmicos baseados em DTO de filtro e paginação, com ordenação padrão aplicada automaticamente quando não especificada.

      🔄 Fluxo de Processamento:

      1. Ordenação: Aplica getDefaultSort() se Pageable sem ordenação
      2. Filtros: Constrói JPA Specification baseada no FilterDTO
      3. Consulta: Executa query otimizada com filtros + paginação + ordenação

      📋 Exemplo de Uso Completo:

      
       // Entidade com ordenação padrão:
       @Entity
       public class Funcionario {
           @DefaultSortColumn(priority = 1)
           private String departamento;
           
           @DefaultSortColumn(priority = 2)  
           private String nome;
       }
       
       // DTO de filtro:
       public class FuncionarioFilterDTO extends GenericFilterDTO {
           @Filterable(operation = CONTAINS)
           private String nome;
           
           @Filterable(operation = EQUALS)
           private String departamento;
       }
       
       // No Controller:
       @PostMapping("/filter")
       public Page<FuncionarioDTO> filter(@RequestBody FuncionarioFilterDTO filterDTO, Pageable pageable) {
           return service.filterMappedWithIncludeIds(filterDTO, pageable, null, this::toDto);
       }
       

      🌐 Exemplos de Requisições:

       POST /api/funcionarios/filter?page=0&size=10
       Body: {"nome": "João", "departamento": "TI"}
       → SQL: SELECT * FROM funcionarios 
              WHERE nome LIKE '%João%' AND departamento = 'TI'
              ORDER BY departamento ASC, nome ASC  -- @DefaultSortColumn aplicada
              LIMIT 10
       
       POST /api/funcionarios/filter?page=0&size=10&sort=salario,desc  
       Body: {"nome": "Maria"}
       → SQL: SELECT * FROM funcionarios 
              WHERE nome LIKE '%Maria%'
              ORDER BY salario DESC  -- ordenação específica sobrescreve @DefaultSortColumn
              LIMIT 10
       

      🎯 Benefícios:

      • Flexibilidade: Combina filtros dinâmicos com ordenação inteligente
      • Performance: Queries otimizadas com índices corretos
      • UX Consistente: Resultados sempre organizados de forma lógica
      • Zero Config: Funciona automaticamente sem código extra
      Parameters:
      filterDTO - DTO com critérios de filtro anotados com @Filterable
      pageable - Parâmetros de paginação e ordenação opcional
      Returns:
      Página filtrada de entidades com ordenação aplicada
      See Also:
    • filterWithIncludeIds

      default org.springframework.data.domain.Page<E> filterWithIncludeIds(FD filter, org.springframework.data.domain.Pageable pageable, Collection<ID> includeIds)
      Aplica o filtro informado e injeta entidades adicionais no topo da primeira página.

      Os IDs fornecidos em includeIds são removidos da página retornada para evitar duplicação. Quando a página solicitada for a primeira (page = 0), entidades ausentes são buscadas via findAllById(Collection) e inseridas no topo na mesma ordem dos IDs fornecidos. Nas páginas subsequentes apenas a remoção de duplicados é realizada, evitando que itens sejam reexibidos em múltiplas páginas.

      Parameters:
      filter - critérios de filtro
      pageable - informações de paginação
      includeIds - IDs que devem aparecer no topo da primeira página
      Returns:
      página resultante com injeção opcional de entidades
    • filterMappedWithIncludeIds

      default <R> org.springframework.data.domain.Page<R> filterMappedWithIncludeIds(FD filter, org.springframework.data.domain.Pageable pageable, Collection<ID> includeIds, Function<E,R> mapper)
      Executa filtro com inclusão opcional de IDs e projeta o conteúdo dentro do contexto do service.
      Type Parameters:
      R - tipo projetado
      Parameters:
      filter - critérios de filtro
      pageable - informações de paginação
      includeIds - IDs que devem aparecer no topo da primeira página
      mapper - função de projeção
      Returns:
      página resultante projetada
    • filterByCursor

      default CursorPage<E> filterByCursor(FD filter, org.springframework.data.domain.Sort sort, String after, String before, int size)
      Executa o filtro utilizando paginação por cursor.

      Implementações devem aplicar uma ordenação estável e retornar os cursores codificados para navegação. O método padrão lança UnsupportedOperationException indicando que a entidade ainda não suporta paginação por cursor.

      Parameters:
      filter - critérios de filtro
      sort - ordenação estável a ser aplicada
      after - cursor para avançar na lista
      before - cursor para retroceder na lista
      size - quantidade de registros a recuperar
      Returns:
      página baseada em cursor
      Throws:
      UnsupportedOperationException - caso não seja implementado
    • filterByCursorMapped

      default <R> CursorPage<R> filterByCursorMapped(FD filter, org.springframework.data.domain.Sort sort, String after, String before, int size, Function<E,R> mapper)
      Executa o filtro por cursor e projeta o conteúdo dentro do contexto do service.
      Type Parameters:
      R - tipo projetado
      Parameters:
      filter - critérios de filtro
      sort - ordenação aplicada
      after - cursor para avançar
      before - cursor para retroceder
      size - tamanho da página
      mapper - função de projeção
      Returns:
      página por cursor projetada
    • groupByStats

      default GroupByStatsResponse groupByStats(GroupByStatsRequest<FD> request)
      Executes a canonical group-by aggregate over the filtered dataset.
      Parameters:
      request - stats request
      Returns:
      group-by stats response
      Throws:
      UnsupportedOperationException - when the resource does not support stats
    • timeSeriesStats

      default TimeSeriesStatsResponse timeSeriesStats(TimeSeriesStatsRequest<FD> request)
      Executes a canonical time-series aggregate over the filtered dataset.
      Parameters:
      request - stats request
      Returns:
      time-series stats response
      Throws:
      UnsupportedOperationException - when the resource does not support stats
    • distributionStats

      default DistributionStatsResponse distributionStats(DistributionStatsRequest<FD> request)
      Executes a canonical distribution aggregate over the filtered dataset.
      Parameters:
      request - stats request
      Returns:
      distribution stats response
      Throws:
      UnsupportedOperationException - when the resource does not support stats
    • locate

      default OptionalLong locate(FD filter, org.springframework.data.domain.Sort sort, ID id)
      Localiza a posição absoluta de um registro considerando um filtro e ordenação.

      Implementações devem retornar o índice zero-based do registro dentro do conjunto filtrado. O método padrão retorna OptionalLong.empty(), indicando que a entidade não suporta a operação.

      Parameters:
      filter - critérios de filtro
      sort - ordenação aplicada
      id - identificador do registro
      Returns:
      posição absoluta quando suportado
    • filterOptions

      default org.springframework.data.domain.Page<OptionDTO<ID>> filterOptions(FD filter, org.springframework.data.domain.Pageable pageable)
      Executa o filtro padrão e projeta cada entidade para OptionDTO usando o OptionMapper configurado.

      Em cenários com JPA e carregamento lazy, a expectativa é que a implementação concreta seja provida por AbstractBaseCrudService, para que a resolução do label ocorra dentro de transação.

      Parameters:
      filter - critérios de filtro
      pageable - informações de paginação
      Returns:
      página de opções reduzidas
    • byIdsOptions

      default List<OptionDTO<ID>> byIdsOptions(Collection<ID> ids)
      Busca entidades pelos IDs fornecidos e as projeta para OptionDTO, preservando a ordem da coleção de entrada.

      Em cenários com JPA e carregamento lazy, a expectativa é que a implementação concreta seja provida por AbstractBaseCrudService, para que a resolução do label ocorra dentro de transação.

      Parameters:
      ids - identificadores a serem buscados
      Returns:
      lista de opções na ordem solicitada
    • filterOptionSourceOptions

      default org.springframework.data.domain.Page<OptionDTO<Object>> filterOptionSourceOptions(String sourceKey, FD filter, String search, org.springframework.data.domain.Pageable pageable, Collection<Object> includeIds)
    • byIdsOptionSourceOptions

      default List<OptionDTO<Object>> byIdsOptionSourceOptions(String sourceKey, Collection<Object> ids)
    • getDefaultSort

      default org.springframework.data.domain.Sort getDefaultSort()

      📊 Constrói Ordenação Padrão Baseada em Anotações @DefaultSortColumn

      Escaneia a classe da entidade em busca de campos anotados com DefaultSortColumn e constrói automaticamente um objeto Sort para uso em consultas JPA.

      🔄 Algoritmo de Processamento:

      1. Escaneamento: Percorre todos os campos da entidade (incluindo herança)
      2. Filtragem: Identifica campos com @DefaultSortColumn
      3. Ordenação: Ordena por prioridade (menor valor = maior prioridade)
      4. Construção: Monta Sort.Order para cada campo

      📋 Exemplo de Uso:

      
       // Na entidade:
       @Entity
       public class Produto {
           @DefaultSortColumn(priority = 1, ascending = false)
           private LocalDateTime dataLancamento;
           
           @DefaultSortColumn(priority = 2, ascending = true)
           private String nome;
       }
       
       // Resultado deste método:
       Sort.by(
           Sort.Order.desc("dataLancamento"),    // prioridade 1
           Sort.Order.asc("nome")                // prioridade 2  
       )
       
       // SQL gerado:
       ORDER BY dataLancamento DESC, nome ASC
       

      ⚡ Performance:

      • Reflection Cache: Fields são cached pelo JVM
      • Lazy Execution: Só executa quando ordenação não especificada
      • Single Scan: Uma única varredura por classe

      🔗 Aplicação Automática:

      Este método é chamado automaticamente por:

      Returns:
      Sort baseado nos campos @DefaultSortColumn ou Sort.unsorted() se nenhum campo anotado
      See Also:
    • getNotFoundException

      default jakarta.persistence.EntityNotFoundException getNotFoundException()