Hibernate e Domain-Driven Design

Bom pessoal, depois de ler a famosa discussão
http://guj.com.br/posts/list/60/60916.java
sobre Repositorio x DAO ficou uma coisa martelando em minha cabeça.

Afinal de contas.

Qual é a melhor forma de se utilizar o hibernate em um DDD?

Se o correto é evitar VOs e BOs
é normal eu exibir, em uma camada de apresentação algo como por exemplo

Cliente c = //Obter o cliente de alguma forma para apresentação.

cliente.getFilialEmSaoPaulo().getContato().getEndereco().getCidade();

E para persistir os dados?
No momento em que eu vou cadastrar um cliente em uma determinada “tela faz tudo” em ajax que nela vou salvando os dados dos contatos e seus respectivos endereços. É normal se aproveitar do cascateamento do hibernate para isso e ter por exemplo um método

ClienteRepositorio.salvarCliente(Cliente cliente);

que salvaria todos os meus dados de cliente, também dos seus contatos e respectivos enderecos?

Qual é a forma que vocês fariam?

Hoje eu uso Hibernate dentro do JBoss e faço da forma que vc falou. Só que o que vc faz com o “Repositorio”, eu faço com o DAO.

Certo. Mas dessa forma eu estou expondo meu domain model (negócios) na camada de apresentação. Isto é correto?

E se por alguma força maior do destino eu ser obrigado a não utilizar o hibernate… eu serei obrigado a criar um monte de repositórios com alguns métodos que fariam o que o hibernate faz.

Logo eu teria em um certo:

List<Endereco> enderecos = cliente.obterEnderecos();

E , dentro da classe cliente, eu teria uma instancia de um EnderecoRepositorio (ou EnderecoDao) para eu retornar os dados.
Este EnderecoRepositorio teria o método

EnderecoRepositorio.obterEnderecoCliente(Cliente cliente).

E assim com as demais relações que eu tiver no meu modelo de domínio.
Ae então… voltando ao exemplo anterior:

Eu teria de implementar um cascateamento manual.

Vendo dessa forma vcs não acham que um Domain Model acaba de uma certa forma “exigindo” uma ferramenta de persistência?

(Sempre trabalhei com DDD e na maioria das vezes utilizei o hibernate ou similares e quando não utilizei tive bastante problemas desse tipo para resolver. Fico imaginando agora como seria a melhor forma de resolver sem ele ehehehe).

A melhor forma é aquela que funciona se vc alterar alguma coisa. Como vc disse se usar o hibernate directamente, e um dia vc deixar de poder usar o hibernate vc tem um problema. Mas se vc usar um objecto intermediário que encapsula o acesso ao repositorio se por detrás dos panos ele usar hibernate ou jpa ou jdbc não interessa ao resto da aplicação. O repositorio tem o proposito:

  1. resolver o problema de localização. É pelo repositorio que os objetos do dominio encontram as entidades.
  2. resolver o mapeamento de agregados*.
    2.1) de isolar o dominio da persistência. Se o software requerer persistencia, ela será encapsulada dentro do repositorio. Mas um repositorio não é um DAO. A persistencia final será da responsabilidade de um DAO

*Agregado é um padrão para criar entidades em que os objetos que lhe são especificos são apenas maniuplados por ela. O repositório tem acesso privilegiado a isso para poder mapeá-los durante a persistência.

O jogo é mais ou menos assim

Uma ação de procura é requisitada ao Repositorio
O Repositorio traduz essa pesquisa em entiddes do dominio.
O repositorio usa um DAO para obter os dados das entidade. Estes dados podem ser dados em várias tabelas ou não.
O repositorio mapeia os dados obtidos do DAO para o objeto da entidade. Por exemplo, endereço é um objeto dentro de cliente, mas na tabela ele é um cojunto de campos no mesmo registro que os outros campos de cliente. O repositorio faz esse trabalho de separar quais dados são de quais objetos.
O repositorio retorna a entidade (ou conjunto de) pretendido.

