Acesso ao EntityManager: através de um DAO ou diretamente?

[quote=DaviPiala]Sou contra o uso de DAO’s indiscriminadamente alem de fazer mal a saude do sistema faz com que alguns programadores tenham dor de barriga, no caso de data logic o DAO serviria bem, ninguem quer um wrapper sem sentindo a mais pra se preocupar…

Ateh gostaria de perguntar alguem por ai jah teve que trocar o mecanismo de persistencia? O DAO ajudou?[/quote]

Eu acho que é preciso ter cuidado com esse tipo de pensamento, porque pra aproximar isso dos infernais “tudo no botao” do Delphi/VB é um passo. Apesar de ja estar encapsulado o EntityManager continua sendo codigo de acesso a dados e acesso a dados no meio das regras é ruim.

A principio parece obvio, pois entityManager.persist(cliente) nao tem diferenca nenhuma para repositorioCliente.persistir(cliente), nem mesmo da pra dizer que seja mais intuitivo, se for é muito pouco. Mas para buscar um cliente por nome ou data de cadastro ou qualquer coisa, de forma simples, do metodo em que estiver, voce vai precisar de tres ou quatro linhas de codigo com entityManager e apenas uma com o repositorio. Aí, já num exemplo bem simples, o repositorio comeca a deixar de ser apenas um wrapper.

Alem disso o esforco extra de usar repositorio é minimo, se existe.

Entendi, eu sempre achei trabalhar com DAO’s trouxesse alguns detalhes de tecnologia que prefiro esconder, no meu caso nao consigo me sentir confortavel em trabalhar com DAO’s diretamente, por isso adotei Model Facades. Como nesse caso se torna redundante acabo removendo os DAO’s.

Davi, acho que todos aqui são contra o uso indiscriminado de qualquer design pattern. A pergunta não é essa, e sem duvida usar o DAO apenas para ?“caso eu pare de usar JPA” também não é bom motivo, como muitos já citaram aqui.

Paulo me expressei mal… Nao tive uma boa experiencia com DAO’s, por isso me expressei dessa forma.

eu entendi davi!

pelo que estou vendo, muitos que falam aqui que ainda usam DAO, na verdade usam o tal do ModelFacade que a Sun esta falando (e novamente, só encontro referencias a esse pattern pelo próprio catalogo beta da sun). mas todos de alguma maneira encapsulam o acesso a Session/EntityManager quando um pequeno procedimento deve ser executado em cima dos dados.

[quote=Paulo Silveira]oi leozin

entedi e concordo com seu exemplo sim, que ai vira so um wrapper.

mas e na hora que voce tem “data logic” e names queries não são suficientes? a classe em que voce concentra isso, como chama?

ah! e DTO ainda tem seu espaço quando voce precisa ajustar a granularidade (mas realmente nao é mais um “pattern obrigatorio”, ja que ejb3 engoliu as deficiencias do 2…)[/quote]

A data logic não vive sozinha, isso é fato. A data logic é alguma operação que vai tanto modificar o estado do objeto e que pode ou não fazer cruds no banco de dados.

Provavelmente exista uma camada de service.

public interface PersonService {

  public void verifyPersonStatus(Person person);

  public void verifyPersonStatus(Integer personId);

}

Que dentro dela vai encapsular as chamadas ao banco de dados. Poderão até existir casos que algumas funções seja simplesmente delegates, como:

public List<Person> loadAllPersons { return personDAO.loadAllPersons(); }

Só que na minha opinião, acesso ao DAO deve ser feito somente pela classe de service, então no final das contas, mesmo que haja um simples “delegate” da service pro DAO, o service teria como objetivo abstrair o código do DAO pra ficar de um nível mais alto.

Em outras palavras, quando houver: Uma criteria, uma named query, um insert e um comportamento XYZ que foi chamado, tipo um envio de uma mensagem, ele pode simplesmente ficar na camada de service. Perceba que isso não faz diferença se tu usar um DAO ou um Entity Manager. A vantagem de tu usar um DAO é que tudo que envolver banco tu pode encapsular no próprio DAO, mas aí o teu service se transforma basicamente em um facade.

