Repository x Spring x Hibernate

40 respostas
J

Olá,

Tenho uma dúvida conceitual sobre Repository que seria o seguinte:

Imaginem que tenho uma entidade de negócio Conta, mapeada e persistida pelo hibernate. Essa entidade possui um método getSaldo() que me retorna o saldo atual.

A implementação de getSaldo() seria a seguinte:

public BigDecimal getSaldo() {
    return this.repository.getSaldo(this);
}

O saldo será o resultado de todos os créditos menos os débitos. Isso poderá estar no banco ou não.

Considero que getSaldo() faça parte do negócio, portanto, faz sentido chamar um Repository dentro da entidade para retornar o saldo.

Agora a dúvida: sendo que eu utilizo Hibernate e Spring, qual seria a melhor forma de injetar o Repository na entidade?

Obrigado a todos.

40 Respostas

P
  1. Criá-la através de uma Factory
  2. Fazê-la usar um Service, se fizer sentido no domínio
J

E como implementar isso de forma menos “intrusiva”?

Seria interessante, por exemplo, fazer:

Cliente cliente = daoCliente.get(1);
cliente.getConta().getSaldo();  // tosco, mas imagine que cliente tivesse apenas uma conta, pra simplificar...

ou ainda

Conta conta = new Conta();
conta.getSaldo();

Já que estou usando spring, seria interessante injetar o Repository nesses caras, mas nesses casos não teria como fazer isso de forma totalmente transparente.

Sei algumas formas de resolver isso, mas nenhuma me cheira muito bem, não sei se é coisa da minha cabeça… :roll:

P
  1. Voce ta falando de DAO ou Repoitorio?
  2. Seu segundo exemplo nao faz muito sentido. Voce acabou de criar a conta, que saldo ela vai ter?

Da um caso de uso de exemlo.

J

Minha questão é relativa ao repository. Você deve estar fazendo essa pergunta pelo “daoCliente” do exemplo, né? Esse é um dao mesmo que tá me retornando um cliente que possui referência a uma conta - como eu disse, é meio tosco o exemplo, mas não me veio nada melhor na cabeça.

A questão é: em que momento o repository que é referenciado por conta vai ser injetado nele (Veja que eu consultei um cliente, não uma conta)? Qual a melhor forma de fazer isso?

Nesse caso não faz muito sentido mesmo. Mas ainda assim, poderia retornar 0. Podem haver casos que faça sentido, como falei estou sem exemplos melhores. Mesmo assim, se não fizer sentido, o método está lá… o que seria mais correto? Um IllegalStateException? Um valor default?

Não estou com um problema real, é mais uma dúvida arquitetural e conceitual mesmo… e nem é por falta do que fazer… rs.

P

Porque você não injeta ao recriar o objeto no DAO?

J

Eu imaginei que você diria isso, por isso eu não fiz direto daoConta.get() e sim daoCliente.get().

Acho que fica complicado eu fazer isso no dao porque eu poderia ter cliente.getBanco().getConta().getSaldo(). Ou seja, quem vai criar todo esse grafo de objetos pra mim é o hibernate. Eu teria que sair varrendo eles pra injetar esse cara? E se eles estiverem vindo por lazy pra mim, vou forçar o seu carregamento? Ruim isso, né?

Uma idéia pra injetar o repository seria no interceptor. Funciona. Parece ser a solução menos intrusiva que eu encontrei até agora. Mas ainda assim não me agrada. O que acho ruim nesse caso é que saio um pouco do controle do spring (EU estou garantindo que ele será injetado) - e já que estou usando spring, ele deveria cuidar disso pra mim. Vou ter que criar uma fábrica pra isso, ou chamar um bean do spring diretamente… não gosto muito disso.

Ele também não funciona com um objeto em que EU dei new.

Mas aí entra outra questão: como este método deveria se comportar nesse caso? Talvez com uma das opções que eu disse no meu post anterior. Qual a sua opinião?

P

Quando sugeri DAO eu não pensei em Hibernate ou qualquer infra-strutura. Se você está usando Hibernate pode sim usar interceptors sem problemas.

Lembre-se que o Spring não faz que nada, e em especial ele não faz persistência (recriar e salvar). Na verdade o pring sequer trabalha com Entity, le trabalha com Services.

Para o que foge ao seu escopo o Spring trabalha em conjunto com outros frameworks e bibliotecas, então se a solução passa por Hibernate e eus interceptors isso não é problema.

J

Mas essa seria uma forma “usual” de se injetar reporitories em entidades de domínio?

P

Depende do que você chama de usual. Essa é uma forma de fazer, não é a mais indicada para todos os casos.

Na verdade não é muito comum -apesar de não haverproblema real- ter uma Entity que acessa Repository diretamente.

J