Repare que o local “fisico” onde os dados estão é responsabilidade do DAO saber e não do Repositorio.
VC poderia ter um HibernateDAO ou um XMLDAO ou um HashMapDAO ou qq outra coisa. O repositorio não se interessa por esses detalhes. Por outro lado o DAO não sabe o que são aquele conjunto de campos que ele recebe. Ele é um “burro de carga” entre o local fisico onde os dados estão persistidos e o repositorio. Apenas isso.

Claro que o Repositorio e o DAO podem ser fundidos, mas isso, quanto a mim, não é flexivel.

Então pensando dessa forma como seria o melhor isolamento da camada de persistência?

seria algo como

class Cliente
{
   /*...Inicializações, variáveis, etc...*/
   

   private List<Endereco> enderecos;

   public List<Endereco> obterEnderecos()
   {
      if (enderecos == null) 
         enderecos = EnderecoRepositorio.getEnderecosCliente(this);

      return enderecos;
   }

}

Ae se eu utilizo o hibernate, eu retorno direto a lista de endereços… se não eu não retorno.

Seria mais ou menos assim?

Eu prefiro separar os acessos ao repositório/DAO em FACADEs (Session Beans). Na realidade, o que vc está propondo é diferente (Active Record).

[quote=Taz]

Eu prefiro separar os acessos ao repositório/DAO em FACADEs (Session Beans). Na realidade, o que vc está propondo é diferente (Active Record).[/quote]

Teria como postar um exemplo?

Assim…

[code]@Stateless
public class CepFACADEBean implements CepFACADE {

@EJB 
CepDAO cepDAO;

public Cep persiste(Cep cep) {
	if (cep == null) {
		throw new IllegalArgumentException("Cep == null");		
	}
	if (isValido(cep)) {
		return cepDAO.makePersistent(cep);
	}
	return null;
}

public void remove(Cep cep) {
	cepDAO.makeTransient(cep);
}

}[/code]

Gostaria de colocar a validação nos próprios Pojos, mas não foi possível devido a um problema com o Hibernate Validator.

Seria mais como

class Cliente
{
   
  public Client(String id){
     this.id = id;
   }

  String id; 
  String nome
   List<Endereco> enderecos = new ArrayList<Endereco>();

   public List<Endereco> enderecos(){
      return enderecos;
   }

   public void add(Endereco end){
       enderecos.add(end);
   }
  
}


class ClientRepository {

     DAO dao;

     public static Client  findByID(String id){

         Client c = new Client  (id);
         Map d = dao.getDadosCliente(id); // aqui os dados vêm num map, mas poderia ser outra coisa
         c.nome = d.get("nome");
         c.enderecos = dao.getEnderecoDoCliente(id);
         
         return c;
     }
}

Sérgio agora você complicou minha cabeça.

Estamos vendo em isolar a camada de persistência…

Desta sua forma está isolado…
Mas ae não consigo enxergar a utilização do hibernate…

Seguindo este processo. com dao.getDadosCliente(id) retornando um map ou a outra coisa…
no hibernate utilizaríamos o dao.getDadosCliente(id) retornando o próprio cliente?

Mas ae a interface muda. como seria fazer um repositório híbrido?

[quote=lelis718]Sérgio agora você complicou minha cabeça.

Estamos vendo em isolar a camada de persistência…

Desta sua forma está isolado…
Mas ae não consigo enxergar a utilização do hibernate…

Seguindo este processo. com dao.getDadosCliente(id) retornando um map ou a outra coisa…
no hibernate utilizaríamos o dao.getDadosCliente(id) retornando o próprio cliente?

Mas ae a interface muda. como seria fazer um repositório híbrido?

[/quote]

:lol: :lol: :lol: Quem quiz usar o hibernate com DDD foi vc , não eu :lol: :lol: 8)