Mas aí é questão de gosto mesmo, trabalhar com camada de service é uma das maneiras, mas há outras opções como:

  • Domain Model
  • Transaction Script
  • DSLs

E qualquer outra coisa que tu tenha feito e tenha gostado =]

Essa discussão do DAO está mto interessante.

Gostaria de expor meu ponto de vista.

Eu levo pelo seguinte ponto:

[code]public class BaseDAO {
private EntityManager manager;

  /* Métodos Comuns */

}
[/code]

public class CarroDAO<Carro> extends BaseDAO<Carro> { public List<Carro> retornaQtCarrosVendidosUltimoSemestre() {...} }

Eu penso da forma acima. O DAO ele existe um genérico onde é implementada as chamadas aos métodos do EntityManager.
E uma outra classe que herda as caracteristicas da BaseDAO e implementa um método retornaQtCarrosVendidosUltimoSemestre que no caso se interage com outras tabelas e faz um filtro desses dados não sendo possivel via *QL.

Nesse caso eu vejo que elimino dois problemas:

  • Mudança de framework. Mesmo que ela seja quase impossivel temos que levar em consideração. Não sabemos o dia de amanhã então por via das dúvidas levamos em consideração, mas não com total consideração.

  • Evita a criação de vários DAO’s. Podendo instanciar a classe BaseDAO com um tipo genérico, isso faz com que em casos que temos CRUD’s básicos não seja necessário a criação de um DAO para ele, utilizamos o nosso BaseDAO. Outros DAO’s só seriam gerados em momentos realmente necessários.

Não sei se me expressei bem. Mas este é meu ponto de vista.

Nessa onda de tentar prever as coisas que podem acontecer durante o ciclo de vida do sistema, acabamos ficando com uma arquitetura horrível de dar manutenção onde a adição de uma simples funcionalidade implica modificar 329842193842093847 classes.

Nessa onda de tentar prever as coisas que podem acontecer durante o ciclo de vida do sistema, acabamos ficando com uma arquitetura horrível de dar manutenção onde a adição de uma simples funcionalidade implica modificar 329842193842093847 classes.[/quote]

Vc ñ tem que prever TODOS os acontecimentos. E sim, os que podem ser os mais impactantes e os que ocorrem sempre.

Sempre que desenvolvo um sistema web eu me previno logo de cara com duas coisas: mudança de visão e mudança de banco de dados.

Isso pode para mtos não ocorrer mas comigo já aconteceu umas 4 vezes.

Tinha sistemas com a visão feita em Struts que foi migrado para Swing sem mtos problemas. Da mesma forma sistemas desenvolvidos em JDBC puro que forão migrados para JPA sem grandes mudanças.

Acho que tem que pensar em prever algumas coisas sim. TUDO tbm não né? Ai o sistema nem vai rodar.

Quando há alguma perspectiva de mudança tudo bem. Mas no local onde trabalho o pessoal engessou a arquitetura para ter uma camada de aplicação cheia de façades (interface + implementação) e uma camada de persistência lotada de DAOs (interface + implementação) delegando pro JPA. Então até mesmo pra sistemas simples, que nunca mudarão as camadas de persistência e apresentação, fica uma arquitetura muito pesada, péssima pra dar manutenção, e sem a menor justificativa.

Para esses sistemas web eu injetaria o EntityManager direto nos managed beans usando @PersistenceContext e pronto, sem façades, sem DAOs, sem nada mais. Note que isso não é necessariamente transaction script, pois essa arquitetura não lhe impede de construir um domain model.

Amigos,

DAO é um padrão de infra… Repository é um padrão de domínio…

(vou tentar terminar meu blog post ainda hoje, mas por enquanto, pensem no diagrama abaixo)

É o domínio que vai direcionar os seus repositórios.

