Estudando sobre Domain Driven Design aprendi sobre Repository, Criteria, etc. Juntamente com os Data Mappers (Famosos DAOs em Java) catalogados por Fowler, percebi que o Hibernate/JPA aparentemente utilizou-se dessa ideia para a criação do seu framework.
Concluí então, que utilizando Hibernate não preciso criar RepositorioXXX ou Criterias ou DAOs já que o Hibernate tem tudo isso a disposição através da Session/EntityManager, Criteria, Dialeto do Banco de Dados e a geração automática das instruções SQL.
Eu sei que a Session/EntityManager é muito genérico, serve como repositório de todos os objetos, diferente de um RepositorioCliente por exemplo, mas mesmo assim, ainda vou precisar em algum cenário criar classes para o padrão Repository quando utilizo Hibernate ?
Utilizando a Session/EntityManager na Fachada de Serviços ou até mesmo dentro de alguma Entity(DDD) em alguma operação de negócio que precise de dados, ao meu ver estou utilizando implicitamente todos esses padrões e implementando da maneira correta.
Neste caso da a impressão mesmo de apenas uma camada a mais, agora vc já parou para pensar em casos mais complexos?
rodrigoy
Eu uso o repository só para buscas… para persistir e para mergear uso o EntityManager… pra mim o EM, já é uma abstração…
xandroalmeida
Por isso eu tenho um AbstractRepository com as principais funções de CRUD. Ai se não preciso nada além do CRUD, basta extenter e não fazer mais nada.
o problema de você não ter repository é correr o risco de duplicar código. Por exemplo, você em várias partes do sistema tem que obter o usuário à partir do email. Se você não tem um Repository de Usuários, terá que construir consultas para buscar o usuário pelo email por varias partes do sistema.
R
ronybrand
Utiliza Spring com HibernateDaoSuport vai ficar bem mais facil de trabalhar com Hibernate, ai ele controla Session/Entity Manager para vc!
xandroalmeida
Os Repositorys acabam sendo usados para buscas mesmo, acho que é esta a idéia.
Como eu disse, eu prefiro ter um AbstractRepository<T> com os métodos de CRUDs básicos, para evistar o uso do EM diretamente, mas também não vejo problemas nisso.
Emerson_Macedo
Até entendi mas ai você ficaria com repositórios incompletos e/ou dupicados. Ex: RepositorioConta só para pesquisa nas contas e o EntityManager para o crud. Não ficaria confuso essas classes no seu Domain Model ? Teoricamente caso eu queira utilizar o seu RepositorioConta eu vou procurar uma opção de salvar minha conta ali, entende ?
Acho que ainda não ta legal …
rodrigoy
Você acha que o EntityManager é uma classe do Domain Model ou da Infra?
nbluis
O Repository faz além de buscas com de filtros sql.
Do tipo.
Busque todas as casas com 3 quartos onde o primeiro quarto tenha a cor rosa e a cozinha tenha uma janela para um bosque com uma macieira e um cavalo branco (apenas um cavalo) .
Uma busca dessas certamente não vai poder ser feita apenas com joins e filtros no where…
Aí que o repository entra efetivamente.
Entende ??
Emerson_Macedo
@rodrigoy
Você tocou no ponto. Tecnicamente EntityManager seria um Repository so que genérico. Porém, a implementação que o Hibernate/JPA fez fica com cara de infra. Então fica parecendo uma mistura das duas coisas, ou seja, responsabilidade demais. O cheiro de infra é tão grande por causa dos métodos persist(), merge(), ao invés de um adicionar() por exemplo.
Mas de qualquer maneira ainda acho meio esquisito você ter um Repository que não tem as opções adicionar() e remover() por exemplo, ficando somente com consultas. Ta certo que poderia ter delegando para o EntityManger fazer isso mais sei lá, parece uma coisia meio bacalhau entende ? O que você acha ?
@nbluis
Isso foi falado anteriormente pelo rodrigoy, porém o problema é justamente este. Um Repository segundo a definição é onde a Entity fica guardada então fica estranho eu ter um RepositoryConta para buscar uma conta e depois quando eu for adicionar uma nova conta eu não poder usar esse Repository. Deveria se único para esse Entity.
nbluis
Como assim não pode chamar o repository?
Vc chama o repository para adicionar, remover ou qualquer coisa dessas.
Já parou para pensar no fato de que nem todas as suas entidade podem ter o adicionar(persist) ?
O Repository é quem controla isso.
faelcavalcanti
Para as consultas você pode utilizar Restrictions, tanto como filtro, como ordenação!
Emerson_Macedo
Não quis dizer que não pode, só quis dizer que vai ser apenas uma delegação, ou seja, um método quase burro.
Correto. O que acontece é que o EntitManager me dá todas essas operações e esse Repository sugerido como ficaria sendo que o Hibernate já tem a sua API Criteria ? Teoricamente Quando implementamos um Repositpry temos Criteria também. Não já é isso que o Hibernate faz ?
A
andre_salvati
Acho que não. Os DAOs ainda são válidos, já os Repositorios nem tanto…
Fica meio estranho acessar o EntityManger de dentro de Entities.
Alessandro_Lazarotti
O seu repositório pode cuidar de operações mais complexas do que simplesmente fazer um CRUD ou uma busca simples. Para formar um agregado, por exemplo, você pode realizar mais de uma consulta no banco.
Um repositório é um elemento ativo no negócio e na modelagem, não é uma ferramenta de acesso a dados. Ele pode ser modelado com comportamento (métodos) que fazem sentido quando colocados em um diagrama do domínio, pois o objeto em sí pertence ao domínio. Um EntityManager não pertence a domínio algum, e seus métodos não são Ubiquitous.
O repositório tbm é um bom lugar para você definir seus contratos quando se interage com algo da persistencia (e retornar uma exception para seu cliente da camada). Isso parece melhor do que retornar true/false, null, 1/0, e outras bizarrices.
Colocar EntityManager diretamente nas entidades parece um grande acoplamento entre meios de persistencia e objetos de negócio.
Emerson_Macedo
@Lezinho
Concordo com você. Então o que eu disse sobre ter no Repository um adicionar que chama o persist() do EntityManager tornando esses métodos do Repository apenas um método que delega a tarefa e era isso que não estava me agradando muito. Mas parece que não tem outro jeito.
Mas de qualquer maneira a idéia do EntityManager não seria implementar um Repository só que genérico ? Eu entendo que fica mais Ubiquitous mas não sei se vale muito a pena abstrair isso em todos os casos. Talvez na maioria dos casos não seja necessário. E isso justamente que eu to me perguntando se tem algum caso que vou precisar realmente …
Exemplos ?
rodrigoy
Parta do pressuposto que é natural da entidade ela ser “persistente”, dessa forma, não é comum que uma entidade “salve” ela própria ou outras entidades dentro do comportamento da própria entidade. Minhas entidades não dependem do EntityManager, mas minhas façades sim…
Dado o paradigma atual uma das grandes responsabilidades da camada de aplicação é concentrar o controle de transação, seja por AOP, Anotações, Contâiner, etc… Assim sendo, é na camada de aplicação que está os manager.persist e manager.merge.
Lembre-se também: só usamos o EntityManager por causa de ORM, e nossas entidades não sabem disso.
Fabio_Kung
Concordo um pouco com o que o Rodrigo disse. Os repositorios trazem bastante valor para agregados e pesquisas complexas. Considerando que você não está usando ActiveRecord, você não vai precisar chamar save/persist/merge dentro dos seus objetos de domínio. Aí o Repositório não precisaria ter o adicionar() que só delega.
Minha opinião pessoal é de que não é tão ruim ter métodos do Repositório que apenas delegam se você não quiser expor a Session/EntityManager. O Repositório nesses casos fica sendo um “Adapter”, mas concordo que isso é um pouco burocrático…
E ai é que eu estava me perguntando se ao invés do Repository poderia ser sempre o EntityManager substituindo-o por completo. Pelo visto o pessoal ta preferindo e indicando encapsular.
É o que eu havia dito que eu não gostaria já que na verdade não está me parecendo muito um adapter e tão apenas um delegate bem simples.
Bem, no próximo projeto vou encapsular o EntityManager em cada Repository específico para gerênciar os agreggates e as queries complexas pra ver se terei algum ganho com isso. Claro que um ganho que percebo de cara é ficar mais Ubiquitous mas realmente como o Fábio falou, que burocracia heim …
pcalcado
Essa discussão já foi feita no GUJ algumas vezes mas acho que 99% das suas dúvidas práticas vão se reslver se você modelar o Repositório como uma interface implementada pelo DAO. O Repositório é um conceito de negócios que desta forma vai ser implementado pelo Dependency Inversion Principle (DIP, Uncle Bob).
Alessandro_Lazarotti
Um exemplo mais simples e direto de como pode comprometer seu objeto Entity de negócio inserindo diretamente EntityManager, é na construção de testes unitários.
rodrigoy
Tanto que nem chamo meus repositories de ContaRepository, chamo eles de ContaDAO mesmo. O que interessa é o conceito.
Neste caso ContaDAO (Interface) vai estar nas classes de Dominio ?
No meu modo de ver se ContaDao ser chamada de ContaRepository e esta ser injetada do Dominio, fica mais organizado.
Emerson_Macedo
Pois é. Pra mim o Repository seria uma interface e não uma classe concreta. Mas o pessoal tava sugerindo como se fosse uma classe concreta. Acho que esse teu exemplo do Repository ser a especificação (contrato/interface, ou como queira) do DAO uma abordagem válida. Mas lembra da tal discussão que teve uma vez no GUJ sobre o Hibernate eliminar a necessidade do DAO, já que o EntityManager/Session faz esse papel de DAO para quase todos os cenários? Mas pelo visto não morre a necessidade que é o que eu achava que ia acontecer …
A unica coisa que mudou é que minha interface do componente de acesso a dados ficou Ubiquitous e ao invés de escrever os SQL eu uso o Hibernate (Claro, com todas as suas vantagens de lazy-load etc etc etc.)
Acho que viajei na maionese achando que essa estrutura iria mudar … Me corrijam se eu estiver errado.
Não estariamos deixando de expressar um conceito no código ? Acho que seria melhor que seja ContaRepository mesmo.
Emerson_Macedo
Um exemplo mais simples e direto de como pode comprometer seu objeto Entity de negócio inserindo diretamente EntityManager, é na construção de testes unitários.
Considerando que EntityManager é uma interface, não vi diferença em coloca-lo no lugar do Repositório para a questão dos testes já que seria possível criar um mock da mesma forma. Não entendi muito bem o seu exemplo.
BiraBoy
Ubiquitous. O que isso quer dizer mesmo?
Fabio_Kung
ps.: odeio fazer isso, mas essa não consegui evitar…
Concordo.
Emerson_Macedo
Ubiquitous language é uma linguagem que todos entendem. No contexto de Domain Driven Design é uma linguagem que a equipe de desenvolvimento e o cliente entendem.
Ex: Se você fala com o cliente do projeto sobre DAO, EntityManager, EJB, JDBC ele vai boiar. Já se voce Fala sobre um RepositorioDeUsuarios ou RepositorioUsuario (prefiro assim), ele pode entender que em algum momento o usuário é armazenado/removido/recuperado por ali.
Fábio… olha, nomes são importantes, mas na minha opinião conceitos são mais importantes. Certo? Eu uso o nome DAO só por ser menos verboso. Teve um projeto que usei ContaREP, mas ficou estranho!
Também vejo que a sua opção acoplou conceitos. Creio que a separação de conceitos entre Entidade e Repositories é bem clara. No seu modelo praticamente estamos usando Client para obter Orders. Provavelmente você terá uma interação entre objetos que essa situação ocorre, mas pode ocorrer de você precisar essa mesma busca em outros pontos do sistema. Uma das idéias do repository é poder reaproveitar essas buscas.
Outro ponto: seu modelo nos diz que eu preciso ter uma instância de Client para obter as Orders. Não sei se dá pra concordar com você nesse ponto de “ser mais OO”. Pode ter aumentado a coesão, mas lembre-se que com isso o acoplamento aumenta também. Eu injeto services e repositories nas minhas entidades para cumprir objetivos de negócio da entidade, não para fazer a entidade ponto de entrada para buscas…
Fabio_Kung
Opa Rodrigo, vamos com calma…
Eu acho que nomes caminham juntos com conceitos. Usar nomes não adequados pode confundir os conceitos empregados, mas isso não é necessariamente o seu caso. Na verdade eu não sei muito bem onde termina o senso comum e começa o gosto pessoal nesse caso.
rodrigoy:
Também vejo que a sua opção acoplou conceitos. Creio que a separação de conceitos entre Entidade e Repositories é bem clara. No seu modelo praticamente estamos usando Client para obter Orders. Provavelmente você terá uma interação entre objetos que essa situação ocorre, mas pode ocorrer de você precisar essa mesma busca em outros pontos do sistema. Uma das idéias do repository é poder reaproveitar essas buscas.
Outro ponto: seu modelo nos diz que eu preciso ter uma instância de Client para obter as Orders. Não sei se dá pra concordar com você nesse ponto de “ser mais OO”. Pode ter aumentado a coesão, mas lembre-se que com isso o acoplamento aumenta também. Eu injeto services e repositories nas minhas entidades para cumprir objetivos de negócio da entidade, não para fazer a entidade ponto de entrada para buscas…
Eu até entendi o seu ponto aqui, e concordo um pouco com ele, mas não era o meu caso. No exemplo que eu dei, dado um cliente eu quero as suas compras obedecendo algum critério. Poderíamos discutir se o cliente é responsável ou não por me dizer quais foram as suas compras, mas supondo que ele saiba disso (e tenha essa responsabilidade), acho razoável pedir a um cliente que me dê as suas compras. Eu não sei se ficou claro, mas como o meu requisito parte de “dado um cliente…”, não haverá pontos no sistema onde essa “busca” não parta de um cliente.
Eu estou longe de sugerir que o cliente seja o ponto de entrada para qualquer compra e que todas as compras devem vir de clientes. Não tenho nada contra o repositório ser usado dentro ou fora de uma entidade.
Emerson_Macedo
Uma coisa que esqueci de perguntar anteriormente foi: Se os conceitos do EntityManager são apenas de persistência e ficarão encapsulados no DAO, como fica o Criteria nessa história?
Hibernate já tem uma API critéria e Domain Driven Design recomenta os Repositórios utilizando Criterias para as pesquisas.
Soluções:
1 - Deixariamos a API Criteria do Hibernate vazar entre as camadas? Não me cheira muito bem isso.[/list]
2 - Criamos outro Criteria para o Repositório e converteriamos esse Criteria no Criteria do Hibernate adicionando a função de adapter a este Repositório ?
3 - ???
Essas são as razões que me fazem pensar que o Hibernate/JPA (principalmente esta spec) foi criado(a) como uma solução contemplando esses conceitos que vão do Repositorio pra baixo. Como falei no post inicial, EntityManager (O Gerênciador de Entidades ) é justamente o que é um Repositório se propoe (Tá certo o nome ta diferente). A parte do Critéria tem até o mesmo nome e a funcionalidade do DAO/Data Mapper está implementada internamente com a geração das queries, dialetos do banco, etc.
O fato é que por ser um EntityManager genérico com métodos que cheiram persistência, ferra com tudo e IMO acabamos tendo que fazer um bacalhau enorme para encapsular isso tudo e ficar com mais cara de conceitos de negócio e isso que ta me incomodando :shock:
Fabio_Kung
Bom, no geral (sempre há exceções) eu não acho isso saudável:
classRepository {
Listbusca(Criteriocriterio);
}
Isso transfere muita responsabilidade aos utilizadores do repositório, além de espalhar esse conceito de “busca” por todos os cantos. Eu prefiro uma interface mais bem definida:
Na implementação do repositorio você usa criterias e specifications.
nbluis
Fabio Kung ++
Emerson_Macedo
É dessa forma que se implementa hoje até mesmo no DAO. Acho que é a melhor mesmo
Alessandro_Lazarotti
Um exemplo mais simples e direto de como pode comprometer seu objeto Entity de negócio inserindo diretamente EntityManager, é na construção de testes unitários.
Considerando que EntityManager é uma interface, não vi diferença em coloca-lo no lugar do Repositório para a questão dos testes já que seria possível criar um mock da mesma forma. Não entendi muito bem o seu exemplo.
Com o EntityManager injetado (mock ou não), você teria na mesma lógica espalhado no seu código possíveis ejbqls/HibernateCriterias/Specifications/nativeSQLs ou no melhor dos casos invocações de NamedQueries… tudo junto com o resto de sua lógica.
Ao realizar os unitTests você vai querer testar apenas o pertinente ao método, ou seja, toda a parafernália da persistência vai estar lá no seu design porém ignorada por uso de Mocks.
Isso tudo funciona, mas a fluência do código vai ficar péssima… TDD nisso vai dar dor de cabeça.
O ganho de uma abstração maior, englobando todos os aspectos da persistencia, é evidente.
Fabio_Kung
Ótimo ponto Lezinho. Imagina ficar mockando EntityManager: se receber query “SELECT * FROM Client …” retorne “new List<Client>() …;”. Que inferno.
Isolar tudo isso em repositórios deixa tudo bem mais testável. E TDD é exatamente sobre isso; melhorar continuamente o seu design.
Emerson_Macedo
Ok. Me rendo rs rs rs. Vai valer a pena encapsular a utilização do EntityManager no Repositório.