O Hibernate é mais do que um simples ORM e mais do que um DAO, ele é um novo conceito
É um mecanismo de abstração de persistência em banco de dados. Esse mecanismo é muito semelhante ao JPA (aliás é historicamente ao contrário, mas ok)
Esse mecanismo do hibernate é mapeamento entre objectos e banco (ORM) + um mecanismo de controlo de ciclo de vida (em semelhança com os entity beans do EJB) mas só funciona com bancos de dados. A sua sintaxe e mecanismo são directamente relacionados ao SQL e à noção de tabela e campo.

Politica à parte, e no essencial das coisas, existe muito campo de manobra em DDD. Nem os seus criadores concordam na implementação do repositorio. A implementação de um repositorio pode ser tão directa como o uso directo de JDBC (que aliás se percebe que pode ser usado dentro das classes elas mesmas) ou tão indireta com o uso de um DAO que não assume mecanismos de persistencia (ou seja, não parte do principio que existirá um banco de dados, mas apenas um mecanismo de persistência abstracto)
Existem opiniões divergentes quanto a se um repositorio é um dao e vice-versa. Quanto a mim não é a mesma coisa. Um dos pilares da programação OO é a separação de responsabilidade. Ela traz mais classes e complica mais o código, mas simplifica a manutenção e a alteração. Veja, entidade não é um dado, registro não é uma entidade. É por isto que precisamos de mapeamento, mas ele só não chega.

A figura do repositorio em DDD é criada , como já disse, com o objetivo principal de permitir que entidades sejam encontradas por outros objetos do dominio e não com o objetivo de persistência. Repare que nenhuns outros tipos de objetos são obtidos com repositórios. No hibernate se vc tem cliente–>Endereço vc pode pgar todos os clientes (com ou sem o endereço) ou todos os endereços (sem os clientes) já que vc está abstractamente lendo duas tabelas diferentes. Em DDD vc não pode ler os endereços em separados dos clientes , a mesmos que os endereços sejam eles próprios entidade ( como chave) per se. São duas visões bem diferentes.
Por outro lado, veja que DDD é extremamente OO apenas na definição das partes do modelo. Ou seja, DDD é uma filosofia de modelagem, não de implementação. Tanto é que podemos ver exemplos de implementações usando JDBC diretamente( e ao mesmo tempo com a sugestão de usar ORM) mas até de uma forma pouco centralizada - que estariamos à espera de encontrar num programa que usa DAO.

Enfim, vc quer mesmo implementar um repositorio e usar DDD ? Esqueça persistência. DDD é tudo sobre modelar e nada sobre persistência. Em DDD nem tudo é uma entidade (nem tudo tem um ID), mas mesmo assim pode estar numa tabela. Isto é bem diferente da filosofia do JPA/Hibernate.

Provavelmente se usar DDD e depois de ter seus objetos de dominio vc for usar Hibernate/JPA irá acabar com essas classes cheias de annotations. Ora, é neste ponto que não é claro se colocar informações de persistência no dominio é violação de principios ou não é. Alguns dizem que não ( porque as annotations são meta programação) e outros dizem que sim ( porque elas estão vinculadas À classe, para as alterar é preciso alterar a classe). Eu acho que sim, pelo simples delas dependerem do DAO. Imagine que eu tenho um DAO puro JDBC
mas tá dando um trabalho cao para manter. Ai eu decido usar JPA/Hibernate. Mas com isso sou forçado a implementar annotations nos meus objetos de dominio só porque o JPA funciona assim. (sim, eu sei que pode escrever tudo no xml, esse não é o ponto). Se a implementação JPA deu pau e tiver momentaneamente que voltar para o DAO JDBC de que me servem todas aquelas anotações ? De nada. Isso mostra o vinculo que existe entre e o DAO e as anotações ( usadas fora do DAO mas que satisfazem apenas um DAO em particular).

O que eu tentei explicar é a diferença entre a responsabilidade de um Repositório em comparação com a de um DAO, que quanto a mim, não são a mesma. Os DAO que a gente vê por ai ( sobre tudo aquelas genericos que usam Hibernate/JPA por detrás dos panos) são na realidade um hibrido (ou seria mutante?). Eles fazem ambos os trabalhos. Mas em qual camada pertence um objeto que sabe sobre o dominio e sobre os dados persistidos ?
Para forçar a separação é preciso ter os dois.

