DDD + IoC\DI + frameworks web + invariantes de um objeto

Imaginem que você esta utilizando algum framework web moderno que trabalha com Pojo e coloca na sua “action” um pojo preenchido com os dados da request.
Ao mesmo tempo você esta utilizando domain driven design e algum framework parrudo de IoC\DI com o Spring.
Usando DDD, você normalmente não tera coisas como noticiaService.publica(noticia) e sim noticia.publica().
No exemplo da noticia, teriamos um código mais ou menos assim:

public Class Noticia {
//...
public void publicar() {

   // um monte de regras de negocio
    this.status = NoticiaStatus.PUBLICADO;
   // um monte de regras de negocio
   noticiaRepository.update(this);
    notificacaoPublicacaoService.notificaPublicacaoNoticia(this);  // manda um SMS pro diretor do jornal
   // um monte de regras de negocio
   
}

}

Noticia é um entity, noticiaRepository é uma interface de um Repository e notificacaoPublicacaoService é uma interface para um Service (ok, não é o melhor exemplo de service).

Usando um container DI\IoC, como o Spring, eu deixaria a instanciação do noticiaRepository e do notificacaoPublicacaoService gerenciadas pelo container IoC\DI (usando o Spring eu posso inclusive cuidar de integração com JPA e outras coisas).

Neste caso, notificacaoPublicacaoService e noticiaRepository são invariantes do objeto noticia, ou seja, para utilizar o objeto noticia eu preciso que esses caras estejam instanciados e “setados” no objeto noticia.

A questão é, como estou usando um framework web moderno baseado em Pojo, o objeto noticia é instanciado pelo framework web. Qual a melhor maneira de injetar as invariantes gerenciadas pelo container IoC\DI nas entities?

Algumas opções, boas e “não tão boas”:

  1. No construtor da Entity chamar a API do container IoC\DI para preencher as invariantes
public Noticia() {
   this.noticaRepository = ApplicationContext.getApplicationContextDeAlgumLugar().getBean("noticiaRepository");
   this.notificacaoPublicacaoService= ApplicationContext.getApplicationContextDeAlgumLugar().getBean("notificacaoPublicacaoService");
}
  1. Fazer a Entity ser gerenciada pelo container IoC\DI e deixar que ele preencha as invariantes e fazer algum gato no framework web para este usar a Entity gerenciada pelo container IoC\DI:
<bean class="example.Noticia">
 <property name="noticiaRepository">
    <ref bean="noticiaRepository"/>
  </property>
</bean>
<!-- ok, posso usar autowire, annotations, etc -->

O que vocês tem usado? Alguém já fez isso com JSF+Spring? Recomendam algum outro framework?

Não necessariamente. Você terá o que fizer sentido na sua linguagem de domínio, se isto fizer sentido então…

Eu realmente não gosto desta idéia. Acoplar uma Entity com infra-estrutura é pedir para ter problemas.

Não sei nada de JSF mas este bean não devia ser a Noticia em si e sim uma Facotory de notícias. Teu framework não te deixa usar uma Factory para objetos? Se não deixar você pode utilizar um daqueles factory beans do Spring, não?

Tem razão, mas vamos dizer que neste caso faz sentido uma construção assim.

Eu tenho esse receito também.

No meu caso é JSF, mas o principio se aplica a qualquer framework web java que preenche um pojo com os dados da request.

Eu também acho que a melhor opção é deixar que uma Factory (do Spring ou criada por você mesmo) preencha as invariantes “complexas” como o Repository e os Services cascudos. Não sei se é tão fácil fazer os frameworks web trabalharem com isso.

Alias, isso fica pior quando se trata de um objeto recuperado pelo banco de dados usando um ORM como Hiberante! Por acaso o Hibernate deixa você usar uma Entity (classe anotada com @Entty neste caso) criada por uma Factory? Ou vou ter que fazer alguma Gambi com Listeners Post-Load?

Definitivamente chamar as coisas do container pra mim não só é feio pelo DDD como fere IoC. Má ideia.

Aqui na Caelum, eu faço a segunda coisa: magia negra (negrinha na verdade) no framework pra injetar as coisas. No caso, uso VRaptor e escrevo um plugin que roda para interceptar o miolo entre o bean ser populado pelo framework e a action ser chamada.

No VRaptor, é um saco fazer essa interceptação antes da chamada da action, como é no Struts, num servlet Filter e provavelmente em muitos outros frameworks. No JSF, isso deve ser ridiculo de fazer com um PhaseListener executando antes do INVOKE_APPLICATION. (ah como eu queria que o vraptor tivesse algo como phase listeners :).

Para injetar coisas nas entities do Hibernate, dá uma olhada no hinjector do Fabio Kung: http://sf.net/project/hinjector

Interessante Sérgio!
Vou tentar algo assim.

No caso do hinjector eu já tinha dado uma olhada mas ele serviria para injetar as invariantes DEPOIS do objeto ter sido criado, o ideal era fornecer para o Hibernate um objeto com as invariantes já preenchidas.

Não sei se não procurei direito, mas é incrível como este tipo de dúvida não foi muito discutida\documentada…

No curso que fiz de JSF com o Fabio Kung na Caelum, ele mostrou um esquema de criar fábricas e declarar no seu faces-config…

Na ocasião não discutimos muito essa prática, mas me pareceu bem interessante. :slight_smile:

na minha opinião AOP seria a melhor pedida …
por exemplo, se tu compilar tudo com o AspectJ o spring pode injetar dependencias automaticamente nos POJOs de Dominio :smiley:

As duas opções que tu colocou eu não cheguei a gostar de nenhuma, exceto se tu estiver trabalhando com o JBoss Seam, ai é só dizer que o POJO é um PAJO (Plain Annotated Java Object) e uma Entity com escopo definido que o Seam gerencia isto para ti (uma das opções mais elegantes na minha opinião, mas usa AOP como eu sugeri antes :smiley: )

E fora isto, uma factory de beans de dominio seria a melhor opção na minha opinião :smiley:

No caso de JSF, os managed beans podem ser beans do Spring. Basta uma configuração explicada aqui.

Havia uma configuração que permitia a injeção de dependências mesmo que você desse new. Usava alguma coisa de AspectJ. Mas infelizmente, me fugiu à memória e não sei mais como se faz.

Olá Rubem,

Eu também acho AOP uma boa pedida. O spring dá um bom suporte para isso usando a annotation @Configurable. Dê uma procurada :wink:

Outra opção não muito conhecida mas muito show de bola é utilizar instrumentação. Não manjo bulufas, mas o Salve (http://code.google.com/p/salve/) faz isso pra você.

Espero que ajude!

[quote=Thiago Senna]Olá Rubem,

Eu também acho AOP uma boa pedida. O spring dá um bom suporte para isso usando a annotation @Configurable. Dê uma procurada :wink:

Outra opção não muito conhecida mas muito show de bola é utilizar instrumentação. Não manjo bulufas, mas o Salve (http://code.google.com/p/salve/) faz isso pra você.

Espero que ajude![/quote]

Depois de algum tempo…

Thiago, acabei não vendo a sua excelente dica com @Configurable e perdi um tempão buscando como fazer isso e acabei achando essa dica em outro lugar :frowning:
Mas valeu, a dica foi excelente, o @Configurable funcionou muito bem com os meus Pojos!

[quote=Rubem Azenha][quote=Thiago Senna]Olá Rubem,

Eu também acho AOP uma boa pedida. O spring dá um bom suporte para isso usando a annotation @Configurable. Dê uma procurada :wink:

Outra opção não muito conhecida mas muito show de bola é utilizar instrumentação. Não manjo bulufas, mas o Salve (http://code.google.com/p/salve/) faz isso pra você.

Espero que ajude![/quote]

Depois de algum tempo…

Thiago, acabei não vendo a sua excelente dica com @Configurable e perdi um tempão buscando como fazer isso e acabei achando essa dica em outro lugar :frowning:
Mas valeu, a dica foi excelente, o @Configurable funcionou muito bem com os meus Pojos![/quote]

Opa, que bom! :slight_smile:

os métodos dentro de um objeto deveriam depender apenas de seus atributos, não é?

Então, como o método publica() utiliza recursos externos ao objeto Noticia (repository e service), isto meio que fere o parágrafo anterior.

Este método traz à classe Noticia as responsabilidades de persistência e de notificação. Logo, fere o SRP.

Como AOP veio para ajudar na modularização de interesses, concordo com todos que disseram que esta tecnologia é uma boa pedida.

Por favor, corrijam-me se eu estiver errado.
Obrigado