Exatamente Yoshi.
Por isso preferimos a dupla EntityManager/Session (infra) & Repositories(dominio) no lugar de AbstractGenericDAO (wrapper) & JpaDAO (???)
Quando não rola repository seja por simplicidade do projeto ou pela equipe não entender de fato o que é um Repository… tbm sou a favor de EntityManager/Session diretamente nos Services, como ja foi citado por outras pessoas aqui (mesmo isso geralmente atrapalhando os testes).

[quote=Alessandro Lazarotti]
Quando não rola repository seja por simplicidade do projeto ou pela equipe não entender de fato o que é um Repository… tbm sou a favor de EntityManager/Session diretamente nos Services.[/quote]

Se pensarmos no porquê o repository é necessário e qual é o seu papel principal no sistema - procurar instancias , ele não passa de um serviço de pesquisa glorificado. (Mais ainda se usar repositorio interface + implementação). O uso da api de pesquisa por detrás de um Service é natural porque é isso que está pedindo do objeto ( o serviço de pesquisar coisas) e ele não tem estado. O passo do service para o repositorio é uma linha ténue e quase sempre só depende de como vc encara a coisa. Na fundo não ha muita diferença tecnica. A diferença é mais ideologia pois o repositório por ter outras responsabilidades para com o dominio ( verificação de relações entre os objetos , por exemplo - caso em que ele tem que ser individualizado do serviço) e depende mais fortemete da estrutura/grafo do dominio que o serviço. Mas no fritar do bacon não passa de um serviço glorificado.

Básicamente vc está fazendo a mesma coisa optando por um ou por outro já que ponto mais importante é que todas as consultas às instancias das entidades são mediada por um objecto/camada. chamar-lhe de Repositorio ou serviço é uma questão de da metáfora que estiver usando no design.

Só lembrando que o Repositório é um centralizador de consultas e não um mapeador de dados.

Enfim, nunca, nunca, nunca se faz acesso à infra diretamente. A razão para isto não é porque quero mudar a infra no futuro (embora assim seja mais facil fazer isso) mas :

  1. facilitar os testes - este é realmente o ponto mais importante. Implementar mocks só é possivel se existir algo que possa ser “mockável”. Fazer um mock do EntityManager é suicidio, mas de um repositório é um passeio no parque e do serviço mais fácil ainda.

  2. Isolamento. Ao utilizar o SoC, vc não pode deixar a responsabilidade de comunicar com o EntityManager ( que se virmos bem, estabelece um protocolo de troca de objetos em OO) espalhada por todos os cantos. Ai vc aplica o SoC e remove isso , mas essa remoção só faz sentido se for concentrada em um unico objecto/camada. Ao isolar essa funcionalidade em um unico objeto/camada vc está fazendo melhor design OO. É este melhor design que lhe permite facilitar os testes.

Veja pela perspectiva de testes (1) ou de OO (2) existem muitas vantagens em não fazer o acesso ao EM diretamente.

[quote=sergiotaborda]Enfim, nunca, nunca, nunca se faz acesso à infra diretamente. A razão para isto não é porque quero mudar a infra no futuro (embora assim seja mais facil fazer isso) mas :

  1. facilitar os testes - este é realmente o ponto mais importante. Implementar mocks só é possivel se existir algo que possa ser “mockável”. Fazer um mock do EntityManager é suicidio, mas de um repositório é um passeio no parque e do serviço mais fácil ainda.
    [/quote]
    EasyMock

// Voilá! EntityManager mockManager = createMock(EntityManager.class);

Pra esses sistemas que citei como simples, eu - veja bem, EU - prefiro abrir mão um pouco da rigidez OO e facilitar a manutenção.

tnaires, creio que não foi isso que o Sergio quis dizer. só mockar por si só, tanto o jmock ou mesmo uma dynamic proxy resolve. mas criar todas as Expectations pra isso, mesmo pras coisas mais simples, vai dar trabalho… imagine criar expectations para as queries. é como mockar Connection e HttpServletRequest… voce nao deveria precisar dessas coisas… deveriam estar bem enteressadas no seu codigo de infraestrutura, e raramente seus testes precisariam mocka-las.