O Repositorio encapsula todas as regras de relações entre objetos do dominio, em especial de entidades com seus VO , enquanto o DAO encapsula o acesso aos mecanismo de persistência.
Usar hibernate como um mecanismo de persistência dentro de um DAO é sim um sobre-trabalho, mas isso porque a tecnologia do hibernate não está nem ai para os repositorios e os DAO. o hibernate é um mediador completo entre o objeto da entidade e o banco e vice-versa que despresa o DAO.

É uma colisão de filosofias e concerteza complica a cabeça de muita gente.

Provavelmente vc retorna um objeto ClienteMemento, ou alguma coisa assim, que será mapeado para Cliente propriamente dito. Para discutir isto em profundidade tem que estar presente o conceito de Aggregado (Aggregation) para que seja clara a distinção entre entidade e conjunto de campos. Pois só assim faz sentido ter um repositorio (coleção de entidades) e um dao (acesso a conjuntos de campos)

Sérgio,

estaria vc dizendo que não é possível implementar DDD com Hibernate!?

Acredito que DDD (“uma filosofia de modelagem”, como vc mesmo disse) pode utilizar Hibernate (uma decisão de implementação).

Realmente, no Hibernate vc pode fazer isso, mas não precisa. Vc poderia criar um DAO apenas para Cliente que retornasse também os Endereços, ignorando assim a existência de um DAO para Endereço. É uma questão de garantir que a implementação esteja adequada à sua “filosofia de modelagem”.

[quote=Taz][quote=sergiotaborda]

O que eu tentei explicar é a diferença entre a responsabilidade de um Repositório em comparação com a de um DAO, que quanto a mim, não são a mesma. Os DAO que a gente vê por ai ( sobre tudo aquelas genericos que usam Hibernate/JPA por detrás dos panos) são na realidade um hibrido (ou seria mutante?). Eles fazem ambos os trabalhos. Mas em qual camada pertence um objeto que sabe sobre o dominio e sobre os dados persistidos ?
Para forçar a separação é preciso ter os dois.
[/quote]

Sérgio,

estaria vc dizendo que não é possível implementar DDD com Hibernate!?
[/quote]

não. Não estou dizendo isso. O que estou dizendo é : se separar Repositorio de DAO e Entidade (DDD) de Entidade (de persistência) e tentar usar Hibernate para o DAO de persitencia, vc vai ter problemas de repetição (usando coisas como ClientMemento ou) ou problemas de acoplamento ( usando annotations)
Se não usar o DAO (com ou sem hibernate) e programar o acesso aos dados (com ou sem hibernate) directamente no repositorio vc está misturando responsabilidades. Se um dia precisar mudar o mecanismo de persistencia vai dar trabalho.

Imagine-se um programa cuja persistência é em xml. O programa cresce e agora é preciso usar banco de dados.
Como vc não isolou a persistencia num DAO e fez tudo no repositorio, agora vc tem que programar todos os repositorios de novo. Mas se vc tivesse usado um DAO e uma implementação XMLDAO agora era só implementar um DataBaseDAO e trocar pelo outro.

Sim então…
é esse o ponto em que eu iria chegar.

Então não existe um modelo para implementar um DAO utilizando o Hibernate?

Nos meus projetos eu utilizo o DAO e faço implementações sobre o hibernate em cima de um “HibernateDAO” onde faço todo trabalho de sessões do hibernate nesta Dao e retorno, diretamente da DAO, objeto necessário por exemplo o método

Cliente cliente = dao.obterCliente(id);

E então eu tenho alguns problemas em cima do Domain Model onde caio naqueles resultados que postei :

class Cliente  
{  
	/*...Inicializações, variáveis, etc...*/  


	private List<Endereco> enderecos;  

	public List<Endereco> obterEnderecos()  
	{  
		if (enderecos == null)   
			enderecos = EnderecoRepositorio.getEnderecosCliente(this);  

		return enderecos;  
	}  

}  
 

