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

72 respostas
Paulo_Silveira

Oi pessoal

Estou escrevendo sobre persistência, e cai na polêmica parte de DAO, Mappers, EntityManager e patterns.

Essa é uma antiga discussão. Devo usar DAO se estou em um ambiente com EJB+JPA?

O Adam Bien té da opiniao que nao devemos mais usar o DAO, e acessar o EntityManager diretamente (em especial se só for um wrapper):
http://www.adam-bien.com/roller/abien/entry/jpa_ejb3_killed_the_dao

Obviamente isso pode gerar confusoes e muita gente fazendo os mesmos procedimento, processando as mesmas entidades de certa forma que uma query nao permitiu buscar, etc. Tanto que ele da um passo pra trás e diz que você pode sim ter um DAO:
http://www.adam-bien.com/roller/abien/entry/you_should_dao_if

Os posts do Vincent Partington (muito bem escritos) ganharam muita popularidade esse ano, e vão para o outro lado, apoiando o uso do DAO (assim como outros):
http://blog.xebia.com/2009/03/09/jpa-implementation-patterns-data-access-objects/

(uma observação, o Vincent ainda aborda outros dois tópicos que eu concordo com ele: tomar cuidado com relacionamento bidirecional de entidades, e que o DTO não está morto, pode ser usado para ajustar granularidade (apenas quando necessário, claro)).

Curioso que essa discussão reaparece inúmeras vezes, e se você for ver, a galera do Spring tem uma opinião, a galera do Hibernate outra, do Glassfish outra, e assim por diante.

Sei que muitos tem preferências (eu também tenho) mas eu gostaria links e referências que deem base para a escolha. Qual é a sua preferência?

72 Respostas

L

Eu acredito que existam “forças” que levam ao incentivo ou à rejeição do DAO.

As que rejeitam o uso do DAO acontece quando:

:arrow: Boa parte da funcionalidade do programa se resume a CRUD. Afinal, não há o que esconder, o negócio é o acesso ao banco.
:arrow: A funcionalidade é chamada genericamente de “relatório”.

As que incentivam o uso de DAO acontece quando:

:arrow: o banco é legado e não reflete a maneira como se pensa o negócio.
:arrow: a entidade persistida possui vários estados onde, em alguns deles, não se permite a inserção/alteração/remoção. O DAO pode impedir operações de maneira consistente.
:arrow: o negócio é complexo, exigindo separar qualquer coisa que impeça uma visão em alto nível.

De qualquer maneira, o JPA sempre vazará a transação, o que significa que só ela é incapaz de encapsular o acesso aos dados. Também existe o movimento dos bancos pós-relacionais ou NoSQL, onde o JPA é simplesmente a abstração errada. Portanto, abandonar DAO (e usar os EntityManagers diretamente) pode não ser uma vantagem se considerarmos todas as ofertas de persistência.

Paulo_Silveira

Ola Leonardo

Acho que é bem por aí mesmo. Sua opinião está mais para o lado do Vincent (a minha também é para esse lado). Curioso a Sun não se posicionar nesses anos todos em relação a essas boas práticas, e ficarmos com o Core J2EE Patterns mais que outdated.

Alessandro_Lazarotti

Penso que depende do projeto e de como esta o resto de sua modelagem.
Quem vem do Java do período jurássico after ORM, lembra de:

  • GenericFirebirdDAO implements GenericDAO
  • CustomerOracleDAO implements CustomerDAO
  • NotaFiscalMySQLDAO implements NotaFiscalDAO
  • RapaduraPostgresDAO implements RapaduraDAO

… maravilha, conseguimos ter nosso projeto "multi-banco".
A idéia era abstrairmos o banco, ja que as instruções SQL sempre tinham suas particulariedades lock-in. A vantagem comercial é clara.

Com as ORMs, começamos a pensar diferente. Vou fazer a persistencia em JDO pra abstrair o banco, mas e se quiser mudar para o Apache OJB, KODO ou Prevayler? Vou fazer em Hibernate, mas e se quiser mudar para o iBatis? E por aí vai… então começaram a "era" dos:

  • CustomerHibernateDAO implements CustomerDAO

… maravilha, temos nosso projeto a prova de mudança de tecnologia. Hmm, mas quantos deles realmente mudamos de tecnologia? Quantos projetos reescrevemos "toda" camada de persistencia de Hibernate para qualquer outra coisa? Pessoalmente, ja vi muito dinheiro com horas de desenvolvimento sendo jogadas fora para interfaces cuja implementação nunca passou de uma classe concreta/per interface.

Mas vamos misturar regras de negócio com códigos de banco? Pra isso temos outros padrões como ActiveRecord, Repository, QueryObjects etc. DataAcessObject quase sempre é implementação de DataMapper, e para isso os ORMs fazem bem o papel. Os outros padrões tem características diferentes e não são de fato DAOs.

Logo, hoje, se utilizo um desenvolvimento like DDD, uso Repository para fazer parte do codigo com uma implementação onde a API do ORM é exposta… aos moldes de Applying-Domain-Driven-Design-Patterns-Examples. Se o projeto não justifica um domain-driven, descarto repository (embora um possa existir sem o outro). Neste caso utilizo EntityManager com suas NamedQueries diretamente, sem medo de ser feliz, chutando pra longe aqueles DAOs onde tinha :

  • dao.save() -> entityManager.persist()
  • blaDao.buscarBlaMaiorQueFulano() -> entityManager.createNamedQuery(?bla.buscarBlaMaiorQueFulano?)

Quase sempre, não sinto falta neste caso dos DAOs. "Quase", pq quando se fala em testes unitários…hmm. Nisso tendo a granularidade em DAOs ou Repositories pode lhe ajudar mais do que atrapalhar.

Paulo, os BluePrints do JavaEE 5 da SUN mostram que "ModelFaçade" é a bola da vez para abstrair as chamadas a JavaPersistence API. DAOs estão fora da jogada:
https://blueprints.dev.java.net/bpcatalog/ee5/persistence/

Paulo_Silveira

Alessandro, mais um excelente post seu, obrigado!

Mas quando sua named query nao é suficiente para trazer o que você precisa, e você precisa de um pós processamento da sua lista de entidades (seja para sumarizar alguma infiormação, seja para uma operação em lote de “data logic”), onde você poe isso?

E de qualquer maneira esse ModelFacade esta bem próximo de um DAO, correto? (vale lembrar da Sun ser campeã em renomear patterns :), esse nome é novo pra mim, apesar da composição obvia, e não encontro referencias em outros lugares).

PS: o King costuma dizer que o Hibernate é quem faz o papel do DataMapper, nao o DAO, mas sinceramente se formos dar atenção a como cada um usa a nomenclatura, vamos achar 100 opções diferentes.

Ferryman

Concordo com a idéia de que o entityManager faz o papel de DataMapper.

Eu gosto de criar uma camada (Que não vou chamar de DAO), onde isolo o uso do entityManager e as queries do negócio.

Faço isso para manter o SRP (Single Responsability Principle). Esse isolamento ajuda muito nos testes unitários, pois eu posso mockar essa camada inteira e testar apenas a lógica da aplicação, sem precisar subir um banco de dados/hibernate para testes (o que aumentaria consideravelmente o tempo de execução dos testes).

Concordo com a opinião que nunca mudei de tecnologia de persistencia durante um projeto, porém, crio essa camada não com o objetivo de poder mudar a tecnologia, mas sim com o objetivo de manter “cada coisa no seu lugar” - Cada camada com sua responsabilidade.

[]s

rponte

Olá Paulo,

Eu concordo com o Leonardo e com o Lazarotti, DAO ainda tem seu lugar e não morreu por causa do JPA.

Basicamente DAO tem seu lugar ainda nas aplicações e deveria ser utilizado nos casos em que faz sentido. O problema, na minha opinião, é que o pessoal tende a replicar DAOs por todos os lugares da aplicação, principalmente para CRUDs e relatórios quando na verdade o DAO está atuando apenas como um wrapper.

Uma coisa que ainda não me entra na cabeça é ter uma DAO layer para tentar abstrair a tecnologia (como citado pelo Lazarotti). Quando se decide por uma tecnologia como JPA/Hibernate você deveria se preocupar mais em aproveitar os recursos da tecnologia a evita-los pelo simples fato de achar que futuramente você poderá mudar de tecnologia.

Paulo_Silveira

Oi Rafael

Vendo seu blog, que no ultimo post fala de DAO, voce comenta que algumas pessoas argumentam assim:

Concordo totalmente que esse não deve ser a justificativa essencial para um DAO. Se só posso nomear como DAO alguem que vai me abstrair se uso JDBC/JDO/JPA, realmente nao chamaria minha classe de DAO, e sim de alguma outra coisa (ModelFacade? como o Alessandro linkou pro beta do catalogo de patterns do Java EE 5).

Acho legal um trecho do wikipedia, que fala As is common in Java, there are many open source and commercial implementations of DAO available. Each of these can have potentially different implementation strategies and usage models. There is a familiarisation curve involved with each of them, realmente cada um faz de um jeito e leva o pattern de um lado pro outro.

Leozin

Sinceramente eu não concordo que DAO deva existir depois do JPA.

Criar classes que estão basicamente sendo tratadas como delegates (como DAOs JPA-based) chega a ser uma maneira de “socar” camadas no sistema, aumentando a complexidade e deixando as classes com excesso de coesão. Não que eu ache que coesão seja algo ruim, pelo contrário, mas quando as definições ficam assim,“forçadas demais”, dá a impressão que o sistema fica meio feio, sei lá.

Querendo ou não, os EntityManagers já atuam como DAOs genéricos.

Além disso, temos a interface EntityManger que é facilmente “stubável” ou “mockável”, eu poderia citar que as vantagens de se utilizar DAOs era a facilidade de ser unit-tested, mas pelo visto não há tantas diferenças não.

Agora, se há chances do sistema trocar de ORM pra JDBC ou qualquer outra coisa, aí sim pode ser que seja necessário criar DAOs. O problema é que nem sempre é assim e o pessoal gosta de se preocupar com apocalypses que mal foram previstos, é como aquela palestra que teve no falando em Java, sobre as “leis” do arquiteto, algo como “se preocupar com performance quando não se há necessidade”, apesar das coisas serem completamente diferentes, a idéia de se preocupar com algo que nem se sabe se haverá é a mesma.

Pela experiência que tive até hoje, o que eu concluí foi isso. Já trabalhei com sistemas multibanco, usando DAO com JPA mas não achei muiiiitas vantagens não.

Paulo_Silveira

Oi Leozin

mas entao onde voce colocaria um método que envolve “data logic”? por exemplo para criar um objeto para diversos relatórios, que é uma combinação de dados de diversas entidades?

qmx

Eu ainda sou defensor dos DAOs, principalmente pela questão de facilitar os mocks na hora dos testes.

Um efeito colateral bom que eu já peguei, foi uma mudança de tecnologia (sim, muito tosca), que jogou parte do que estava no banco pra um GED. Como já tinhamos DAOs (bem magros por sinal), bastou mudar a implementação.

Acho que o tempo perdido é muito pequeno em vista do benefício, então…

IMHO, sempre

rodrigoy

Paulo, o que tenho aplicado é usar Repositórios como interfaces (só para se beneficiar de DI) e com uma implementação que depende do EntityManager. Abandonei o nome DAO, pois o padrão significa mapeamento para o banco, e como o Hibernate faz isso para nós não vejo sentido em usar essa nomenclatura.

Para falar a verdade, estou para blogar sobre Repositórios… vou ver se adianto isso hoje e aí posto aqui para colaborar…

Leozin

Paulo Silveira:
Oi Leozin