Mas essa não é uma das idéias do Repository??? Se não, quem deveria retornar o saldo nesse caso? Cade os objetos “inteligentes”? :roll:

A

No Conexão Java eu levei uma conversa com o Paulo Silveira sobre este assunto e um ponto importante foi levantado na utilização de interceptors do Hibernate neste caso, ele pode acarretar problemas caso em algum lugar do sistema você instancie diretamente a entidade (caso alguém esqueça de setar o Repository neste caso: NPE).

Eu optei por utilizar AOP, marcando um pointcut na instanciação do objeto, com isso tanto faz se quem cria o objeto é o JPA, o conteiner, o porteiro, a faxineira ou eu mesmo com um new. Na thread abaixo eu postei um modelo de aspecto com comentarios que talvez possa lhe ajudar. No código em questão eu utilizei o Jboss Seam, mas não muda muita coisa se você utiliza o Spring:

http://guj.com.br/posts/list/70275.java#369353

Uma dúvida quanto a sua modelagem:
Os dados necessários para a tomada de saldo no classe Conta, como créditos e débitos, não fazem parte natural da modelagem desta mesma classe? Não faz mais sentido você trabalhar com estes atributos (sendo classes compostas ou não)diretamente no modelo, fazendo de getSaldo um método que manipule estes dados?

P

@J2Alex
Não extamente. A idéia é que os objetos de negócio podem se comunicar e como falei no meu post não há problema em fazer isso, so Não é comum. Mas existem milhões de maneiras de implementar isso sem chamar o Repository diretamente, Lazy Loading é uma delas, ter o saldo como uma tributo é outra.

@Lezinho
Não ia ser mais fácil apenas criar uma Factory e não deixar o objeto ser instanciado sem ela?

A

Hi Phillip …
A equipe utilizando DIP por todo o lado nas outras classes, poder fazer um simples:

@In
Repository repository;

… em uma entidade dá um ar de naturalidade no código :wink: .

Além de que com isso, não é apenas um repositório que você pode injetar, mas qualquer classe… como Gateways.

P

Acho que em vez de DIP (dependency Inversion Principle) você quis dizer DI (dependency injection), não?

De qualquer modo, pensei melhor e já que você já está usando Spring e ele tem um bom suporte à AOP não vejo porque não usar. Se tivesse que adicionar algo só ara isso, entretanto, usaria o mais simples, Factories e amigos.

J

Lezinho,

Aspectos era justamente uma das outras alternativas que eu tinha pensado além dos interceptors… vou dar uma olhada no seu post.

E calcado, eu continuo não entendendo o que você diz em “não ser comum”. O próprio exemplo de Fowler sobre Repository mostra algo semelhante a isso e isso me parece bem lógico.

P

Após usar DDD por alum tempo você ai ver que quase sempre quem acessa o Repository é um Service. Ñao é uma regra, é o que geralmente acontece.

A

Um levando ao outro…

P

São coisas difernetes e não necessariamente relacionadas.

Com DIP eu digo que dependo da abstração X. Quem vai me dar uma implementação disso, se é uma Facotry ou não, não importa.

Com DI eu digo que tenho uma dependência Y. Se essa dependência é uma abstração ou uma implementação (ou eja, não DIP) não importa.

A

… ok Shoes, aplicando “refactoring” no que disse:

Se a equipe utiliza abstrações resolvidas por injeções por praticamente todo o sistema,
extender isso as entidades parece conveniente.

C

Por isso geralmente acontece do service ficar enorme. Aí recorremos novamente a AOP ou DI.

Criar entities com repositorio via factory pode ser uma alternativa interessante.

P

Nao entendi porque o Service ficaria enorme. Afinal, voce nao precisa ter um Service, nem deveria, e sim dividir por conceitos de negocio.

Acho que perdi algo no seu argumento, nao entendi a relacao.

C

pcalcado:
Nao entendi porque o Service ficaria enorme. Afinal, voce nao precisa ter um Service, nem deveria, e sim dividir por conceitos de negocio.

Acho que perdi algo no seu argumento, nao entendi a relacao.

Eu falo de Service como facade.

Vocês estão falando de servico de negocio?

A

É o velho conflito da Service Layer versus Domain Services… algumas vezes podem ser os mesmos, outras não …

A

Se você utiliza Seam por exemplo, você pode invocar diretamente do front-end (Páginas, Facelet, GWT …)suas Entities, Repositories, Services, controlar o fluxo de páginas via jPDL, tudo sem necessitar obrigatoriamente de Façades para interagir com o modelo.

Em outros casos algum intermédio é necessário, para por exemplo exibir alguma mensagem informativa (não de exceptions, para isso o Seam cuida sozinho com poucas configurações em xml). Neste caso de intermédio, não se trata de Services de domínio e não devemos confudir como tal.

C

Bem, eu nao concordo que eles possam ser os mesmos. Um reside dentro do dominio o outro nao.

Eu concordo que nem sempre é necessario uma service layer.

Assim como eu acho que nem sempre é necessario um domain service apenas para acessar repositorios.

C

Lezinho:
Se você utiliza Seam por exemplo, você pode invocar diretamente do front-end (Páginas, Facelet, GWT …)suas Entities, Repositories, Services, controlar o fluxo de páginas via jPDL.

Pode claro, mas IMO, não deve! :slight_smile:

ps: pode me chamar de purista.

A

Na prática Carlos, isto pode acontecer.
Já tive projetos com requisito de expor todo o negócio em EJB 3.0.

Para isso, Façades estavam por todo o sistema. Mas como ela era POJO (EJB 3), em muitos casos servia perfeitamente como um Domain Service (tanco conceitualmente como na aplicabilidade), em outros ela fazia apenas a ponte da camada de serviço. Não é sempre que isso acontece, mas quando você trabalha com EJB3 e Seam, é normal você se deparar com esta situação.

A

cmoscoso:
Lezinho:
Se você utiliza Seam por exemplo, você pode invocar diretamente do front-end (Páginas, Facelet, GWT …)suas Entities, Repositories, Services, controlar o fluxo de páginas via jPDL.

Pode claro, mas IMO, não deve! :slight_smile:

ps: pode me chamar de purista.

E pq não devo?
O que eu perco invocando um repository em uma página, como:

<h:dataTable value=#{clienteRepositorio.clientesComDivida} ...>
C

Lezinho:
cmoscoso:

Bem, eu nao concordo que eles possam ser os mesmos. Um reside dentro do dominio o outro nao.

Eu concordo que nem sempre é necessario uma service layer.

Assim como eu acho que nem sempre é necessario um domain service apenas para acessar repositorios.

Na prática Carlos, isto pode acontecer.
Já tive projetos com requisito de expor todo o negócio em EJB 3.0.

Para isso, Façades estavam por todo o sistema. Mas como ela era POJO (EJB 3), em muitos casos servia perfeitamente como um Domain Service (tanco conceitualmente como na aplicabilidade), em outros ela fazia apenas a ponte da camada de serviço. Não é sempre que isso acontece, mas quando você trabalha com EJB3 e Seam, é normal você se deparar com esta situação.

Concordo em relação à visão que o cliente tem do dominio. Pra ele a fachada, quando existe, é o dominio.

Mas quem implementa o dominio sabe diferenciar o domain service da service layer.

C

Lezinho:
cmoscoso:
Lezinho:
Se você utiliza Seam por exemplo, você pode invocar diretamente do front-end (Páginas, Facelet, GWT …)suas Entities, Repositories, Services, controlar o fluxo de páginas via jPDL.

Pode claro, mas IMO, não deve! :slight_smile:

ps: pode me chamar de purista.

E pq não devo?
O que eu perco invocando um repository em uma página, como:

<h:dataTable value=#{clienteRepositorio.clientesComDivida} ...>

Caso os requisitos do cliente do repositorio sejam alterados para que ele acesse o dominio remotamente você terá um trabalho maior para evitar que seus objetos sejam distribuidos.

É necessário cautela caso você nao tenha certeza se isso pode acontecer.

A

cmoscoso:

Mas quem implementa o dominio sabe diferenciar o domain service da service layer.

Mesmo se o Domain service é a própria fachada remota para um cliente (dispensando a service Layer neste caso , já que um simples metadado resolve tudo)?

Bom, a tecnologia foi simplificada que muitas vezes um próprio elemento do Domínio (como um service), pode assumir um outro papel sem afetar o próprio modelo (só lamento que as anotações em java são tão dependentes), é isso.

cmoscoso:
Caso os requisitos do cliente do repositorio sejam alterados para que ele acesse o dominio remotamente você terá um trabalho maior para evitar que seus objetos sejam distribuidos.

É necessário cautela caso você nao tenha certeza se isso pode acontecer.