onde sou realmente obrigado a implementar como vc mesmo diz um ActiveRecord.

Agora seria esta a melhor forma de implementação de um domain model com o hibernate? com o padrão de ActiveRecord ?
E então numa situação onde sou obrigado a retirar o hibernate o meu objeto passa a solicitar as informações para os Repositórios…
O que você acha disso?

Vamos lá, estou estudando isso e deixe-me ver se sei responder ou ajudar alguma coisa, pq tb to tendo alguma dificuldade de entender esses conceitos, nem tanto de entender mas de ver a coisa toda se encaixando.

O Dao sabe como persistir e recuperar um objeto (não importa onde). O repositório atua no meio de campo entre os objetos de domínio e os dao. OU seja o repositório sabe quais daos instanciar e usar para salvar e/ou recuperar um determinado objeto.

Dessa forma os conceitos são diferentes, o dao tem de se preocupar com banco de dados, xml, arquivo texto, etc, ele carrega mastiga e o repositório gerencia os objetos de domínio e suas agregações. Olhando pelo outro lado, objetos de domínio deveriam trabalhar apenas com o repositório e não precisam saber ou imaginar o q é dao.

No repositório então vc teria um método List<Usuario> getUsuarioComNotaBaixaSemComentario(); (por que sua lógica de negócios exige que vc marque todos os usuários sem comentários e com notas baixas), e dentro do método vc chama o dao, monta a query.

Acho que um dos problemas principais de entender essa abordagem é a dificuldade de ver exemplos práticos disso implementados, enquanto uso de dao com hibernate vc acha montes de exemplos códigos, sugestões, com ddd, vc não acha tanto código e mais a parte teórica. O motivo disso, eu vejo que normalmente usa-se ddd em sistemas maiores e mais complexos, sendo assim difícil de ter um sisteminha de locadora ou biblioteca ou cdteca com essas idéias. Não sei quanto aos outros, mas eu gosto de pegar código e comparar com o q eu faço.

Quem sabe mais favor indicar os erros, rs


É isso ai mesmo. Muito bom resumo.

Eu acho que ActiveRecord é um retrocesso.
Eu acho que realmente não é proveitoso/prático usar Hibernate para implementar um DAO. (Porque, como já falei 1) O Hibernate já é um DAO em si mesmo 2) tem o problema do acoplamento das anotações).

Em DDD vc tem que pensar muito bem se Endereço é uma entidade. Se ele precisa realmente ter um ID proprios ou se uma referencia ao seu cliente dono é suficiente. Este gerenciamento é o proprio cerne de DDD e da noção de agregado. Repare que não deve existir um XYZRepositorio a menos que XYZ seja uma entidade (no sentido do DDD e não no sentido de ORM).

Por outro lado vc quer fazer lazy-loading dos endereços. Tudo bem que vc faça isso, mas então crie uma lazy-List e use como se fosse uma outra lista qualquer (melhor). Ou crie um LazyLoader que é usado pela classe cliente e por detrás referencia o DAO e/ou o repositorio (menos bom que o lazy-list, mas melhor que invocar o repositorio diretamente)
Esqueça por um momento o lazy loading e pense que o repositório sempre monta o cliente do zero quando ele é requisitado. Comece por ai, e depois vc encaixa as otimizações.

Enfim ,acho que primeiro é importante ter o modelo bem definido, pois isso é vital para saber quantos e quais os repositorios necessários.

Sobre Facades…

Uma aulinha…

http://www.parleys.com/display/PARLEYS/Java+EE+5+Blueprints+(JPA)?showComments=true

Direto da fonte…

http://www.hibernate.org/124.html

Sobre DAOs com Hibernate…

http://www.hibernate.org/328.html

Suas dependências, a grosso modo, ficariam assim:

Cliente -> Facade
Facade -> DAO (Hibernate)
Facade -> Pojos
Facade -> Facade (outro componente)

Olá

Só para citar uma outra opinião:

http://www.adam-bien.com/roller/page/abien?entry=jpa_ejb3_killed_the_dao