Eu nunca mockei uma Session ou EntityManager na vida, sempre os daos/repositorios. eu tambem prefiro sempre isolar o entitymanager… seja la em qual pattern for (talvez o melhor nome seja mesmo o Facade… apesar de que Facade é daqueles patterns que quase toda classe que separa concerns acaba fazendo)

Então Lezinho, o problema é que eu não vejo uma única situação onde não seja necessário uma abstração do domínio que faz buscas! Todo sistema que fiz pode ter começado simples, mas depois, sempre me vejo implementando repositórios (talvez seja só o costume mesmo). O domínio fica bonito e não vejo que há muito custo implementar e ainda ganha uma melhor testabilidade. Nas arquiteturas atuais isso é simples.

A falta de conhecimento da equipe não deveria interferir nas decisões arquiteturais (mas eu sei!!! elas interferem!!!). Se a equipe não sabe escrever testes devemos fazer uma arquitetura acoplada e não se preocupar com isso? (sim esse foi um exemplo bem exagerado!!)

[quote=Alessandro Lazarotti]
tbm sou a favor de EntityManager/Session diretamente nos Services, como ja foi citado por outras pessoas aqui (mesmo isso geralmente atrapalhando os testes).[/quote]

Não vejo que isso atrapalha tanto os testes, a não ser o tempo de rodar, mas com um HSQLDB in Memory, dá para injetar o EM e testar seu repositórios ou service sem problemas. Assim como o Paulo, eu não mockeio o EntityManager… já tentei de tudo. Mockear, Stubar (ou Stubetar… não sei se essa palavra existe)… fica tosco.

Eu não acho a testabilidade do EntityManager as mil maravilhas. Para quem já testou um ActiveRecord do Rails sabe o que eu estou falando. EntityManager (e sua implementação mais comum que é o Hibernate) é muito lento para subir, se você tem muitas entidades esses testes de integração demoram bem para rodar.

Err… só para comentar… nunca ví aqui uma thread sobre DAOs e Repositórios tão civilizada.

:wink:

[quote=rodrigoy]Err… só para comentar… nunca ví aqui uma thread sobre DAOs e Repositórios tão civilizada.

:wink: [/quote]

é que estamos todos evitando citar os patterns, to tentando jgoar a conversa para o código e prática em si.

achei excelete, e provou que 99% das coisas que falam são bem próximas, as pessoas só não concordam com a nomenclatura (que não tem a mesma importância que a prática, na minha opinião).

Paulo, não seria necessário mockar o EntityManager para as queries, pois como falei antes injetá-lo diretamente no managed bean não me impede de ter um domain model, com repositórios e tudo. Então é só mockar os repositórios.

Tudo o que eu queria fazer era aproveitar a simplicidade da anotação @PersistenceContext com a finalidade de simplificar a estrutura da aplicação, para os casos que citei antes. Mas fazendo um balanço agora, isso se torna uma faca de dois gumes. Ou você mockeia o EntityManager ou você “complica” mais um pouco movendo-o para dentro dos repositórios, tornando-os editáveis. Reconheço que a segunda opção é bem mais viável.

[quote=Paulo Silveira]tnaires, creio que não foi isso que o Sergio quis dizer. só mockar por si só, tanto o jmock ou mesmo uma dynamic proxy resolve. mas criar todas as Expectations pra isso, mesmo pras coisas mais simples, vai dar trabalho… imagine criar expectations para as queries. é como mockar Connection e HttpServletRequest… voce nao deveria precisar dessas coisas… deveriam estar bem enteressadas no seu codigo de infraestrutura, e raramente seus testes precisariam mocka-las.

Eu nunca mockei uma Session ou EntityManager na vida, sempre os daos/repositorios. eu tambem prefiro sempre isolar o entitymanager… seja la em qual pattern for (talvez o melhor nome seja mesmo o Facade… apesar de que Facade é daqueles patterns que quase toda classe que separa concerns acaba fazendo)[/quote]

Paulo, só um addendo, eu sempre usei testes unitários com algumas classes auxiliares do Spring, lá tem alguns mocks prontos para HttpServletRequest, que são beeem interessantes :stuck_out_tongue: