Hibernate - Vale a pena abstrair ainda mais?

39 respostas
Emerson_Macedo

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.

Sugestões ?

39 Respostas

xandroalmeida

Eu tenho usado Repositorys diretamente de dentro das Entitys, e dentro do Repository uso diretamente o Hibernate, sem mais abstrações.

Emerson_Macedo

Pois é. Mas é isso que eu acho não fazer sentido.

Por exemplo:

public class RepositorioUsuario {
   private EntityManager em;
   public void salvar(Usuario u) {
      em.persist(u);
   }
}

O que eu ganhei com isso ? Nada.

Virou um delegador apenas.

Abdon

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

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) :smiley: .

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

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…

De qq forma, Java é burocrático mesmo! :twisted:

Emerson_Macedo

Exatamente isso que eu disse nos posts anteriores. Porém pensando no Repository, em alguns casos temos referência dentro da Entity para um repositório, vide esse exemplo http://blog.caelum.com.br/2007/06/09/repository-seu-modelo-mais-orientado-a-objeto/.

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.

Um padrão comum nas minhas aplicações é este:


rodrigoy

O que não impediria também de fazer isso:

M

Mas será que nome não faz parte do 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.

Baixa o livro gratuito Domain Driven Design quikly disponível no InfoQ e no capítulo 2 fala sobre o assunto com mais profundidade. http://www.infoq.com/minibooks/domain-driven-design-quickly

rodrigoy

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 :smiley: ) é 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:

class Repository {
  List busca(Criterio criterio);
}

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:

class Repository {
  List&lt;Client&gt; getSuspendedClients();
  List&lt;Client&gt; getOldClients();
  // ...
}

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. :smiley:

Criado 16 de novembro de 2007
Ultima resposta 19 de nov. de 2007
Respostas 39
Participantes 13