http://www.adam-bien.com/roller/page/abien?entry=to_layer_or_not_to

https://p4j5.dev.java.net/

Quero deixar claro que não é sempre que concordo com Adam Bien mas taí a opinião dele.

[]s
Luca

Taz,será que está dizendo que Repository é um Façade ? Posso até endender isso, mas o repository é só uma parte da historia do DDD. Como vc modelas as entidades ? POJO com anotações PJA ? Se sim, para quê o DAO ?
Como já disse e o Luca deixou confirmado com as suas referencias JPA/Hibernate não precisa do DAO. (Visto de outra forma, o JPA tem um DAO chamado EntityManager e o Hibernate tem o Session)

Entendamos que existe uma questão politica à volta do hibernate. É uma ferramenta facilitadora que pegou o que melhor havia no ejb, jogou independencia de banco e tornou-se um padrão na forma do JPA. Como todos os padrões eles estão ai para não serem usados- afinal EJB foi um padrão que teve que ser muito remodelado para agradar. Mas tem uma pegadinha, nem todas as persistencias são em banco de dados, nem todos os DAO são locais. O famoso problema do objetos desconexos existe porque o JPA herdou do Hibernate uma visão local. O DAO tem um alcance muito superior a isso. Depois um verdadeiro DAO abstrai o acesso a dados e não apenas oacesso a dados no banco relacional. Para aplicações web, que correm num unico servidor ( mesmo onde o clustering pode ser automatico) o fator local do JPA/Hibernate não afeta ninguem. Mas a partir do momento que o sistema é um desktop swing com servidor central o hibernate vai para o espaço. Junto com o JPA. O hibernate é um ORM end-to-end mas funciona apenas para bancos de dados. Poderiamos até pensar em um Session que é façade remoto, ou um que conversa com xml ou outro tipo de persistencia, mas a questão é que a sobre-valorização do Hibernate/JPA pode não ser uma tão boa ideia - afinal nem todo o mundo faz aplicações web.

E vou ter que dizer que nem sempre se quer um codigo esparguete sem layers nem reponsabilidades definidas criado on-the-fly por uma equipa ágil. Às vezes queremos algo que dure, não no sentido que é imutável, mas no sentido que é tão facil de mudar que sempre será moderno e atual.

Acho que que a critica a layiring não cabe em DDD quando a propria teoria é sobre criar um layer completo. Por outro lado, um Façade+DAO é uma super simplificação da intenção do Repositorio do DDD. Pelo menos a meu ver. Mas também , a meu ver, DDD não é tudo isso que dizem… como dissse no principio a implementação de DDD não é muito clara nem para os seus criadores. Por outro lado os escritos dos mesmos sempre mostram um certo span temporal demasiado grande. Frases do tipo ’ algums meses depois concluimos que o modelo precisava ser alterado e remodelá-mos as classes A e B’ - convenhamos que “alguns meses” não é propriamente pouco tempo para uma modelagem. Mas o hype do DDD irá passar ( se é que alguma vez chegou) e todo o mundo irá continuar criados seu programas ágeis sem layers nem daos apenas com a ajuda do JPA e o cuspo das annotations (na versão seguinte JPA terá Criteria, que é mais um mecanismo herdado do Hibernate).

Vamos ver no que dá.

Não. Facade é Facade. Repository é Repository. Estou usando Facades.

Sim. POJO com anotações, elas não incomodam tanto. Dê uma olhada no link que passei e entenderá melhor. JPA não é a pancéia, em alguns casos vc precisa fazer acessos mais específicos à base (com Criteria do Hibernate por exemplo). Daí, o DAO.

Sim, e estou perseguindo isso: camadas com responsabilidades bem definidas. Na realidade, é um projeto SOA e estamos utilizando também o JBoss ESB para implementar os serviços em um nível de granularidade maior. Até andei trocando umas idéias sobre Facades com o pessoal da JBoss ESB:

http://www.jboss.com/index.html?module=bb&op=viewtopic&t=115682

Não entendi essa… :roll: