sexta-feira, 6 de agosto de 2010

DDD e EJB 3.0


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  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<T> {
   
    List<T> buscarTodos();

    T buscarPorID(int id);

    void add(T object); 

    void remove(T object);

    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<Cliente> {

    @PersistenceContext()
    private EntityManager entityManager;

    public List<Cliente> buscarTodos() {

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

    }

    public void remove(Cliente object) {

            entityManager.remove(entityManager.merge(object));
    }

    public Cliente buscarPorID(int id) {

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

    public void add(Cliente object) {

            entityManager.persist(object);
    }

    public void update(Cliente object) {

            entityManager.merge(object);
    }
   
}

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  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 {

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

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

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

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

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

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

    @Embedded
    protected Endereco endereco;

    public Cliente() {
        this.endereco = new Endereco();
    }

    //getters, setters e outros métodos 
}

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 {

    @EJB
    Repositorio repositorio;

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

        Cliente cliente = new Cliente();

        RequestDispatcher rd = null;

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

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

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

            repositorio.add(cliente);

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

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

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

    }

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  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 é  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: 

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)

6 comentários:

  1. Legal vc falar disso aqui pq no meu blog eu não me permito falar de tecnologias específicas. EJB3 (Java EE 5) só não é mais legal que EJB3.1 (Java EE 6). Com EJB 3.1 a interface não é necessária (embora em alguns casos ela seja bastante útil) e o container de dependency injection (CDI: Context and Dependency Injection) permite anotar e injetar qualquer POJO. Inclusive, o Weld (RI do CDI) pode rodar fora do App Server. Já fiz uns testes com ele no Tomcat. A DI é toda por anotações e muito flexível.
    Agora falando um pouco de interfaces, apesar do domínio ser muito bom pra conversar com o cliente, um protótipo de telas é excelente para validar requisitos, especialmente com aqueles clientes com os quais você repassa os requisitos e casos de uso (ou seja como for que você capturou as informações) e ele apenas "assina aquele monte de papel".
    No entanto, é uma faca de dois gumes e aqui conhecer o seu cliente faz toda a diferença. Por um lado, você pode fazer protótipos evolutivos, ou seja, as telas praticamente prontas, bastando implementar a camada de negócio e integrá-las. O esforço é todo ou quase todo aproveitado. Por outro, há aqueles clientes que ao ver as telas já te interrompem com um "Já?! Posso usar semana que vem?!". Já querem que os botões do protótipo façam alguma coisa. Isto pode aumentar a pressão por prazos e custos no projeto e complicar um pouco a vida de quem está negociando estas coisas. Neste caso, o bom é o esquema de wireframes mesmo, que tem um custo de produção muito menor e nada, exceto o conhecimento capturado é aproveitado. Mas pelo menos seus requisitos foram bem validados e o cliente não precisa ficar imaginando as telas.

    ResponderExcluir
  2. Fala Peter,

    A minha filosofia é um pouco diferente da sua: eu posso escrever sobre qualquer coisa desde que eu não foque toda hora em uma mesma tecnologia. :-)

    Acho legal não precisar mais da interface para os session beans. Em .Net isso aconteceu também, as interfaces pros componentes deixaram de ser necessárias, simplificando o desenvolvimento. Entretanto, continuo achando útil tê-las, mas o bom é que agora é uma questão de escolha.

    Escrevi sobre a versão 3.0 por que é a que conheço mais. Por que você não abre uma exceção é escreve sobre a 3.1 no seu blog? hehe

    Quanto às telas sou da mesma opinião: o perigo é o usuário achar que o sistema já está "pronto" e gerar certos desentendimentos quanto a esforço e prazo.

    Abração

    ResponderExcluir
  3. Professor, blz? Maneiro o Post. Já comecei a estudar EJB para a certificação. Tem coisas ainda que não entendo a fundo, mas teu post deu para seguir um passo a passo maneiro.

    Parabéns ae! [=

    ResponderExcluir
  4. Fala cara,

    Cheguei de viagem ontem e hj vim dar uma conferida aki no blog...

    Eu não conheço a especificação EJB, por isso não posso dar opiniões mto técnicas, mas gostei do que li, design orientado a domínio, código enxuto e como vc disse, não compromete "a testabilidade dos componentes". Merece a minha atenção!

    Abçs

    ResponderExcluir
  5. Jake ("mineiro"),

    Teu blog tá show de bola! Já to lá te seguindo... hehe

    Rafa,

    O EJB 3.0 é bom demais! Vale muito olhar a versão 3.1 (Java 6).

    Editei o post com um link do pré-release (atualizado) da O`REILLY que deve estar bem interessante.

    Abs a todos,

    ResponderExcluir
  6. Fala grande Vinícius!

    Após estudar e analisar nas aulas sobre EJB 3.0 e começar a ler o livro "EJB3 em ação" estou tendo uma visão bem ampla de como construir aplicações ricas com simplicidade e legibilidade.
    Se a tecnologia do EJB3 elevou os padrões de desenvolvimento server-side, não quero nem saber como será o EJB3.1.... rsrs

    Acredito que será o mais novo "Minority Report!!!" rsrss onde iremos parar rsrsrs!!!

    forte abraço amigo da turma do infinito!!!

    ResponderExcluir

Related Posts with Thumbnails