mas entao onde voce colocaria um método que envolve “data logic”? por exemplo para criar um objeto para diversos relatórios, que é uma combinação de dados de diversas entidades?

O ideal mesmo era colocar um repository no teu service, algo de “nível mais alto” se não quiser misturar “tanto” persistência com negócios.

Isso se tu quiser abstrair mais ainda, porque o EM já abstrai muitas coisas na minha opinião, ainda mais se tu trabalhar com NamedQueries por exemplo.

Y

Paulo Silveira:
Oi Leozin

mas entao onde voce colocaria um método que envolve “data logic”? por exemplo para criar um objeto para diversos relatórios, que é uma combinação de dados de diversas entidades?

Pois é, esse é o ponto.

Eu tbm nao concordo em ter toda aquela hierarquia DAO como propunha o padrao a principio. Mas será que é uma boa ter o codigo de acesso aos dados (Session, EntityManager, etc…) misturado as regras?

Eu tbm vou pelo caminho dos repositorios, mas as ideias que rechaçam os DAOs tbm nao podem ser aplicadas aos repositorios? Afinal eu nao estaria criando uma camada pra, em grande parte dos casos, somente traduzir os metodos do EntityManager.

Mas especifamente esse exemplo que vc deu , eu acho que torna clara a razao da existencia de um repositorio.

diegosantiviago

acho que não devemos esquecer os principios do padrão de projeto DAO. :slight_smile:

Context
Access to data varies depending on the source of the data. Access to persistent storage, such as to a database, varies greatly depending on the type of storage (relational databases, object-oriented databases, flat files, and so forth) and the vendor implementation.

Fonte:
http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html

Paulo_Silveira

diegosantiviago:
acho que não devemos esquecer os principios do padrão de projeto DAO. :slight_smile:

Oi Diego. Realmente se usarmos a definição da Sun, encapsular as queries e lógica de dados não se encaixa como um DAO. Então o que seria essa classe?

Paulo_Silveira

só um adendo: eu fico perdido em discussões em que todo mundo tem um ponto válido e que concordo com todos eles!

Leozin

hahahahaha é verdade

Mas Paulo, tu já percebeu o tanto de design patterns que "desapareceram" quando o JEE 5 apareceu?

Digo, existe ainda business delegate? service locator? dao? dto? Em ambiente JEE 5?

Alguns design patterns no j2ee foram feitos exatamente para suprir determinados pontos falhos, como por exemplo, o citado no falando em java ano passado (denovo rs), o DTO/TO/VO para evitar as chamadas RMI no servidor em Entity Beans quando se ia acessar uma determinada propriedade.

Sinceramente, focando no assunto do tópico, não acho que "seja errado" criar DAOs com EntityManagers, mas é que, vai variar de acordo com o sistema que tu vai desenvolver, e que na maioria das vezes, vale mais a pena trabalhar com um EntityManger e com/sem repositories do que JDBCDAO ou JPADAO.

Em um dos sistemas que trabalhei, a hierarquia era assim:
public interface PersonDAO {

  public void createPerson(Person person);

}
public abstract class BaseJPADAO {

  protected EntityManager em;

  protected Query createNamedQuery(String query) {
    // A named query template method
  }
}
public class JPABasedPersonDAO extends BaseJPADAO implements PersonDAO {
  public void createPerson(Person person) {
    em.persist(person);
  }
}

Lembrando que caso haja interesse de usar um JpaTemplate/JdbcTemplate do Spring, pode-se colocar no Base* de algum DAO.

Como a programação era voltada a interfaces, ficava fácil de trocar caso houvesse necessidade de criar uma versão JDBC, outra JPA, outra iBatis e assim por diante, porque as classes Base serviam para colocar alguns templates methods até pra ter uma melhor reutilização do código.

Não lembro se os nomes das classes eram exatamente assim, porque ficou feião hehehe vou ver se consigo reviver o código de lá e mostro pra vocês verem o que acham, mas pelo que eu me lembre, só mudou o nome das classes mesmo, porque a maneira que foi implementado é a mesma.

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

D

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?

Y

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?

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.

D

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.

Paulo_Silveira

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.

D

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

Paulo_Silveira

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.

Leozin

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

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 =]

king_of_gods

Essa discussão do DAO está mto interessante.

Gostaria de expor meu ponto de vista.

Eu levo pelo seguinte ponto:

public class BaseDAO<T> {
      private EntityManager manager;

      /* Métodos Comuns */
}
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.

tnaires

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.

king_of_gods

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.

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.

tnaires

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.

rodrigoy

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.

Alessandro_Lazarotti

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

sergiotaborda

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.

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.

tnaires

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.

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.

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)

rodrigoy

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

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

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.

rodrigoy

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

:wink:

Paulo_Silveira

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

:wink:

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

tnaires

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.

Leozin

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)

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:

king_of_gods

Nesse debate estou vendo dois pontos importantes

1º - Encapsular as chamadas ao EntityManager OU Chamar diretamente o EntityManager.

2º - A forma de implementação do acesso ao Entity Manager ou pelo DAO, ou por Facede, ou por Repository, ou outros. Como o Paulo falou:

São bem próximas. Acho que no final acabamos chegando no mesmo lugar usando várias formas diferentes. E todas, ao meu ver, certas.

Acho que é muito mais complexo essa discussão, pq vai entrar no gosto do Arquiteto da aplicação. Um pode gostar de Usar JPADao e seus *DAO. Outros preferem usar Facede e assim é a vida. Cada um faz como acha melhor fazer, mas como o Paulo falou o problema não é a prática é o nome que está se dando as nomeclaturas.

L

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

O que acho interessante é que eu faria desse jeito. Mas estranhamente, chamo tanto “Repository” quanto “DAO” de “DAO”, porque não consigo olhar um objeto façade e dizer: “esse é de infra”, “aquele é de domínio”.

Outra coisa que eu gosto de fazer é não fazer do “DAO” um façade especial, ou de categoria diferente. Por exemplo, se estou usando o EJB3, os DAOs serão Stateless Session Beans, assim como qualquer façade de negócio; ou se estou usando Spring, os DAOs serão também beans do Spring.

Assim, no “hardcore” do sistema, haveria façades de negócio acessando os DAOs necessários; enquanto que nos CRUDs da vida, os DAOs seriam acessados diretamente pelos Actions/Components/Managed Beans/Controllers dos frameworks MVC.

Leozin
Leonardo3001:
O que acho interessante é que eu faria desse jeito. Mas estranhamente, chamo tanto "Repository" quanto "DAO" de "DAO", porque não consigo olhar um objeto façade e dizer: "esse é de infra", "aquele é de domínio".

Olha só o exemplo que o king_of_gods deu na página anterior:

public class BaseDAO&lt;T&gt; {
      private EntityManager manager;

      /* Métodos Comuns */
}
public class CarroDAO&lt;Carro&gt; extends BaseDAO&lt;Carro&gt; {
    public List&lt;Carro&gt; retornaQtCarrosVendidosUltimoSemestre() {...}
}

Esse é o melhor exemplo que o rodrigoy quis se referir pelo diagrama.

na verdade, abstraindo para Repository, seria assim:

public class CarroRepository {
    public List&lt;Carro&gt; retornaQtCarrosVendidosUltimoSemestre() {
      // algum código randômico
      Date dataFinal = new Date();
      Date dataInicial = getDataSemestre();
      return dao.buscarPorPeriodo(dataInicial, dataFinal);
    }
}
public class CarroDAO&lt;Carro&gt; extends BaseDAO&lt;Carro&gt; {
    public List&lt;Carro&gt; buscarPorPeriodo(Date dataInicial, Date dataFinal) {...}
}

Você fazer uma busca por período não é domínio. Agora, uma regra de negócio aplicada nele, aí sim fica legal.

O DAO, na minha opinião, se utilizado, não deve envolver regras de domínio e sim, somente facilitar o acesso a dados que estão persistidos em algum lugar. Encapsular e deixar as queries dinamicas, fazer inserções em N lugares, excluir logicamente etc.

Luca

Olá

A discussão está muito boa e só quero arranjar um jeito de receber por email quando este tópico for atualizado. Então vou colocar 2 links (de um cara que nem sempre gosto por ser muito JEE biased):

http://www.adam-bien.com/roller/abien/entry/jpa_ejb3_killed_the_dao
… The DAO pattern is actually no more interesting for general data access, but is still needed to access data from stored procedures, flat files etc. …

http://www.adam-bien.com/roller/abien/entry/daos_aren_t_dead_but

… you could easily use the EntityManager directly in your Service layer and if you notice some repetition or duplication just to refactor the code into one place…
… in J2EE, however, you would start with a DAO in own layer, in Java EE a DAO can just evolve but is optional…

[]s
Luca

king_of_gods

Leozin,

Podemos fazer algo muito parecido com o Repository usando Facade.

interface CarroFacade { public List<Carro> retornaQtCarrosVendidosUltimoSemestre(); }

