Hibernate - Vale a pena abstrair ainda mais?

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 …

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

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.

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:


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

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.

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.

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.
[/quote]
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.

Ubiquitous. O que isso quer dizer mesmo?

ps.: odeio fazer isso, mas essa não consegui evitar…

Concordo.

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

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…

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.

[quote=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…[/quote]
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.

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:

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<Client> getSuspendedClients();
  List<Client> getOldClients();
  // ...
}

Na implementação do repositorio você usa criterias e specifications.

Fabio Kung ++

É dessa forma que se implementa hoje até mesmo no DAO. Acho que é a melhor mesmo

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.
[/quote]
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.[/quote]

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.

Ó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.

Ok. Me rendo rs rs rs. Vai valer a pena encapsular a utilização do EntityManager no Repositório. :smiley: