DDD e EJB 3.0
Autor: Vinicius Morgado | Publicado: 2010-08-06T10:07:00-07:00


DDD é uma técnica poderosa para se construir software focado no negócio da organização e apoiado na linguagem corrente dos que irão utilizar o sistema. Dessa forma, o aplicativo é construído de "dentro para fora", permitindo que regras de negócio sejam implementadas (e testadas) bem antes da construção das outras camadas e partes importantes do sistema como telas, controles e bancos de dados.
Em algum momento, teremos que implementar funcionalidades para outras camadas&nbsp; e alguns serviços essenciais como controle de transações, object pool, injeção de dependência, persistência dos objetos de domínio, etc. Nesse post vou apresentar uma ideia geral de como poderíamos realizar essas tarefas de modo bem simples usando EJB 3.0.
Antes de tudo uma observação: se você (como eu) odeia as complexidades e complicações do EJB 2.0 e torceu o nariz no instante que leu o título desse post, saiba que a versão 3.0 é completamente diferente da anterior. Os session beans e entidades JPA são agora objetos leves e que permitem (entre outras coisas) que sejam testados fora do container que os gerencia. Se você gosta da ideia de trabalhar segundo as premissas de Eric Evans e curte criar POJOs para resolver seus problemas então EJB 3.0 é a ferramenta perfeita para suas necessidades.
Primeiramente, vamos criar uma interface genérica para nossos repositórios:
@Local
public interface Repositorio&lt;T&gt; {
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; List&lt;T&gt; buscarTodos();

&nbsp;&nbsp;&nbsp; T buscarPorID(int id);

&nbsp;&nbsp;&nbsp; void add(T object);&nbsp; 

&nbsp;&nbsp;&nbsp; void remove(T object);

&nbsp;&nbsp;&nbsp; void update(T object);
} 
Não fosse pela anotação @Local poderíamos dizer que essa seria uma interface comum escrita em Java. Aquela anotação diz que essa interface permitirá acesso a uma referência de um session bean que reside na mesma máquina que o container web. Caso precisássemos de acesso remoto, bastaria anotar a interface com @Remote para termos acesso a um enterprise bean residente em outro servidor.
Vamos agora implementar a interface Repositorio em uma classe RepositorioDeClientes: 

@Stateless
public class RepositorioDeClientes implements Repositorio&lt;Cliente&gt; {

&nbsp;&nbsp;&nbsp; @PersistenceContext()
&nbsp;&nbsp;&nbsp; private EntityManager entityManager;

&nbsp;&nbsp;&nbsp; public List&lt;Cliente&gt; buscarTodos() {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Query query = entityManager.createQuery("select c from Cliente c");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return query.getResultList();

&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; public void remove(Cliente object) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; entityManager.remove(entityManager.merge(object));
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; public Cliente buscarPorID(int id) {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Cliente cliente = entityManager.find(Cliente.class, id);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return cliente;
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; public void add(Cliente object) {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; entityManager.persist(object);
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; public void update(Cliente object) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; entityManager.merge(object);
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; 
}
RepositorioDeClientes é uma classe POJO normal, transformada em um poderoso session bean sem estado através da anotação @Stateless. O container gerencia um pool de objetos RepositorioDeClientes para servir aos usuários de seus serviços - usuários dessa e de outras aplicações. Além disso, usamos o recurso de dependency injection com a anotação @PersistenceContext, que serve para injetar um objeto EntityManager, elemento JPA responsável em gerenciar o provider OR/M usado na camada de infra-estrutura.
JPA, através da interface EntityManager, gerencia as entidades marcadas com a anotação @Entity fornecendo serviços de&nbsp; persistência para os objetos. Funciona como um "guarda-chuva" para os diversos providers de mapeamento objeto relacional existentes no mercado como TopLink e Hibernate (para o exemplo o provider utilizado foi o Hibernate).

Anotando a entidade cliente:

@Entity
@Table(name="CLIENTES")
public class Cliente implements Serializable {

&nbsp;&nbsp;&nbsp; @Id
&nbsp;&nbsp;&nbsp; @GeneratedValue(strategy=GenerationType.AUTO)
&nbsp;&nbsp;&nbsp; @Column(name="CLIENTE_ID", nullable=false)
&nbsp;&nbsp;&nbsp; protected int clienteID;

&nbsp;&nbsp;&nbsp; @Column(name="NOME", nullable=false)
&nbsp;&nbsp;&nbsp; protected String nome = "";

&nbsp;&nbsp;&nbsp; @Column(name="LOGIN", nullable=false)
&nbsp;&nbsp;&nbsp; protected String login = "";

&nbsp;&nbsp;&nbsp; @Column(name="EMAIL", nullable=true)
&nbsp;&nbsp;&nbsp; protected String email = "";

&nbsp;&nbsp;&nbsp; @Column(name="SENHA", nullable=false)
&nbsp;&nbsp;&nbsp; protected String senha = "";

&nbsp;&nbsp;&nbsp; @Column(name="TELEFONE", nullable=true)
&nbsp;&nbsp;&nbsp; protected String telefone = "";

&nbsp;&nbsp;&nbsp; @Embedded
&nbsp;&nbsp;&nbsp; protected Endereco endereco;

&nbsp;&nbsp;&nbsp; public Cliente() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.endereco = new Endereco();
&nbsp;&nbsp;&nbsp; }
&nbsp; &nbsp; //getters, setters e outros métodos&nbsp; }
Várias anotações (como @Table, @ID e @Column) dão importantes informações ao JPA de como deve ser feito o mapeamento objeto-relacional de uma maneira fácil e segura. O mais interessante é que as anotações são ignoradas pela JVM fora do contexto do container EJB e do controlador de entidades JPA. Em outras palavras, podemos testar tudo sem a necessidade de um aparato complicado que certamente prejudicaria a testabilidade dos componentes (conceito caro para quem trabalha com TDD).
Com tudo isso pronto, poderíamos ter um servlet controlador com um código parecido com esse:

public class GerenciarClienteServlet extends HttpServlet {

&nbsp;&nbsp;&nbsp; @EJB
&nbsp;&nbsp;&nbsp; Repositorio repositorio;

&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp; protected void doGet(HttpServletRequest request, HttpServletResponse response)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws ServletException, IOException {
&nbsp;&nbsp;&nbsp;&nbsp; 

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Cliente cliente = new Cliente();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RequestDispatcher rd = null;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //validações excluídas para melhorar a legibilidade 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cliente.setNome(request.getParameter("nome"));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cliente.setLogin(request.getParameter("login"));

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //dados de endereco
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cliente.getEndereco().setRua(request.getParameter("rua"));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cliente.getEndereco().setBairro(request.getParameter("bairro"));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cliente.getEndereco().setCidade(request.getParameter("cidade"));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cliente.getEndereco().setEstado(request.getParameter("estado"));

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cliente.setSenha(request.getParameter("senha"));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cliente.setTelefone(request.getParameter("fone"));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cliente.setEmail(request.getParameter("email"));

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repositorio.add(cliente);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.setAttribute("cliente", cliente);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.setAttribute("mensagem", "Cliente cadastrado com sucesso!");

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rd = request.getRequestDispatcher("/cadastroCliente.jsp");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rd.forward(request, response);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception e) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //gero log, direciono para página de erro, etc
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; }

A anotação @EJB injeta uma instância de RepositorioDeClientes e passa a controlar o seu ciclo de vida, facilitando muito o trabalho do desenvolvedor. Quem fica responsável pela criação do objeto é o&nbsp; próprio container, que também se encarrega de sua destruição. O container irá criar um pool de objetos para otimizar o desempenho da aplicação. É importante observar que um servlet compartilha a mesma instância com vários clientes e por isso não devemos injetar um session bean @Stateful pois esse manterá o estado do objeto entre todas as requisições. Como nosso EJB do exemplo é&nbsp; anotado com @Stateless podemos injetá-lo sem problemas.
Concluindo, nessa breve introdução, vimos como é viável trabalhar com um design limpo (orientado ao domínio e utilizando POJOs) e utilizar os recursos oferecidos pelo EJB de maneira fácil e descomplicada. A maior parte da "magia negra" do EJB é devida aos recursos oferecidos pelas anotações Java. Transformar uma classe em um poderoso enterprise bean apenas usando uma anotação @Stateless é simplesmente bom demais para ser ignorado.

Para saber mais:&nbsp;
http://www.amazon.com/EJB-3-Action-Debu-Panda/dp/1933988347
http://www.amazon.com/Enterprise-JavaBeans-3-1-Andrew-Rubinger/dp/0596158025 (previsão 09/2010)