Eu posso distribuir diretamente o repositório, ele é uma interface. Em caso mais extremos, o método pode ser refatorado caso o cliente radicalize seus requisitos, de maneira a otimizar as chamadas remotas. Caso utiliza-se Façade, o trabalho seria o mesmo, com o acrescimo da possibildade do cliente nunca mudar o requisito de distribuição (foram raros os projetos que ví onde o cliente exigiu: (Agora eu quero que isso seja um EJB).

C

Lezinho:

Mesmo se o Domain service é a própria fachada remota para um cliente (dispensando a service Layer neste caso , já que um simples metadado resolve tudo)?

Bom, a tecnologia foi simplificada que muitas vezes um próprio elemento do Domínio (como um service), pode assumir um outro papel sem afetar o próprio modelo (só lamento que as anotações em java são tão dependentes), é isso.

Aí agente volta à diferença entre os conceitos. Se todo seu dominio se resume a apenas esse domain service, ok, mas geralmente um dominio é maior do que isso, o domain service representa um conceito do seu negocio, ao contrario da facade que representa todo o seu dominio atraves de uma interface simplificada.

No caso de remoting, um domain service é um objeto, uma service layer nao, ou seja, domain service não deveria ser distribuido. A velha historio de que nao devemos distribuir nossos objetos.

A

Uma fachada que representa todo meu Domínio? Acho que estou entendendo errado, ou você sugere mesmo uma Mega Booster Super Façade que engloba todo meu domínio? Sinceramente eu acharia isso perigoso, em vista que meu domínio pode não ter uma distribuição homogenia por um único meio.

Concordo que as façades são interfaces simples para o cliente que podem agregar serviços e diminuem a carga na distribuição, isso é fato e utilizo desta forma quando necessário, mas no mínimo aplicar ele por caso de uso. Nisso, usando DDD, em determinados momentos você pode notar que alguns Domain services agregam (não necessariamente e não comumente) atividades que podem compreender perfeitamente a um Caso de Uso, e isso faça sentido para o domínio… neste caso ele é tanto o objeto de domínio que oferece serviços relacionados a entidades ou repositorios como pode ser uma fachada remota (é só anotar… afinal de contas pode compreender um Use Case).

Concordo e apoio, eu prefiro distribuir a aplicação do que os componentes (cluster web+business no lugar de EJBs clusterizados). Mas requisitos são requisitos… se um cliente quer que exponha um serviço de forma remota para determinado caso de uso, ele terá… da forma que quiser (webservice SOAP, Rest, JSon, EJB, etc).

C

editado: acho que o Phillip não precisa esclarecer nada :slight_smile:

Sei la, caso você possua um Super Mega Booster Dominio… :wink:

A

Acho que esta havendo uma confusão entre Domain Models e Domain Objects.
Quando me refiro ao domínio, estou me referindo ao todo do domínio e não uma única classe (apenas para ser claro) e nesta caso uma única façade, é no mínimo, “exótico”.

C

Lezinho:
Acho que esta havendo uma confusão entre Domain Models e Domain Objects.
Quando me refiro ao domínio, estou me referindo ao todo do domínio e não uma única classe (apenas para ser claro) e nesta caso uma única façade, é no mínimo, “exótico”.

Sim, estamos falando da mesma coisa.

Você tem razão, clientes não devem depender de interfaces que eles nao utilizam mas meu ponto era que apesar de as vezes a facade nao se fazer necessaria os conceitos continuam sendo independentes.

Eu apenas acho simplista dizer nessas situacoes que sao conceitos iguais, a nao ser do ponto de vista do cliente que as utiliza.

Ficamos combinado assim? :thumbup:

A

Mas é isso mesmo Carlos. :thumbup:

O conceito não muda (o Domain Service tem seu papel e a Service/Application Layer tem outro). Apenas achei relevante mencionar que algumas vezes podemos ver classes acumulando (por razões de pontos de vista distintos da aplicação) mais de um papel e normalmente isto recai sobre os Services mesmo.

R

Só voltando um pouco a dúvida inicial :slight_smile:

Uma outra solução já que você está se utilizando do Spring seria utilizar a anotação @configurable nas tuas entidades, dá uma olhada aqui, isso deve te ajudar, http://debasishg.blogspot.com/2007/02/domain-driven-design-inject.html

Abraços.

A

A alternativa seguinte seria uma possibilidade para o caso simples em que o repositorio possui uma implementação única… se não for o caso, poderia ser utilizado um Registry.

Nesta implementação, o hibernate chama o construtor privado para criar o objeto, que então se encarrega de instanciar sua dependência. Para o caso de instanciação via operador new, o que pode ser útil para testes de unidade, fica a cargo do cliente a criação do objeto num estado consistente, o que inclui a injeção da dependência com o repositório (ou então coloca-se isso numa Factory).

@Entity
public class Conta {

// outros atributos

@Transient
private Repository repository;

// construtor privado usado apenas pelo Hibernate para instanciar o objeto

private Conta() {

repository = new DaoQueImplementaRepository();

}
public Conta(Repository repository) {

this.repository = repository;

}

// outros metodos

}

Isso resolve o problema proposto? ou cria outros problemas?

Abraços

H

Sei que já faz muito tempo que este POST foi escrito e com certeza você já deve ter encontrado alguma solução. Mas caso queira conferir mais uma maneira de resolver seu problema, sugiro que leia este post: http://submundojava.com.br/wordpress/2010/04/04/injetando-repositorios-em-entidades-com-spring/

Abraço.

Criado 11 de março de 2008
Ultima resposta 4 de abr. de 2010
Respostas 40
Participantes 7