Duvida no padrao Repository/Service

uso aquele famoso pattern anemico: entity->repository->service->controller
minha duvida é em qual camada colocar queries que sao especificas da regra de negocio

resumindo:

  • devo colocar queries JPQL/SQL/etc que sao especificas da regra de negocio na camada de serviço ou encapsula-las em uma chamada de metodo no DAO/repository?

  • notei que se eu colocar na camada de servico fico com todo o negocio nessa camada somente (porem vou ter queries na camada de serviço … isso quebraria o pattern?)

  • se eu colocar no repositorio vou ficar com a regra de negocio nas 2 camadas , (porem a camada de serviço apenas chamaria um metodo no repositorio/DAO)

obrigado

São muitas queries muito diferentes? Se são poucas, você pode criar um método (com os parâmetros necessários) no repositório para cada query. Se são muitas, você pode criar apenas um esquema de critérios, passando um critério de query para o repositório. Tipo assim:

// camada de serviço
Criterio criterio = Criterio.predicado1(parametro1)
    .and(Criterio.predicado2(parametro2)
        .or(Criterio.Predicado3(parametro3)
    )
);
List<Entidade> entidades = repositorio.find(criterio);

// repositorio
sql = criterio.toSql();
// faz malabarismo com os prepared statement e etc
return query.getResultList();

Implementar algo assim não é muito trivial, alem de que o método toSql vai ter que estar de acordo com o banco que vc tá usando. Talvez exista alguma biblioteca que converte de SQL genérico para um vendor específico, não sei. Existe uma API de critérios no JPA, caso você queira usar esse framework.

Existem dois tipos de camada de serviço:

  • Fachada de Domínio: camada de serviço extremamente simples, a única coisa que faz é gerenciar transações e delegar chamadas para as entidades, que por sua vez possuem TODA a lógica de negócio;
  • Script de Operação: delega algumas chamadas para as entidades (que possuem lógica de negócio genéricas), mas também executa algumas operações pertinentes à transação (lógica de negócio muito específica àquela chamada), como envio de emails, notificações ou coisas do gênero.

Fonte

De qualquer forma, acho que é “errado” colocar SQL na camada de serviço. Mas se é um negócio simples, não acho que tem problema.

Qual o domino do seu sistema, enterprise ou de internet?

Esse é o primeiro passo pra saber que tipo de implementação você tem em mente.

obrigado pela resposta Barbosa

sim sao muitas queries… alias, melhor dizendo sao muitas regras diferentes… a maioria destas regras possui uma ou duas pequenas queries… nós encapsulamos muito bem nossas regras de negocio mas o problema é que estas regras precisam inicialmente de processamento Java e depois queries para confirmar no banco o resultado deste processamento

entao nos fazemos os processamentos necessarios no codigo Java (@service) e depois criamos queries JPQL para confirmar e persistir no banco o resultado destes processamentos (@service tbm)

nossa camada @repository atualmente esta bem generica (jah que utilizamos um Genericdao) e praticamente com os metodos de CRUD (pesquisar, pesquisar por ID, remover, remover uma lista e etc)

a grande questão é que estas queries fazem parte da regra… ai me pergunto:

deixo estas queries (que sao parte da regra) na camada service que é onde deve ficar as regras ou,
deixo estas regras no repository que é o lugar das queries do sistema?

obrigado @pfk66

é um sistema web… na verdade é um framework de persistencia que outros sistemas web vao usar aqui na empresa

estamos encapsulando as regras de negocio em um GenericDao/GenericService que vai ser utilizado por outros projetos aqui… estamos na verdade criando um framework… nesse framework contem o generic dao e service onde estao as regras qe eu citei acima

Baseado no padrão de responsabilidade, a sua camada de serviço iria acabar fazendo mais do que a sua obrigação. A responsabilidade por conhecer o acesso ao banco de dados, como fazer esse acesso e como realizar a comunicação é da camada DAO. Deixando as camadas mais separadas até com relação a responsabilidade melhora bastante a modularização, manutenibilidade e evolução do código.

Como mencionou que é um framework, ele pode sofrer constantes evoluções, pra oferecer sempre uma tecnologia mais atual. E caso nessas evoluções seja necessário trocar a origem do repositório de dados (exemplo: o repositório atual é um banco de dados, porém irá mudar para web service) o seu impacto será somente em uma camada, na de repositório, não irá precisar se preocupar com a camada de serviço, será somente manter as interfaces entre as camadas.

obrigado @Welsonfscarvalho

sim existe a possibilidade (e grande) de mudança no DAO/repo já que uma de nossas software houses tem clientes que utilizam EclipseLink ao inves de JPA

atualmente estamos fazendo em JPA mas jah temos que nos prevenir por que com certeza o framework sera utilizado com EclipseLink (e possivelmente outros, depende do cliente)

é uma das coisas que eu pensei… colocar as “queries de negocio” no @Repository fará com que eu não mexa na camada de servico quando esta mudança acontecer… porem como disse acima, vou ter uma parte da regra de negocio (queries) fora da camada de serviço e essa é a minha grande duvida

Enterprise né? Frameworks genéricos existem muito no ambiente corporativo, pra “facilitar” a vida do programador.

Pra mim framework de persistência é JPA, Hibernate. Se precisa criar outro framework encima do framework real, é porque tem algo de errado.

1 curtida

Isso é sempre recorrente na comunidade Java principalmente, querer fazer abstração da abstração, se preocupando mais em atender n diretrizes técnicas do que a funcionalidade para o cliente com resposta eficiente.

As diretrizes técnicas precisam fazer sentido. Por exemplo, se seu objetivo é ser capaz de atender o cliente de maneira eficiente, uma diretriz técnica que faz sentido é usar serviços e organizar equipes de forma que sejam capazes de tomar decisões sobre como melhor implementar e operar cada um desses serviços de maneira autônoma.

Claro, isso é mais fácil numa empresa de internet, com equipes pequenas e capazes.

Claro, tem fazer sentido para atender o cliente de forma eficiente, sem ficar se preocupando com um exagero de diretrizes técnicas somente por fins técnicos, mais uma abstração em cima da abstração do JPA/Hibernate e por ai vai… É por isso que linguagens como Go surgem para balançar um pouco esses exageros.

Mas se essas empresas resolverem usar go, será que vai fazer alguma diferença pra elas?

O software produzido reflete a estrutura organizacional da empresa, e não o contrário. A menos que elas mudem a estrutura organizacional, o software que elas produzem continuará sendo o mesmo, independente da linguagem que foi escrito.

Se os vícios forem os mesmos não faz diferença a tecnologia. Não quis dizer de “mudar para Go”. Mas de ser um advento importante para voltar a uma cultura simples, não impedindo de continuar com as mesmas plataformas de desenvolvimento.

Concordo com diretrizes técnicas bem trabalhadas para agregar mais valor para o cliente. Porém está ficando cada vez mais comum, empresas focadas em soluçoes corporativas terem camadas abstratas para uso de alguns frameworks, principalmente para abstrair requisitos de negócio mais genéricos, quanto a essa estrutura sou a favor, pois facilita em alguns pontos o desenvolvimento.

Mas já vi casos onde há mesmo uma abstração total do framework, na ocasião foi criado componentes de abstração para uso do JPA, Spring e até mesmo do AngularJS. Quanto a esse tipo de estrutra já vejo uma possibilidade muito alto para erros e restrições de uso do framework, impossibilitando um bom andamento do desenvolvimento.

1 curtida

Oi @jaabax!

Achei que o exemplo dado pelo colega @lvbarbosa tinha respondido seu questionamento, mas talvez não tenha ficado claro.

Se você precisa gerar muitas queries não há problema fazer com que o repositório receba um objeto criado no serviço que represente essa query.

Esse padrão é descrito no eea como QueryObject, já no DDD existe uma abordagem parecida, porém mais focada na regra de negócio em vez de ser um mero descritor do SQL, onde o padrão é chamado Specification mas que também é passado para o Repository com as “especificações” das entidades buscadas (um dos usos desse padrão).

No JPA existe uma implementação do QueryObject na Criteria API que pode ser usado na camada de serviço para criar suas queries complexas. Como o JPA é uma especificação então não importa qual a implementação você vai usar se Hibernate ou EclipseLink (se bem que se você for mudar de um para outro provavelmente vai ter problemas com LazyLoading etc… mas que não vem ao caso para o que foi questionado).

Não estou dizendo para escrever SQL no serviço senão não faria sentido existir o repositório, mas não há problema algum em colocar a construção dessa query no serviço desde que usando um objeto que abstraia o SQL para que o serviço não seja dependente de banco e seja fácil de testar (em aplicações reais às vezes é preciso até colocar lógica no banco, coisa que é horrível do ponto de vista da manutenção).


Para ficar bem claro e fácil a resolução desse tipo de problema no futuro eu recomendo a leitura do [eea] (https://martinfowler.com/books/eaa.html) (apesar de longo basta ler as primeiras 100 páginas conforme dito pelo próprio autor, o resto é para consulta quando precisar implementar).

O DDD também é essencial porém a leitura é bem pesada. Tem que ler no mínimo umas 3 vezes pra entender essa bagaça (ou não entender nunca como no meu caso rsrs).

E também, pra tirar férias de vez da família, OOA&D mas esse é de boa leitura rsrs.


Se acha que isso é overengineering eu concordo que pode ser sim, depende do caso. Isso deveria ser usado apenas se realmente necessário. O que a maioria das aplicações grandes e enterprise o são. Não existe solução simples para problemas complexos.

É a solução mais fácil, não precisa lidar com DDD, nem contratar domain experts de R$10.000,00/mês pra entender o problema e então definir os contextos, porque pode ter certeza não existe regra de negocio genérica. :slight_smile:

Em vez disso, um digitador de R$1.200,00 pode ctrl+f e contar os resultados. Se aparecer mais de um é regra genérica e vai pro “framework interno”.

Não faria diferença mudar. Devido a estrutura organizacional, esse tipo de empresa ia acabar reescrevendo AbstractSingletonProxyFactoryBean em Go.

Empresas de internet sim podem adotar essa cultura mais simples, que reflete numa arquitetura mais parecida com microservice.

Concordo, como tinha falado, se os vícios continuarem, o excesso de engenharia continua. Só quis dizer pelo lado disso ajudar a trazer um movimento com cabeças nascendo nesse sentido.