JEE é um acrônimo para Java Enterprise Edition (JEE)
Os tópicos inicialmente abordados serão sobre padrões da camada de apresentação, em particular, sobre os padrões Intercepting Filter, Front Controller, Context Object e Application Controller.
Antes de darmos início à explicação sobre os quatro padrões acima citados, vamos revisar a listagem de padrões agrupados por camada:
Padrões da Camada de Apresentação (Presentation Tier Patterns):
Intercepting Filter (implementar um login required, Middleware Classes do Django)
Front Controller (direcionar a requisição para views (Django) específicas)
Context Object (criar um objeto "customizado")
Application Controller (Direciona para cada ação específica do CRUD)
View Helper (Como as templatetags do Django)
Composite View (header, body_content, footer)
Service to Worker (Como um Template Context Processor do Django, executado antes do controle ser passado para a view)
Dispatcher View (Executado depois que o controle é passado para a view)
Padrões da Camada de Negócio (Business Tier Patterns):
Business Delegate (acesso a serviços de forma transparente, sem detalhes de implementação)
Service Locator (Uma busca de forma transparente)
Session Façade (interface mais simples para clientes remotos)
Application Service
Business Object (Um objeto de uma classe qualquer)
Composite Entity (Basicamente um modelo com várias chaves estrangeiras)
Transfer Object (Recupera um objeto completo para evitar RPC ao ler cada atributo)
Transfer Object Assembler (Vários objetos em um só)
Value List Handler (Paginação)
Padrões da Camada de Integração (Integration Tier Patterns):
Data Access Object (DAO. Como um ORM para manipular a base de dados)
Service Activator (Requisições de forma assíncrona)
Domain Store
Web Service Broker
Camada de Apresentação
Intercepting Filter
Problema: interceptar e manipular uma requisição e uma resposta antes e depois que a requisição é processada.
Solução: utilizar um Intercepting Filter como um filtro plugável para pré e/ou pós-processar requisições e respostas dessas requisições. Dessa forma, filtros fracamente acoplados são combinados de forma encadeada, podendo ser adicionados, removidos e recombinados a qualquer momento.
Exemplos de aplicabilidade:
Imagine uma situação em que cada requisição do cliente deve ser interceptada para fins de autenticação e/ou controle de acesso, registro (logging), abertura de um contexto transacional ou qualquer outra funcionalidade que seja mais ou menos genérica. Uma opção seria replicar essa lógica em todas as páginas ou todos os servlets da aplicação. Mas isso seria um desastre do ponto de vista de manutenibilidade e robustez do código, uma vez que haveria replicação de código e nenhuma garantia de que o processamento necessário seria de fato invocado.
Daí a necessidade de um mecanismo que reúna as seguintes vantagens:
Processamento centralizado, genérico e não invasivo, independente da lógica de negócio da aplicação.
Possibilidade de pré-processar uma requisição, alterando-a, por exemplo, para fins de conversão de dados.
Possibilidade de pós-processamento da resposta de uma requisição para alterá-la, converter dados para apresentação, etc.
Flexibilidade para adicionar e/ou remover lógica de pré ou pós-processamento sem interferir no core da aplicação.
A imagem a seguir exemplifica o uso de um Intercept Filter:
Para que o filtro acima definido seja ativado, é preciso configurá-lo no deployment descriptor da aplicação, também conhecido como arquivo web.xml. No código abaixo, a classe br.timasters.MeuFiltro é configurada para interceptar todas as requisições a páginas JSP da aplicação (*.jsp):
Front Controller
Problema: Deseja-se um ponto de acesso centralizado para tratamento de requisições à camada de apresentação.
Solução: Usar um Front Controller como o ponto inicial de contato para tratar um determinado conjunto de requisições. Tal conjunto pode englobar todas as possíveis requisições para a aplicação, ou apenas as requisições relacionadas a uma determinada funcionalidade, permitindo respectivamente a existência de um único controlador para toda a aplicação ou mesmo um controlador por funcionalidade.
O Front Controller centraliza lógica de controle que de outra forma estaria replicada, e gerencia as principais atividades relacionadas com o tratamento de requisições.
Exemplos de aplicabilidade:
Atualmente, o papel desempenhado pelo Front Controller costuma ser encapsulado por frameworks de mercado como Struts, Java Server Faces e vários outros. Em geral, o Front Controller é implementado como uma servlet que centraliza a lógica comum ao processamento de todas as requisições e em seguida redireciona o processamento da requisição para o controlador mais adequado.
Context Object
Problema: Deseja-se evitar o uso de informações de sistema específicas de um protocolo fora de seu contexto.
Solução: Usar um Context Object para encapsular informações de estado de forma independente de protocolo, para que possa ser compartilhada por todas a aplicação.
Exemplos de aplicabilidade:
O código abaixo mostra um ContextObject que encapsula informações de sistema relativas à sessão do usuário, tais como os dados do usuário logado, o idioma utilizado, etc. Veja que, embora tais informações tenham sido obtidas da requisição e da sessão HTTP, elas podem ser acessadas a partir de qualquer camada da aplicação sem que para isso seja necessário interagir com a API relativa ao protocolo HTTP, tais como as classes HttpServletRequest ou HttpSession.
Finalmente, se no futuro houver a necessidade da aplicação empregar uma camada de aplicação que não seja Web, mas sim baseada em desktop ou mesmo em sistemas móveis (ex.: celulares, palms, etc.), a lógica de negócio que fizer uso da API oferecido pelo Context Object continuará funcionando normalmente. A única modificação é a forma como o objeto ContextObject será populado.
Application Controller
Problema: Deseja-se centralizar e modularizar o gerenciamento de ações e visões.
Solução: Utilizar um Application Controller para centralizar a recuperação e invocação de componentes responsáveis pelo processamento de requisições, tais como comandos (commands) e visões (views)
A seguir temos um exemplo de Apllication Controller:
View Helper
Problema: Separar uma view da sua lógica de processamento.
Solução: Usar views para encapsular código de formatação e helpers para encapsular a lógica de processamento relacionada à view. Uma view delega suas responsabilidades de processamento para classes auxiliares (helpers), que podem ser POJOs (Plain Old Java Objects, tags customizadas ou arquivos de tag. Assim, os helpers atuam como adaptadores entre a view e o modelo, e executam processamento relacionado com a lógica de formatação, por exemplo, geração de código HTML.
Exemplos de aplicabilidade:
O trecho de código abaixo, adaptado do livro Core J2EE Patterns, mostra um exemplo de helpers implementados como tags JSP definidas pelo desenvolvedor, no caso <corepatterns:employeelist/> e <corepatterns:employee/>. Trata-se de uma página JSP que exibe uma tabela de funcionários. No exemplo, a classe Java associada à tag employeelist encarrega-se de gerar uma linha na tabela para cada elemento da lista employeelist_key, definida em algum escopo visível pela página JSP (request, sessão, etc.), ao passo que a classe associada a employee é responsável por renderizar um campo específico de cada elemento (FirstName, LastName, etc.)
Composite View
Problema: Deseja-se construir uma view a partir de componentes modulares e atômicos que são combinados para criar o todo, ao mesmo tempo gerenciando o conteúdo de cada componente e o layout da view de forma independente.
Solução: Utilizar Composite Views que são compostas dinamicamente de múltiplas subviews atômicas. O layout da página pode ser alterado independentemente do conteúdo de cada subview.
Exemplos de aplicabilidade:
Um exemplo prático da implementação deste padrão de projeto é o esquema de Tiles do framework Struts. Abaixo, são mostrados trechos de código auto-explicativos adaptados de um exemplo introdutório do framework que pode ser encontrado na Internet [3]. O exemplo mostra a definição do layout de um portal de forma independente do conteúdo de cada um dos componentes de sua página. Na imagem a seguir temos a definição do Layout:
E na imagem seguinte temos a associação dos componentes do layout às subviews correspondentes:
Service to Worker
Problema: Deseja-se tratar uma requisição e invocar lógica de negócio antes que o controle seja repassado para a view.
Solução: Usar o padrão Service to Worker para centralizar o controle e o tratamento de requisições, recuperando um modelo de apresentação antes de repassar o controle para a view. A view, por sua vez, gera uma resposta dinâmica com base no modelo de apresentação.
Exemplos de aplicabilidade:
O exemplo mais comum de aplicabilidade do padrão Service to Worker são os frameworks MVC mais comumente usados no mercado, a exemplo do Struts. Como pôde ser visto, este padrão é composto pela combinação de vários outros padrões.
Dispatcher View
Problema: Deseja-se que uma view trate uma requisição e gere a resposta, em cenários onde um processamento limitado de negócio deve ser executado.
Solução: Utilizar o padrão Dispatcher View com views atuando como o ponto inicial de acesso para as requisições. Caso seja necessário executar um processamento de negócio, este deve ser feito de forma limitada e gerenciada pelas views.
Exemplos de aplicabilidade:
O exemplo prático possivelmente mais conhecido da implementação deste padrão é o framework Java Server Faces (JSF) [4]. Em geral, as páginas no JSF são invocadas diretamente, ao passo que seu conteúdo dinâmico é recuperado a partir de backing beans que nada mais são do que objetos – POJOs – que geram os dados a serem exibidos e executam posteriormente a lógica associada ao processamento da submissão de formulários, por exemplo.
A diferença básica entre o padrão Dispatcher View e o Service Worker é que, enquanto o primeiro posterga o processamento de negócio até depois que o controle é passado para a view, o segundo executa tal processamento antes que o controle chegue à view.
Camada de Negócio
Business Delegate
Problema: Isolar a aplicação cliente da complexidade da comunicação remota com componentes de negócio.
Solução: Usar um Business Delegate para encapsular o acesso a um serviço de negócio. O Business Delegate esconde os detalhes de implementação do serviço de negócio, tais como mecanismos de consulta e acesso.
Dessa forma, pode-se acessar a partir do cliente um serviço de negócio remoto de forma transparente, da mesma forma que se acessaria um objeto local.
Service Locator
Problema: Deseja-se localizar de forma transparente componentes e serviços de negócio de maneira uniforme.
Solução: Utilizar um Service Locator para implementar e encapsular a lógica de localização do componente ou serviço de negócio.
Session Façade
Problema: Deseja-se expor componentes e serviços de negócio para clientes remotos.
Solução: Usar uma Session Façade para encapsular componentes da camada de negócio e expor um serviço de alta granularidade para clientes remotos. Dessa forma, os clientes acessam uma Session Façade ao invés de acessarem os componentes de negócio diretamente, diminuindo com isso a complexidade e a replicação de código.
Application Service
Problema: Deseja-se centralizar a lógica que envolve vários componentes e serviços da camada de negócio.
Solução: Usar um Application Service para centralizar e agregar comportamento, a fim de fornecer uma camada de serviço uniforme.
Entidades ou entities, que nada mais são do que classes Java que fazem uso de anotações para mapeamento objeto-relacional, ao estilo de frameworks já conhecidos, como o Hibernate em suas versões mais recentes.
Business Object
Problema: Tem-se um modelo de domínio conceitual com lógica de negócio e relacionamentos.
Solução: Utilizar Business Objects – objetos de negócio – para separar dados e lógica de negócio usando um modelo de objetos.
A imagem a seguir mostra um exemplo:
Composite Entity (chaves estrangeiras)
Problema: Deseja-se usar entity beans para implementar o modelo de domínio conceitual.
Solução: Usar um Composite Entity para implementar Business Objects (ver padrão Business Object) utilizando entity beans locais e POJOs. Um Composite Entity agrega um conjunto de Business Objects relacionados em implementações de entity beans de alta granularidade.
Transfer Object
Problema: Deseja-se transmitir múltiplos elementos de dados entre camadas.
Solução: Usar um Transfer Object para carregas múltiplos elementos de dados entre camadas.
Exemplos de aplicabilidade:
A principal motivação por trás deste pattern está no fato de que, quando se acessa um business object remoto, como por exemplo um entity bean, para recuperar e gravar os dados individuais deste objeto, cada chamada respectivamente a um método get ou set gera uma chamada remota (RPC), o que por sua vez incorrerá em maior tráfego de rede e consequente queda de desempenho da aplicação cliente. Daí, devem-se substituir tais chamadas por uma única chamada a um método get/setData().
Abaixo, temos um exemplo de código que emprega o padrão Transfer Object.
Transfer Object Assembler
Problema: Deseja-se obter um modelo de aplicação que agrega transfer objects provenientes de vários componentes de negócio.
Solução: Usar um Transfer Object Assembler para construir um modelo de aplicação como um transfer object composto. O Transfer Object Assembler agrega múltiplos transfer objects provenientes de vários componentes e serviços de negócio e retorna o transfer object resultante para o cliente. A principal vantagem aqui é centralizar a criação desse objeto, de modo a evitar que tal código seja disseminado pelas aplicações cliente.
Exemplos de aplicabilidade:
Os trechos de código abaixo mostram um exemplo bastante simples de um Transfer Object Assembler que constrói e retorna os dados de um cliente juntamente com os dados da sua conta, em um sistema bancário.
Value List Handler
Problema: Tem-se um cliente remoto que deseja iterar por uma lista grande de resultados.
Solução: Usar um Value List Handler para buscar, fazer cache dos resultados e permitir ao cliente percorrer e selecionar itens do resultado.
Exemplos de aplicabilidade:
Uma motivação bastante comum para o emprego deste pattern é que, em cenários onde são utilizados entity beans, os método ejbFinder() retornam uma lista de objetos remotos. Dependendo da quantidade de objetos retornados, o conjunto inteiro pode gerar um alto tráfego de rede. Além disso, para recuperar os dados de cada item do conjunto, são necessárias chamadas a métodos remotos, aumentando ainda mais o overhead.
Camada de Integração
Esta camada, como o próprio nome dá a entender, concentra componentes responsáveis por encapsular acesso a dados e comunicação com sistemas externos, através por exemplo de web services, troca de mensagens, etc.
Data Access Object (DAO)
Problema: Deseja-se encapsular acesso e manipulação de dados em uma camada separada.
Solução: Utilizar um Data Access Object para abstrair e encapsular todo o acesso ao armazenamento persistente. O Data Access Object gerencia a conexão com a fonte de dados para obter e armazenar dados.
Exemplo de um DAO:
public class ClienteDAO{
public void excluir(Long id){}
public void add(Cliente cliente){}
public Long count(){}
}
Service Activator
Problema: Deseja-se invocar serviços de forma assíncrona.
Solução: Usar um Service Activator para receber requisições assíncronas e invocar um ou mais métodos de negócio.
Domain Store
Problema: Deseja-se separar o código de persistência do modelo de objetos.
Solução: Usar um Domain Store para transparentemente persistir um modelo de objetos. Diferentemente dos mecanismos de persistência gerenciados pelo container ou pelo bean oferecidos pela Plataforma JEE, o mecanismo de persistência empregado por este pattern é separado do modelo de objetos.
Esse padrão basicamente não é criado desde zero nos dias de hoje.
Web Service Broker
Problema: Deseja-se fornecer acesso a um ou mais serviços usando XML e protocolos Web.
Solução: Usar um Web Service Broker para expor e intermediar um ou mais serviços utilizando XML e protocolos Web.
Fontes:
http://waltercunha.com/blog/index.php/2010/04/11/padroes-jee-parte-1/
http://waltercunha.com/blog/index.php/2010/04/16/padroes-jee-parte-2/
http://waltercunha.com/blog/index.php/2010/04/21/padroes-jee-parte-3/
http://waltercunha.com/blog/index.php/2010/04/25/padroes-jee-parte-4/
http://waltercunha.com/blog/index.php/2010/04/29/padroes-jee-parte-5/