Injeção de Dependência em Domain Model (ServiceLayer+DomainModel+Repository)

Muito tem se falado no grupo sobre DDD, Repository, Domain Model, etc.
Contudo estou tendo muita dificuldade para tornar estes conceitos práticos, utilizando JavaEE.

Por exemplo:
Quem orquestra os objetos de domínio é uma ServiceLayer, no meu caso um Stateful ou Stateless do Seam.

Meu DomainModel é constituído de Entitys(Objetos com identidade) com lógicas de negócio além obviamente de seus atributos :arrow: no meu caso ele é uma classe JPA anotada com @Entity.

@Entity
@Name("user")
    public class User implements Serializable {
        ...
     }

Na maioria das vezes as lógicas neles contidas precisam de um acesso a banco, então eles levam um (ou varios) repository consigo… como atributo da classe ou dentro do próprio método.

@Entity
@Name("user")
public class User implements Serializable {
	@Transient	
	RepositoryUser repositoryUser;
	
	...
}

Então começam alguns problemas. Meu Entity deveria receber injeção deste repository… porém como o Entity é carregado por JPA, eu não consigo injetar nenhum objeto nem por @In do Seam nem mesmo por Spring.

@Entity
@Name("user")
public class User implements Serializable {
	
	//nao funciona a Injecao pq eh em entity
	@In
	@Transient	
	RepositoryUser repositoryUser;  
	
	...
}

Uma solução seria fazer com que a ServiceLayer enviasse por “set” um repository para o Entity. Mas o código do Service iria ficar poluido, tendo um repository que ele nem faz uso, só instancia para o entity. Toda vez que fosse utilizar uma lógica de negócio do entity que utiliza acesso a banco, teria que minha outra camada setar o repository pra ele.

/*A ServiceLayer*/
@Stateless
@Name("authenticator")
public class AuthenticatorService implements Authenticator{

	@In(create=true) 
	@Out 
	User user;

	@In
	private RepositoryUser repositoryuser;

	public boolean authenticate(){

	   //setando porcamente o repositorio	
           user.setRepository(repositoryuser);
           user.authByNameAndPass();	
           ...

         }				
}

Qual seria uma outra solução que poderia ser adotada? Mesmo com o advento do JavaEE 5 com tantos frameworks de D.I. vou ter que utilizar ServiceLocator para localizar Repository nos entitys?

[]´s

[quote]… porém como o Entity é carregado por JPA, eu não consigo injetar nenhum objeto nem por @In do Seam nem mesmo por Spring. [/quote]Com Spring 2.0 vc pode injetar dependências em objetos criados fora do conteiner, vc tem até mesmo duas opções, por AOP ou por chamada explicita do BeanFactory, Urubatan corrija-me se estiver errado :wink:

Isso é mesmo possível? Impressionante!
Bom, acabei descobrindo tbm que consigo acessar qualquer componente do Seam através de Componente.getInstance(“nomeDoComponente”). Mesmo abstraindo este acesso nos objetos de entidade, não me parece uma coisa muito elegante, contudo ao que parece utilizando o Seam não tenho outra saida.

Ou Tenho?

No Hibernate (mesmo usando JPA) voce pode adicionar um Interceptor que toda vez que um bean sai do entity manager/session, o setXXXRepository é invocado.

Paulo,

Mas esses interceptors fazem parte da spec de JPA? Porque se não fizer, qual o sentido de utilizar JPA se o sistema só irá funcionar com a implementação do hibernate?

[]s

Ferry

Me falta tempo, mas eu tô tentando resolver justamente esses problemas no: http://sourceforge.net/projects/hinjector/

Bom, fiz algo que ficou bem interessante:

  • Um Aspecto que intercepta a instanciação de todas as classes do meu pacote com classes de entidades, injetando neste momento as dependências que forem necessárias. O legal é que fica completamente transparente para o desenvolvedor.

Com isso tanto faz se a instanciação for através de “new”, “jpa” ou qualquer outro framework, a injeção vai funcionar do mesmo jeito.

Veja o código usando AspectJ:


public aspect InjectEntity{

   //pointcut criado para interceptar na instanciacao de uma classe   
  // br.com.siq.entity é o pacote onde estao minhas entidades
  pointcut inject() : initialization(public br.com.siq.entity.*.new ());

 //execucao do aspecto apos concluida (advice after) a instanciacao da classe
       after():inject(){
 //obtendo o objeto em questao (joinpoint)
               Object obj = thisJoinPoint.getThis();
              
//variaveis que injetarao em um determinado atributo
               String valueOfContextVariable = null;
               Boolean createContextVariable=false;


//Procurando anotacao de injecao na classe, no caso do Seam o "In.class"
               for(Field field:obj.getClass().getDeclaredFields()){
                       if(field.isAnnotationPresent(In.class)){
                               In in = field.getAnnotation(In.class);

//se a anotacao nao tem o atributo value setado, utilizar o nome do campo
                               if(in.value().equals("")){
                                       valueOfContextVariable = field.getName();
//caso contrario utilizar o atributo value
                               }else{
                                       valueOfContextVariable = in.value();
                               }

 //verificar se esta anotado como create
                               if(in.create())
                                       createContextVariable = true;
        
//tornando acessivel o atributo caso ele seja private, para ser possivel fazer a injecao 
                               field.setAccessible(true);

                               try {
//injetando no atributo, como uso  JBossSeam obtenho a referencia atraves do
//Component.getInstance() , mas poderia ser qualquer coisa,
//como um applicationContext do Spring
                                       field.set(obj, Component.getInstance(valueOfContextVariable, createContextVariable));
                               } catch (Exception e) {
//escrever  aqui rotina de excecao
                               }
                       }
               }
       }

Na entidade, basta anotar:


@In
 private Repository repository;

O Código do Aspecto pode ser melhorado, como incluir suporte para injecao pelo método “set”, mas esse foi apenas um esboço de como pode ser feito.

[]'s

Hmmm… lógica de acesso a banco nos Entitys… estranho isso…

Lógica de acesso a banco? Onde?

A entidade tem acesso ao “repositório”, que de nada tem haver com “Lógica de acesso ao banco”. O repositório tem “intimidade” com a lógica de negócio, faz parte dela, nada mais justo que estar no modelo de domínio.

Mas concordo que fui infeliz na minha afirmação do primeiro post " precisam de um acesso a banco", o q queria dizer foi:
precisam de um acesso ao repositório”.

[quote=Lezinho]Mas concordo que fui infeliz na minha afirmação do primeiro post " precisam de um acesso a banco", o q queria dizer foi:
precisam de um acesso ao repositório”.[/quote]

Continua estranho, Entitys dependendo de Repositories…

A dependencia é injetada, entao nao vejo problema. O fato de ser um repository dentro de um entity tbm não encontro nada de errado, ambos fazem parte da lógica de negócio… mais uma vez… REPOSITÓRIO É NEGÓCIO…

Correto. A falta de entendimento das pessoas sobre Repositories x DAOs causa este tipo de confusão mas basta ler a bibliografia para entender que um Repositório é parte do domínio e por isso não há qualquer problemas em relacionar entidades à ele.

A questão é a forma como ela é injetada (lembre-se do princípio do KIS). :wink:

Os Repositories - e os DAOs dos quais eles dependem, já que vc prefere assim - dependem do Service e não dos Entitys.

Dê um olhada no caveatemptor (aplicação de referência para uso de Hibernate).

Taz, vc pde injetar por “set” sendo enviado pelo Service, como mostrei no primeiro post, gerando o problema que tbm já mostrei.

O que fiz, via annotation, faz a injeção pelo SEAM (por debaixo dos panos do aspecto), não a nada de errado nisso… a dependencia foi invertida.

Você não esta sendo claro, ou não esta entendendo.

Esqueceu de levantar a possibilidade de que vc não esteja entendendo. Dê uma olhada na aplicação de referência que te falei e nos exemplos de JBoss Seam (que não são poucos). Depois discutimos…

De fato não olhei estes codigos, assim como também não sei pq deveria. Não tenho dúvidas de como usar “repository”, e sim “tinha” de como injeta-los no load de algum framework de persistencia (o que foi resolvido via Aspect).

Portanto Taz, dispenso a discussão, o foco desta thread não foi este. Quem achou estranho usar repository em entity foi você (o que o Shoes desmistificou muito bem).

Se você esta com dificuldade de entender repositories em entities, abra uma thread sobre isso.

Boa Sorte.

Belez…

“O pior cego é aquele que não quer ver” (ditado popular)

Abraço e boa sorte (vc vai precisar se não estudar os exemplos) :wink:

Taz, eu sei como o Hibernate trabalha com interceptors, tbm sei como delegar isso para service.

Contudo não quero dependencia dos interceptors Hibernate (eu posso instanciar minha entidade atraves de “new”, nesta caso o Interceptor do hibernate não me ajudaria) e tbm não quero associar metodos de negocios do repositorio na ServiceLayer, mas sim na minha entidade de domínio. Eu não preciso ler fontes dos exemplos do Hibernate para isso, sei o que quero ou não fazer, assim como não sei de nenhuma literatura mencionando que o que fiz esta quebrando algum conceito OO.

“O pior orador é aquele que se pronuncia sem necessidade alguma” :stuck_out_tongue:

[quote=Taz]

Belez…

“O pior cego é aquele que não quer ver” (ditado popular)

Abraço e boa sorte (vc vai precisar se não estudar os exemplos) :wink: [/quote]

O problema é que as duvidas dele nao sao referentes ao Sean mas sim se tem como aplicar DDD com o Sean. Ai faz sentido ter repositorios dentro dos entities (entities do DDD).

]['s