public class CarroFacadeImpl implements CarroFacade { public List<Carro> retornaQtCarrosVendidosUltimoSemestre() { // algum código randômico Date dataFinal = new Date(); Date dataInicial = getDataSemestre(); return dao.buscarPorPeriodo(dataInicial, dataFinal); } }

Dois Patterns para a mesma solução. Duas forma elegantes de fazer a msm coisa.

Uma coisa que eu gostei pacas foi essa de só deixar o DAO “executar SQL”. Realmente, estava errado no pensamento anterior. Acho mto mais “elegante” e fácil tratar o domínio dessa forma.

sergiolopes

(voltando de férias e entrando no debate)

Me parece que a maioria concorda que o DAO como pattern para encapsular totalmente o BD para depois trocar a camada de persistência é furada. Alias, algo que as vezes nao pensamos é que mtas coisas da JPA vazam para o modelo mesmo quando usamos DAO como por exemplo as classes de ID para chaves compostas e outras ideias.

Mas pra mim parece estranho colocar o EM espalhado pelo dominio todo. Aqui to fazendo igual ao Yoshima, o Taborda e outros: uma interface pro repositorio dentro do domain model encapsulando as operacoes e outra implementacao em algum canto. As razoes principais pra isso sao tests e SoC (como o taborda falou).

Hoje eu dou o nome de DAO a essa implementacao do Repositorio por força do habito mas confesso que tbm estou atras de um novo nome. (e sinceramente ModelFaçade é horrivel)

E um ultimo adendo: como o Yoshima falou, o DAO é um padrao de infra. Mas uma coisa bem esquisita é ter um ProdutoDao na infra que depende de coisas do domínio, como a propria classe Produto. Discuti isso certa vez com o Calçado e ele falou que tambem estava se sentido incomodado com isso mas nao havia mto jeito de fazer diferente. Alguem ai falou de encarar o Hibernate/ORM como infra. Eu sinceramente to pensando em encarar o RepositoryImpl (sem chamar de DAO) como dominio e dependendo do EM que é infra.

tnaires

Só gostaria de reiterar que sugeri utilizar diretamente o EntityManager apenas para create, update e delete. Não sugeri em momento algum utilizá-lo no lugar de repositórios para recuperar os dados.

Paulo_Silveira

como eu disse… todos bem alinhados :slight_smile:

Alessandro_Lazarotti

Concordo com o que vc disse Taborda, mas não me refiro “as Services” que são adendos ao Domain Model. Me refiro as services da própria Service Layer (ou Application), ou seja, aquele modelo de Services a qual aproveitamos a própria injeção do persistence em uma SessionFacade, por exemplo.

Este é o ponto Yoshi, infelizmente elas interferem, principalmente em certos nichos. Se vc esta trabalhando com licitações, no papel de consultor da arquitetura, onde alguma “Fabulosa Fábrica de Softwares” será responsável pela implementação (e vc não sabe qual), fica muito difícil sugerir uma arquitetura que seja plenamente aderente ao domínio (aqui me refiro em todas as praticas para tal, incluindo repository). Primeiro pq modelagem e desenvolvimento comprometido com domínio, implica em cultura que a maioria das fabricas de softwares não tem.

Neste caso, descartar repositories pode diminuir o risco de “apenas utilizar o nome do padrão” e a implementação ser qualquer coisa, menos o referido. Assim, utilizar um entitymanager direto nos Services (como citei acima pro Taborda), pode ser mais viável para a realidade da equipe.

Que fique claro que não defendo sacrificar SoC, só digo que nem sempre o que gostamos ou desejamos implementar, seja de fato implementável por quem vai fazer o serviço (bem infelizmente).

Acho que depende qual fase de testes vc esta rodando Yoshi. Para testes unitários, se vc tem um repository no lugar de EntityManager/Session direto no código a ser exercitado, fica mais fácil isolar o comportamento do método criando mock de seu repositório. Se vc não tem a interface do Repository, mockar EntityManager não rola na prática (ou rola de maneira totalmente sacal) e acabamos por não conseguir testar de forma totalmente isolada a unidade, só em integração.

Emerson_Macedo

Eu comecei uma discussão relacionada faz quase 2 anos e a conclusão que cheguei foi que vale a pena ter o Repositório acessando o EntityManager.

http://guj.com.br/posts/list/74599.java

Quanto ao DAO, acho desnecessário na maioria dos casos. De qualquer forma, ainda continuo com a opinião de que quando criaram a dupla EntityManager/Criteria, a idéia que os caras tinham era um Repositório genérico com Criteria genérico. Lógico que é uma suposição minha. Confesso que na época isso me confundiu bastante, só que como a interface do EntityManager cheira a infra-estrutura e temos alguns problemas com testes me convenci de que não fica muito legal deixar o EntityManager espalhado pela aplicação. Portanto, geralmente tenho um Repositorio na frente do EntityManager quando uso JPA|Hibernate.

Ferryman

Aproveitando a discussão,

O livro Patterns of Enterprise application architecture cataloga um pattern chamado queryObject. Acompanhando a discussão, pensei na possibilidade de ter uma camada de persistência considerada como Infraestrutura, e essa camada saberia traduzir os QueryObjects para queries jpa ou qualquer que seja a tecnologia de acesso a banco.

Neste caso os queryObjects seriam objetos de negócio, como Specifications, que poderiam ser utilizados pelo domínio.

Alguem já fez uma implementação parecida com isso? Será que vale a pena?

[]s

danielbussade

Leonardo3001:
rodrigoy:
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.

O que acho interessante é que eu faria desse jeito. Mas estranhamente, chamo tanto “Repository” quanto “DAO” de “DAO”, porque não consigo olhar um objeto façade e dizer: “esse é de infra”, “aquele é de domínio”.

Outra coisa que eu gosto de fazer é não fazer do “DAO” um façade especial, ou de categoria diferente. Por exemplo, se estou usando o EJB3, os DAOs serão Stateless Session Beans, assim como qualquer façade de negócio; ou se estou usando Spring, os DAOs serão também beans do Spring.

Assim, no “hardcore” do sistema, haveria façades de negócio acessando os DAOs necessários; enquanto que nos CRUDs da vida, os DAOs seriam acessados diretamente pelos Actions/Components/Managed Beans/Controllers dos frameworks MVC.

Olá, concordo totalmente com a afirmação. Também não consigo ver essa diferença entre “Repository” e “DAO”, ate mesmo na questaos dos packages do JAva, fica estranho ter o Repository no package domain e o DAO no infra, ou persistence.
Então o que costumo ter hoje, e um DAO<T> generico que encapsule um EM, ou Session, e ter os metodos basicos, além de metodos que facilitam bastante como o findByExample do Hibernate, isso evita findByX, findByXX, ou seja so criou outro DAO se for uma pesquisa bem especfiica.

Esse DAO é chamado do Service, ou em casos de CRUD apenas, são chamados dos ManagedBeans/Actions etc.

Desta forma, consigo resolver os problemas facilmente, sem criar camada "ocas", e descenecessárias. Houve um tempo em que eu tinha:

Repository -> RepositoryImpl-> Dao->DaoImpl

Mas na minha opinião em 90% dos casos isso e um overkill, hoje tento ser mais simples possível.

Valeu

Paulo_Silveira

todo mundo do guj praticamente concordando com todo mundo
sinal do fim dos tempos? ou paz mundial? :slight_smile:

obrigado pessoal, agradeco a ajuda!

sergiotaborda

Ferryman:
Aproveitando a discussão,

O livro Patterns of Enterprise application architecture cataloga um pattern chamado queryObject. Acompanhando a discussão, pensei na possibilidade de ter uma camada de persistência considerada como Infraestrutura, e essa camada saberia traduzir os QueryObjects para queries jpa ou qualquer que seja a tecnologia de acesso a banco.

Neste caso os queryObjects seriam objetos de negócio, como Specifications, que poderiam ser utilizados pelo domínio.

QueryObject é o nome do padrão de projeto usado pelo Hibernate e pelo JPA2 nos seus Criteria.
A diferença é que para essas cadas o Criteria é atrelado ao mecanismo e portanto não é genérico.
O que quero dizer com isto é que essa ideia já usada, mas não em toda a sua potencialidade.

A ideia que melhor modela o acesso a dados é o Repositorio criando QueryObject genéricos e passando a um terceiro objeto que os interpreta para a tecnologia de acesso.

Vale com certeza a pena. Eu já usei mas com interpretação directa para JDBC. Passar pelo JPA ou Hibernate não funciona porque esse mecanismos têm vazamento para os objetos das entidades. Tlv sem anotações funcione melhor ou com mecanismos avançados do hibernate puro (sem JPA)

O Criteria Toolbox do MiddleHeaven propõe exatamente um modelo genérico guiado por critérios genéricos que tanto podem ser aplicados a um Collection como a um Banco ou a um XML. Isto permite uma clara separação entre o que é da aplicação o que é do dominio e o que é de infra. (DAO não são infra, são de aplicação ) Para isso ele implementa o padrão DomainStore que é o padrão de fato para isolamento de acesso a dados utilizando domínios.

Leozin

Ferryman:
Aproveitando a discussão,

O livro Patterns of Enterprise application architecture cataloga um pattern chamado queryObject. Acompanhando a discussão, pensei na possibilidade de ter uma camada de persistência considerada como Infraestrutura, e essa camada saberia traduzir os QueryObjects para queries jpa ou qualquer que seja a tecnologia de acesso a banco.

Neste caso os queryObjects seriam objetos de negócio, como Specifications, que poderiam ser utilizados pelo domínio.

Alguem já fez uma implementação parecida com isso? Será que vale a pena?

[]s

Pra mim, o Query Object não tem uma diferença muito grande de uma Criteria, na verdade é uma criteria simples.

Aí eu já acho viagem demais, se tu usa hibernate. Se tu tá usando JDBC por exemplo, até pode ser, mas acho que na maioria dos casos, usar Query Object foge totalmente do principio do KISS.

sergiotaborda

Na realidade é o Criteria que é um Query Object simples.
Não existe nenhuma consideração sobre o quão um QueryObject pode ser complexo. QueryObject pode ser desde um bean até … Deus sabe. Normalmente ele não precisa ser muito complexo. É o builder dele que é, ou se não houver builder, o conjunto de classes que auxiliam a criação do objecto.

Bem pelo contrário. Se fosse assim ele não seria um padrão de projeto.
Tlv não pareça mas um QO é mais simples que uma frase SQL, mesmo se não acompanhado de builder.
Só o fato de ser um objeto per se (e não apenas um string) ajuda bastante.

Leozin

sergiotaborda:
Bem pelo contrário. Se fosse assim ele não seria um padrão de projeto.
Tlv não pareça mas um QO é mais simples que uma frase SQL, mesmo se não acompanhado de builder.
Só o fato de ser um objeto per se (e não apenas um string) ajuda bastante.

Então quer dizer que mesmo você usando Hibernate, tu iria criar Query Objects mesmo tendo o Criteria nas disponível?

ps.: Sim, Criteria é uma implementação de Query Object.

rponte

Alessandro Lazarotti:

Acho que depende qual fase de testes vc esta rodando Yoshi. Para testes unitários, se vc tem um repository no lugar de EntityManager/Session direto no código a ser exercitado, fica mais fácil isolar o comportamento do método criando mock de seu repositório. Se vc não tem a interface do Repository, mockar EntityManager não rola na prática (ou rola de maneira totalmente sacal) e acabamos por não conseguir testar de forma totalmente isolada a unidade, só em integração.

Exato, é por ae mesmo, Lazarotti.

Utilizar EntityManager diretamente em vez de DAOs faz sentido (e provavelmente será mais utilizado) na camada de aplicação [Application Layer], na maioria dos casos para funcionalidades relativamente simples sem muita lógica (inclua CRUDs aqui) em que fica quase claro a não necessidade de alguma camada de indireção apenas por convenção da arquitetura. O pattern tem seu papel e deve ser utilizado onde se precisa e não em todos os lugares só porque fica mais “comodo”.

Se houver necessidade de fazer alguns testes unitários em que seja complicado mockar o EntityManager então acredito que neste lugar seja melhor um DAO ou mesmo Repository. Para os demais casos testes de integração pode ser um melhor caminho.

O importante é não generalizar tudo apenas por questões de convenções da arquitetura.

sergiotaborda

Leozin:
sergiotaborda:
Bem pelo contrário. Se fosse assim ele não seria um padrão de projeto.
Tlv não pareça mas um QO é mais simples que uma frase SQL, mesmo se não acompanhado de builder.
Só o fato de ser um objeto per se (e não apenas um string) ajuda bastante.

Então quer dizer que mesmo você usando Hibernate, tu iria criar Query Objects mesmo tendo o Criteria nas disponível?

Não.
Embora seja possivel criar um QO genérico e depois interpretá-lo para um criteria do hibernate e executar em cima do hibernate, como eu disse isso não é realmente genérico porque as entidates precisam de controle do hibernate. Então não ha vantagem em ter um QO no meio.

Veja a analogia

Arquitetura geral : algumacoisa -> repositorio -[monta] – > QueryObject | repositorio – [executa QO sobre] --> DomainStore

Arquitetura com hibernate : algumacoisa -> repositorio -[monta] --> Criteria | reposiotiro-- [executa Criteria sobre] --> Hibernate

sergiotaborda

rponte:

Se houver necessidade de fazer alguns testes unitários em que seja complicado mockar o EntityManager então acredito que neste lugar seja melhor um DAO ou mesmo Repository. Para os demais casos testes de integração pode ser um melhor caminho.

O importante é não generalizar tudo apenas por questões de convenções da arquitetura.

hummm… Uma regra importante na moda, no design e na arquitetura e que se aplica aqui é : less is more.
Menos é mais. O que singifica isto ? Básicamente significa que mesmo que vc possa fazer algo (Adicionar) isso não significa que deva.

O que separa o poder do dever e do fazer é uma linha ténue , dificil de enxergar e que requer algum tipo de visão especial tipo visão de raio-X do super-homem. O que quero dizer com isto é que é facil dizer “não vamos fazer isso porque a arquitetura manda, vamos fazer isto aqui porque é mais simples” … o buraco escorregadio aqui é o que significa “simples”. Para os mais desatentos “simples” significa “mais depressa” ou “já sei fazer”. Ora isso é uma forma muito egoísta de olha a construção do sistema. “Simples” deve significar isso mesmo “simples” e não “o jeito que eu achei mais rápido para tirar o meu da reta” ( isto tem outro nome : gambiarra).
Cada vez decide não pensar, estudar ou pesquisar sobre um problema e tira uma solução da cartola vc está criando mau design (aka gamb) que no fim vai vir de noite e engolir você… não veio ainda? é porque ainda está no fim.

Só porque existe um jmock isso significa que posso sair por ai mockando todo e qualquer objeto ?
Se levarmos à letra sim, mas se usarmos o principio do menos é mais, mockar menos é melhor. Mockar Thead seria impossível ou quase tão dificil como criar um novo mecanismo de paralelismo. Fazer um mock tem que ter um objetivo e tem que ser o ultimo recurso. Por exemplo, se vc tem um serviço com interface S e quer criar um tipo particular para usar em testes vc criar uma implementação STest. Repare que vc está criando um mock, mas não usando uma ferramenta de mock. Isso significa , então , que vc está implementando uma outra “encarnação” de S. Isso é extremante util para entender se o design de S está correto ( é desacoplado e conciso) , se o seu tratamento/modelagem de exceção é coerente e util, se os tipos de retorno e entrada são genéricos e simples de construir. Usando o jmock vc não entenderia isso.

Acho que - e não é de agora - a maioria aqui vê o filme ao contrário. Todo o mundo fala que “generalizar” é mau. Se fosse tão mau assim , será que OO seria baseado em abstrações - que são uma forma de generalização ? Não.

Quando vc pensa em design vc pensa em abstrato. Vc imagina contratos, interfaces, trocas de mensagens. Vc não imagina codigo, classes, corpos de método, ou fica preocupado com o tempo de codificação.

Codificar um hibernate da vida é extremante complexo. Existem muitos items ligados a performance. Mas o modelo ? o modelo é o mais abstrato que pode ser. E ai que está o poder dessa biblioteca, não na sua implementação.

Programadores utilizam-se dos contratos dos objetos, nãos das implementações. Então, um bom codigo, um bom design, um bom sistema é aquele que tem bons contratos.

Enfim, tudo isto para dizer que não é irrelevante a escolha entre isolar ou não o entitymanager. isolá-lo é inerentemente mais vantajoso e isso, eu acho, é muito obvio. Se essa a forma que minimiza todos os problemas, porque usar outra ? mesmo que essa outra pareça mais simples , não será a melhor.

Porquê negar o que é bom ? isso não faz sentido para mim.

tnaires

Isso não é verdade sempre. Já lidei com vários sistemas cujo ciclo de vida terminou mas ele nunca saiu daquela arquitetura inicial. E é desses casos que falo Sérgio.

Agora eu me tornei um desenvolvedor apressado, desleixado ou gambiarrento só porque decidi utilizar uma abordagem menos “generalizada” para UM projeto em particular? Claro que não.

Generalizar não é ruim. Generalizar sempre, cegamente, sem refletir sobre a necessidade daquela generalização específica e o impacto que ela vá causar na manutenção do sistema, pra mim é ruim.

Concordo com todos os seus outros pontos.

sergiotaborda

Ok. Sendo assim, será fácil para você - e todos os que concordam com você - apresentar casos reais onde uma generalização correta levou a um aumento no custo ou no esforço de manutenção.

Concordo que uma generalização mal feita possa levar a isso. É difícil aceitar que uma bem feita o faça.
Antes que alguem pergunte: bem feito neste contexto significa ter seguido os princípios de OO : Separação de Responsabilidade, Encapsulamento , Abstração, Herança e Inversão de Controle ( não injeção automática de dependência) por meio ou não de padrões de projeto.

Note ainda que todas as API padrão do java seguem estes princípios de uma forma natural em que, muitas vezes, o programador - sobretudo o novato - não as reconhece Contudo ele as usa e naturalmente ele as acha funcionais. A Collections API é um ótimo exemplo. Outros são o JDBC , a api de servlets e o swing. Todas elas respeitam OO e é isso que permite que vc esteja , por exemplo, no JDBC 4 totalmente retrocompativel com o JDBC 1.0. Vão argumentar que ao fazer um sistema ninguém está pensando que ele vai durar 20 anos como a JDBC. Mas é isso que significa baixa manutenção - daqui a 20 anos ainda posso usar as mesmas coisas que uso hoje. Posso usar mais, mas isso é bonus, o importante é não mudar o que já tenho.

Aguardo exemplos. Se quiserem abrir outro tópico estejam à vontade.

Emerson_Macedo

Fiquei um pouco confuso sobre Herança nessa sua lista de princípios OO. Acho que faltou mais algumas coisas também.

http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

[]s

sergiotaborda

Fiquei um pouco confuso sobre Herança nessa sua lista de princípios OO. Acho que faltou mais algumas coisas também.

Tentei jogar por baixo. Respeitado esses elementos vc obtem os outros a que se refere o texto e que são chamados realmente principios. Da SoC vc pode derivar todos os outros pontos que escrevi excepto abstração. E da abstração nasce a herança. Só tentei colocar em termos simples para todos podessem apresentar exemplos.
Por outro lado, as violações mais comuns de OO são relacionadas ao não comprimento das regras de algum dos elementos que citei. O uso de 'principios" não foi feliz, seria mais “elementos”.

Contudo, após os exemplos serem apresentados podemos analizar na otica dos principios que mencionou.

tnaires

Pelo que vejo nossos pontos convergiram e você já concordou de antemão:

Se você tivesse dito a frase acima sem a palavra “correta”, eu falaria o seguinte:

Um exemplo que vemos com freqüência aqui no GUJ - e que acredito que você enquadra como "generalização mal feita", assim como eu - está ilustrado abaixo:

Veja que o esforço de manutenção de uma camada de persistência que foi implementada como acima é maior do que se tivéssemos simplesmente:

sergiotaborda

[quote=tnaires]
Um exemplo que vemos com freqüência aqui no GUJ - e que acredito que você enquadra como "generalização mal feita", assim como eu - está ilustrado abaixo:

Veja que o esforço de manutenção de uma camada de persistência que foi implementada como acima é maior do que se tivéssemos simplesmente:

Sim, realmente eu vejo isso como uma generalização mal feita. Por alguma razão as pessoas adoram criar classes chamadas DAO e as nomeiam assim mesmo quando sabem que não são DAOs. Isso, para mim, é surreal. O Sindrome de DAO é um problema grave. É a gripe suina do design Java do pós EJB 2.1.

Este problema é diretamente derivado de um desrespeito ao SoC. Qual seria a diferença entre a responsabilidade do DAO e do Repositório ? A resposta comum é que o rep é para colocar coisas do dominio e o dao para coisas de infra. Mas isso nunca acontece. Se vc usar hibernate por baixo do dao e criar criterias no repositorio o dao só serve para passar a bola à frente. Não ha desacoplamento algum já que o DAO não tem qualquer responsabilidade. Se o rep criasse critérios agnosticos (que não dependem de nehuma lib) e o dao os interpretasse para criteria do hibernate, perfeito. Assim teriamos 2 responsabilidades diferentes e podemos até pensar em subsittuir o HibernateDAO por outro usando outra tencologia já que ele é um interpretador. hum… mas perai! o DAO é suposto ser um datamapper. Esse papel de interpretador não cabe em um dao. Logo esse objecto intermédio não é um dao, é um Interpreter que atua como estratégia para o repositorio. Veja que o problema não é o numero de camadas, mas a responsabilidade de cada uma. E que o verdadeiro datamapper está algures dentro do próprio hibernate.
A estrutura seria assim:

(quando vc é tentando a colocar Impl no nome da classe isso significa que vc não sabe diferenciar em quê a implementação é diferente de outra)

ProdutoRepository não precisa implementar nenhuma interface especifica chamada Repository. Contudo, ele pode. Mas aqui não estamos usando o mesmo padrão que no DAO (interface + N implementações) que é um tipo Service. Estamos usando outro padrão : Layer Super Type.
A diferença não está na implementação porque ambos usam interface + N implementações. A diferença está na responsabilidade e na abstração.

O Service tem uma interface porque define um contrato. As implementações são especificações reais de como esses contratos podem ser providos. A implementação é irrelevante para quem usa o contrato.Todas as implementações têm a mesma responsabilidade (satisfazer o contrato) e o mesmo comportamento ( dado pela interface do contrato)

O Layer Super Type tb tem uma interface, mas esta não é um contrato do qual derivam implementações. É um denominador comum entre objetos com responsabilidades diferentes que se comportam de forma semelhante. ProdutoRepository e ClienteRepository tem responsabilidades diferentes mas compatilham alguns comportamentos semelhantes como adicionar, remover e procurarTodos. Eles tem alguns comportamentos iguais, não todos. Por exemplo, produtorepo tem findProdutoComNecessidadeDeReposição que não faz nenhum sentido para ClienteRepositorio

Não consigo sublinhar o suficiente quando é importante entender os padrões. A maior parte das pessoas não os entende e isso causa problemas - alguns absurdos. Contudo, se entender SoC e seguir uma ou duas práticas não é necessário saber padrões. Pior que não entender é dizer que não é necessário e causa problemas. Ao contrário, padrões são coisas boas. Eles ajudam a criar aplicações de forma mais rápida e com menos custo e esforço de manutenção. Eles são o “caminho das pedras” , a “receita de bolo” , de fato. Sem gambiarra.
Se isto não fosse verdade não existiram livros como “Refactoring to Patterns”.

Luca

Olá

O que será este tal de SoC?

[]s
Luca

Alessandro_Lazarotti

Luca:
Olá

O que será este tal de SoC?

[]s
Luca

Separation Of Concerns

rodrigoy

Bem, minha contribuição para o tópico. Não vai responder nada, mas demonstra alguns estilos de repositórios que já apliquei.

(creio que a maior contribuição é mais sobre conceito de repositório e nomenclaturas)

Rubem_Azenha

Muito legal os seus exemplos Rodrigo!

Juk

O que eu costumo fazer é criar uma interface pro repositório e uma implementação desta interface que acessa a session (ou o entity manager). Esta implementação normalmente extende uma classe abstrata que fornece o CRUD.

Assim os serviços conhecem apenas a interface do repositório, deixando as classes mais coesas e facilitando os testes unitários. Nunca gostei muito da idéia de usar o entity manager diretamente nos services, mas lendo todo o tópico irei considerar para os casos mais simples.

vitu

Bem, minha contribuição para o tópico. Não vai responder nada, mas demonstra alguns estilos de repositórios que já apliquei.

(creio que a maior contribuição é mais sobre conceito de repositório e nomenclaturas)

Acho horrível esse tipo de nomenclatura que você utilizou.
Acho que o nome da classe deve ser algo abstrato.
Usaria esses nomes em instâncias, não no nome das classes.
Não vejo problema em um PedidoRepository. O nome deixa clara a responsabilidade da classe.
Vejo Pedidos faturados como um estado de pedido, caso o negocio mude e evolua e novos estados apareçam, teria que criar mais repositórios. Acho isso desnecessário, um PedidoRepository apenas bastaria e seria mais fácil de manter.

Sinvaldo_Junior

Boa Tarde amigos !

Bom estou iniciando alguns estudos na técnica de Mapeamento Objeto Relacional e escolhi esse framework
para iniciar os meus testes, ficaria muito grato se alguem tivesse alguma aplicação que exemplifica a utilização
desse framework. se possível me enviar eu agradeceria muito

Obrigado !

Criado 2 de agosto de 2009
Ultima resposta 19 de abr. de 2010
Respostas 72
Participantes 22