PA R T E
I
Linguagem de
padrões EJB
Padrões de arquitetura da camada EJB 23
CAPÍTULO
Padrões de arquitetura
da camada EJB
Ao projetar sistemas EJB (Enterprise JavaBean) pela primeira vez, a escolha de uma arquitetura cor-
reta, ou o particionamento de lógica, que satisfaça a preocupações do projeto, como desempenho,
manutenção e portabilidade, é uma das tarefas mais difíceis encontradas pelos desenvolvedores.
Este capítulo cobre alguns padrões de arquitetura básicos atualmente em uso na indústria,
especificamente:
Session Façade. O mais usado de todos os padrões de projeto EJB, ele mostra como partici-
onar adequadamente a lógica de negócio em seu sistema para ajudar a minimizar as depen-
dências entre cliente e servidor, ao mesmo tempo em que força os casos de uso a serem
executados em uma chamada de rede e em uma transação.
Message Façade. Este padrão discute quando e como particionar a lógica de casos de uso que
sejam assíncronos por natureza.
EJB Command. Sendo a antítese do padrão Session Façade, este padrão advoga a colocação
da lógica de negócio em Command Beans Java pequenos e simples. Os maiores benefícios
deste padrão são o completo desacoplamento do cliente em relação ao próprio EJB e a
execução de casos de uso em uma chamada de rede e em uma transação.
Data Transfer Object Factory. Desmistifica a velha prática de colocar a lógica de criação/
uso de DTOs no próprio Entity Bean e prescreve a centralização da lógica de criação e uso do
objeto de transferência de dados para uma única camada (implementada como Session
Beans ou fábricas Java normais).
Generic Attribute Access. Este padrão discute quando e como fornecer uma interface de
domínio genérico para os atributos de um Entity Bean para fins de manutenção e desempe-
nho.
Business Interface. Este padrão mostra como implementar um esquema de implementação
de interface que possa fornecer checagem em tempo de compilação das assinaturas dos
métodos nas interfaces Remota/Local e na classe EJB Bean.
24 CAPÍTULO 1
SESSION FAÇADE
Um cliente EJB precisa executar a lógica de negócio para completar um caso de uso.
Uma solução é colocar lógica adicional dentro de nossos Entity Beans para desempenhar muitas
operações em uma única chamada do cliente. Esta solução causa problemas de manutenção, porque
nossa camada de Entity Beans será provavelmente usada de muitas formas diferentes com o decorrer
do tempo. Se adicionarmos lógica de aplicação a nossos Entity Beans cada vez que precisarmos de
uma melhoria de desempenho, eles irão rapidamente inchar e se tornar difíceis de entender, realizar
manutenção e reusar. Estamos juntando nossa lógica da aplicação (verbos) com nossa lógica de persis-
tência (substantivos), o que é um desenho de aplicação de qualidade inferior.
Outra abordagem é nosso cliente demarcar uma transação grande e agregada por meio da API
Java Transaction (JTA). Isto faria cada chamada de método de um Entity Bean operar sob a mesma
transação, executando tudo ou nada. Se o depósito falhar, então a retirada será desfeita e o dinheiro
do usuário estará seguro. Entretanto, esta solução aprimorada ainda tem muitas desvantagens:
䡲 Alto overhead de rede. Ainda temos seis chamadas de rede com que trabalhar, as quais
diminuem o desempenho (a não ser que usemos interfaces locais).
䡲 Baixo grau de concorrência. Se o cliente estiver muito distante do servidor (como no caso
de uma applet ou aplicação interagindo com um sistema EJB remoto, talvez até mesmo pela
Internet ou por meio de um firewall), a transação irá durar bastante tempo. Isto acarreta exces-
so de bloqueios, aumentando a chance de colisões ou deadlock, e reduz o grau de concorrência
de outros clientes acessando as mesmas instâncias de Entity Bean.
䡲 Alto grau de acoplamento. Nosso cliente escreve diretamente para uma API de Entity
Bean, o que o acopla fortemente ao Entity Bean. Caso a camada do Entity Bean precise de
alteração no futuro, deveremos alterar também o cliente.
䡲 Baixo grau de reuso. A lógica de negócio que executou o caso de uso “transferência de
fundos” estava embutida diretamente no cliente. Por isso, ela fica presa àquele cliente. Outros
tipos de clientes (aplicações Java, applets, servlets e assim por diante) não podem reusar esta
lógica de negócio. Esta mistura de lógica de apresentação com lógica de negócio é um projeto
de aplicação de qualidade inferior para qualquer distribuição séria.
䡲 Baixo grau de manutenção. O uso da API Java Transaction deixa a lógica do middleware
para a realização de transações entrelaçada com a lógica da aplicação. É muito mais legível
separar as duas por meio de transações declarativas, de forma que possamos refinar e ajustar
nosso middleware sem afetar nossas regras de negócio.
䡲 Baixa separação dos papéis de desenvolvimento. Uma prática comum em projetos
grandes é separar os programadores das tarefas de desenvolvimento da lógica de apresentação
(como desenvolvedores de servlets/jsp) dos programadores da lógica de negócio/middleware
(desenvolvedores EJB). Se a lógica de negócio for codificada na camada cliente/apresentação,
não será possível uma separação clara de papéis. A lógica de negócio e a de apresentação irão
atrapalhar uma à outra caso sejam programadas na camada de apresentação.
O ponto importante de nossa discussão é que precisamos de uma abstração do lado servidor
que sirva de intermediária e armazene as chamadas para Entity Beans. Session Beans são projetados
para isso.
Portanto:
Envolva a camada de Entity Beans em uma camada de Session Be-
ans chamada Session Façade. Os clientes devem ter acesso apenas aos
Session Beans, não aos Entity Beans.
O padrão Session Façade aplica os benefícios do padrão Façade tradicional à EJB escondendo
completamente da camada cliente o modelo de objetos no servidor, de modo que uma camada de
Session Beans seja o único ponto de acesso ao cliente. A Figura 1.2 ilustra como a arquitetura pode ser
melhorada adotando esta abordagem. O padrão Session Façade também inclui os benefícios de garan-
tir a execução de um caso de uso em uma chamada de rede e fornecer uma camada limpa para
encapsular as lógicas de negócio e de workflow usadas para satisfazer aos casos de uso. O Session
26 CAPÍTULO 1
Façade é normalmente implementado como uma camada de Session Beans sem estado (embora o
padrão também possa ser implementado com Session Beans com estado).
Para ilustrar o modo como este paradigma funciona e seus benefícios, usaremos o exemplo
anterior. Nossa lógica de negócio para o caso de uso de transferência de fundos será agora colocada
em um Session Bean que contém um método chamado transferFunds(userpk, accountpk, accountpk,
amount). O Session Bean BankTeller executa, deste modo, grandes operações sobre Users e Bank
Accounts, conforme mostrado na Figura 1.3.
Já que o Session Bean BankTeller está colocado junto com os Entity Beans User e Account,
deve ser codificado explicitamente para se comunicar com os Entity Beans por meio de suas interfa-
ces locais, reduzindo assim o overhead de rede necessário para executar este caso de uso para
apenas uma chamada (a chamada do cliente para o BankTeller). Além disso, todas as atualizações
na camada dos Entity Beans devem ser executadas dentro da transação iniciada pelo BankTeller
quase sempre definida no seu descritor de localização com uma configuração de TX_REQUIRED.
Isto efetivamente envolve todo o caso de uso dentro de uma transação, garantindo que todas as
atualizações sobre os Entity Beans sejam executadas dentro da transação iniciada na execução do
método transferFunds do BankTeller.
O padrão Session Façade é o padrão EJB mais importante em uso (e é por isso o primeiro deste
livro). Ele não apenas fornece benefícios em termos de desempenho, como também sugere uma arqui-
tetura padrão para sistemas EJB, particionando suas aplicações J2EE de forma que a fronteira entre o
cliente e o servidor seja separada por uma camada de Session Beans, cujos métodos mapeiam (e
contêm a lógica de negócio de) todos os casos de uso na aplicação.
Levando o exemplo do BankTeller mais adiante, obviamente há mais casos de uso envolvendo
a aplicação bancária do que simplesmente a transferência de fundos. Usando o padrão Session
Façade, seriam criados Session Beans para agrupar os casos de uso com funções semelhantes em um
único Bean. Deste modo, poderíamos adicionar outras operações bancárias subordinadas ao Bank-
Teller (como withdrawFunds, depositFunds, getBalance()). Em outro local na aplicação bancária,
casos de uso com propósitos diferentes também seriam agrupados em um Session Bean. Por exem-
plo, cada banco tem um Departamento de Empréstimos. Os casos de uso necessários para modelar
as operações deste departamento não são relacionados aos casos de uso de um BankTeller. Desta
maneira, eles seriam agrupados em um Session Bean LoanServices. De maneira similar, uma aplica-
ção bancária também precisaria de um Session Bean para encapsular casos de uso relacionados a
investimentos. Usando o padrão Session Façade, a aparência da arquitetura desta aplicação bancá-
ria se pareceria com a Figura 1.4.
O padrão Session Façade funciona tão bem que muitas vezes é fácil abusar dele. É comum
encontrar projetos nos quais o Session Façade está sendo usado de forma errada:
䡲 Criação de um único Session Bean. Muitas vezes, os desenvolvedores colocam todos os
casos de uso de um sistema em um único Session Bean, o que resulta em um Session Bean
inchado e em menor produtividade no desenvolvimento, porque todos os desenvolvedores pre-
cisam acessar esta única classe. Os Session Beans devem ser divididos para abrigar grupos de
casos de uso relacionados.
䡲 Colocação da lógica do domínio em Session Beans. Um modelo de domínio orientado
a objetos bem projetado deve conter toda a lógica de negócio/casos de uso na sua aplicação
(Fowler, 2001). A maioria dos métodos do Session Façade deve simplesmente delegar para o
Entity Bean apropriado, a não ser que o caso de uso envolva lógica de workflow que precise
operar sobre diferentes Beans que possam não estar diretamente relacionados.
䡲 Duplicação da lógica de negócio pela fachada. À medida que o projeto cresce, muitas
vezes, os métodos de um Session Bean contêm código duplicado, como a lógica da execução de
checkCreditHistory, que poderia ser parte do workflow para outros casos de uso. A solução é
acrescentar uma camada de serviços (implementada como Session Beans ou em classes Java)
que encapsule esta lógica de negócio reusável e independente de casos de uso. Esta camada de
serviços é escondida do cliente. À medida que os projetos aumentam de tamanho, é útil realizar
sessões de refatoração regularmente, nas quais a lógica duplicada é encontrada e extraída.
A seguir, estão os benefícios do padrão Session Façade:
䡲 Baixo overhead de rede. Na medida em que a camada de Session Beans acrescenta uma
camada extra por meio da qual as chamadas devem passar, o cliente pode agora transferir
fundos em apenas uma chamada de rede, em vez de seis. No servidor, o Session Bean se comu-
nica com Entity Beans por interfaces locais, não incorrendo, portanto, em qualquer overhead na
rede. Mesmo com os Entity Beans sendo usados apenas por interfaces remotas, a maioria dos
servidores de aplicação otimizaria a comunicação entre os EJBs no mesmo servidor.
䡲 Separação clara e rígida entre as lógicas de negócio e da camada de apresenta-
ção. Usando um Session Façade, a lógica necessária para executar a lógica de negócio fica
completamente envolvida por trás de métodos nos Session Beans. Os clientes EJB só precisam
se preocupar com questões da camada de apresentação e nunca deveriam ter de executar mais
de um método em um EJB para obter uma unidade de trabalho executada. Isto separa rigida-
mente a lógica de negócio da lógica da camada de apresentação.
䡲 Integridade transacional. Nosso Session Bean encapsula toda a lógica para executar a
transferência bancária em uma transação. O Session Bean age portanto como uma fachada
transacional, a qual localiza as transações no lado do servidor e as mantém curtas. As transa-
ções também são demarcadas em nível de método de Session Bean, configuráveis por meio de
descritores de localização.
䡲 Baixo acoplamento. O Session Bean armazena pedidos entre o cliente e os Entity Beans. Se
a camada dos Entity Beans precisar mudar no futuro, poderemos evitar mudanças no cliente
devido à camada de indireção de Session Beans.
䡲 Boa reusabilidade. A lógica de nosso caixa de banco fica encapsulada em um Session Bean
modular e pode ser acessada por qualquer tipo de cliente (JSPs, servlets, aplicações ou applets).
O encapsulamento da lógica da aplicação em Session Beans significa que nossos Entity Beans
podem conter dados e lógica de acesso apenas a dados, tornando-os reusáveis pelos Session
Beans na mesma ou até em diferentes aplicações.
䡲 Boa capacidade de manutenção. Deve-se definir a transação de forma declarativa no
descritor de localização do Session Bean BankTeller, em vez de fazê-lo por meio de programa-
ção via JTA. Isto nos dá uma clara separação entre o middleware e a lógica da aplicação, o que
aumenta a capacidade de manutenção e diminui a probabilidade de erros.
䡲 Separação verbo-substantivo clara. A camada de Session Beans modela os casos de uso
específicos da aplicação, os verbos da nossa aplicação, enquanto a camada de Entity Beans
modela os objetos do negócio, ou os “substantivos”, da nossa aplicação. Esta arquitetura torna
muito fácil mapear casos de uso de um documento de requisitos para uma arquitetura real EJB.
Padrões de arquitetura da camada EJB 29
O padrão Session Façade é uma base no desenvolvimento EJB. Reforça um projeto altamente
eficiente e reusável, além de separar claramente as lógicas da apresentação (o cliente), de negócio (a
fachada de sessão) e de dados (Entity Beans e assim por diante). O Session Façade descreve uma
arquitetura útil para implementar qualquer tipo de caso de uso; entretanto, se o caso de uso for
assíncrono por natureza, o padrão Message Façade fornecerá uma abordagem mais escalável.
Padrões relacionados
Message Façade
Data Transfer Object
Session Façade (Alur et al., 2001)
Session Façade (MartinFowler.com)
MESSAGE FAÇADE
Um cliente Enterprise Java Bean quer chamar os métodos de diversos EJBs dentro do contexto
de um caso de uso e não requer uma resposta imediata do servidor.
Como um cliente EJB pode chamar os métodos de diversos Session
Beans ou Entity Beans dentro de uma transação sem precisar bloquear e
esperar respostas de cada Bean?
* * *
Especialmente em sistemas grandes, a escalabilidade dita que a lógica de negócio de um caso de
uso seja executada separadamente da lógica do cliente, sem exigir que este espere até que a execução
esteja completa. Este tipo de comportamento, chamado assíncrono, permite aos clientes interagirem
com a interface de usuário (UI — User Interface) com tempos de resposta máximos, porque eles não
precisam sentar e esperar enquanto o caso de uso que foi iniciado é executado. Esta abordagem permi-
te a um sistema grande crescer, porque os casos de uso podem ser enfileirados e operados em lote, de
modo transparente para o usuário, que instantaneamente passa para a próxima parte de uma interface
de usuário. As partes do sistema que realmente executam os casos de uso também podem ser amplia-
das e sofrer atualizações se começar a haver acúmulo de casos de uso na fila, tudo sem alterar a
qualidade ou disponibilidade do serviço para os clientes.
Considere um sistema de registro de passagens aéreas simples baseado na Web no qual um
servlet recebe uma solicitação para reservar um lugar para um usuário em um vôo específico. Neste
cenário, o servlet deve registrar um usuário em um avião, determinar se os assentos estão disponíveis
no vôo e, em caso afirmativo, reservar um para o usuário, conforme mostrado na Figura 1.5 (p. 30).
Neste exemplo, temos um cliente executando diversas chamadas síncronas para o servidor para
executar um caso de uso. Cada passo do processo requer uma chamada de rede separada e o bloqueio
por parte do cliente. Em um sistema pesado como uma aplicação de reserva de passagens aéreas, este
gargalo é obviamente inaceitável. Além disso, executar a lógica desta forma reduz o grau de capacida-
de de manutenção e o reuso do sistema e não fornece consistência de transação ou isolamento para o
caso de uso.
A solução mais comum é usar o padrão Session Façade. Com ele, uma aplicação cria uma
camada de Session Beans que contém a lógica de negócio para satisfazer aos casos de uso de negócio.
Cada Session Bean executa uma grande quantidade de operações sobre Entity Beans ou outros recur-
sos no lado servidor em nome dos clientes em uma chamada grande, conforme mostrado na Figura 1.3
no padrão Session Façade. Infelizmente, mesmo se o caso de uso inteiro estiver envolvido em um
método do Session Façade, a abordagem ainda apresenta diversas desvantagens:
䡲 Tempo de resposta inaceitável. Um usuário interagindo com um website não esperará por
muito mais do que alguns segundos. A execução deste caso de uso requer muito processamento
em segundo plano, que poderia abranger diversas bases de dados em diversos sistemas de
30 CAPÍTULO 1
reservas. Devido ao fato de a chamada EJB ser “síncrona”, o cliente teria que bloquear até que
o processo inteiro tivesse sido completado.
䡲 Falta de tolerância a falhas. Este caso de uso poderia envolver EJBs espalhados por até
três instâncias de servidores EJB e três bancos de dados separados (uma para usuários, uma
para empresas aéreas e uma para vôos). Se qualquer um desses bancos de dados estivesse fora
do ar, o processo inteiro falharia e o pedido de reserva do usuário seria perdido. Mesmo se a
camada do servlet estivesse se comunicando com apenas um servidor EJB, o processo falharia se
o servidor estivesse fora do ar.
Usar o Session Façade resolve os problemas de acoplamento, desempenho, manutenção, reuso
e consistências, mas não resolve completamente os de tempo de resposta e confiabilidade. O cliente
ainda tem de realizar bloqueio enquanto um caso de uso complexo e que consome muito tempo é
executado. O caso de uso também falhará se o servidor EJB ou qualquer um dos sistemas dos quais ele
dependa não estiver rodando no momento em que o caso de uso for executado.
O ponto importante da nossa discussão é que precisamos de uma abstração do lado servidor
tolerante a falhas que sirva como intermediária, executando os casos de uso em uma chamada e uma
transação (resguardando os clientes das complexidades do modelo de objetos do lado servidor) e que
não requeira que o cliente bloqueie e espere até o caso de uso ser completado. Beans orientados a
mensagens são projetados apenas para isso.
Portanto:
Use Beans orientados a mensagens para criar uma fachada toleran-
te a falhas e assíncrona. Os clientes devem ter acesso apenas a Beans
orientados a mensagens, não a Entity Beans.
Usar Beans orientados a mensagens (MDB — Message-driven Beans) como fachada melhora o
padrão Session Façade ao adicionar a capacidade de execução de casos de uso de uma maneira assín-
crona e tolerante a falhas. Quando usamos uma fachada de mensagem, a lógica de negócio em cada
um dos casos de uso de uma aplicação mapeia para seu próprio MDB.
Considere o exemplo anterior. Nossa lógica de negócio para reserva de lugares em um vôo será
agora colocada no método onMessage( ) em um Bean orientado a mensagens ReserveSeat. O objetivo
Padrões de arquitetura da camada EJB 31
O ponto a considerar aqui é que, ao usar o padrão Message Façade, os desenvolvedores devem
planejar novas maneiras de se comunicar com o cliente sobre os resultados de um caso de uso.
Uma desvantagem de usar o padrão Message Façade é que agora a lógica de negócio está
distribuída através tanto dos Beans orientados a mensagem (pela fachada de mensagem) quanto dos
Sessions Beans (pela fachada de sessão). Isto pode não ser uma preocupação importante para a maio-
ria, mas seria bom manter a lógica de negócio em um único local da aplicação. Uma maneira mais
inteligente de resolver este problema é implementar todos os casos de uso na própria fachada de
sessão e usar a fachada de mensagens para delegar à fachada de sessão. Desta forma, todos os benefí-
cios de usar uma construção assíncrona e tolerante a falhas como um Bean orientado a mensagens são
mantidos, ao mesmo tempo em que a lógica permanece na camada dos Session Beans.
As vantagens do padrão Message Façade incluem todas mostradas no padrão Session Façade,
como:
䡲 Tempo de resposta instantâneo/comunicação assíncrona. Quando um cliente envia
uma mensagem JMS, está livre para continuar processando sem esperar até que o servidor
complete o caso de uso e responda. Um caso de uso comprido e complexo pode, assim, ser
iniciado enquanto o controle de fluxo retorna instantaneamente ao usuário.
䡲 Eliminação de pontos individuais de falha. O uso de mensagens garantirá que sua
aplicação continue a funcionar mesmo quando o servidor EJB ou outro subsistema do qual ele
dependa sair do ar. Por exemplo, se o banco de dados não estiver funcionando, a transação do
MDB não será completada e a mensagem de reserva de lugar permanecerá na fila, sendo feita
nova tentativa mais tarde. Se o contêiner EJB estiver fora do ar, a mensagem será armazenada
novamente. Tais capacidades de superar falhas não seriam possíveis se usássemos um modelo
síncrono. É claro que, se o seu servidor JMS não estiver clusterizado e sair do ar, isto ainda
representará um ponto falho individual, mas pelo menos o número de possíveis causadores de
interrupção foi reduzido.
Entretanto, como subproduto do uso de Beans orientados a mensagens, o padrão Message
Façade também apresenta alguns problemas:
䡲 Beans orientados a mensagens têm parâmetros de entrada fracamente tipa-
dos. O papel de um Bean orientado a mensagens é consumir mensagens JMS, as quais parecem
todas idênticas em tempo de compilação. Isto contrasta com os Session e Entity Beans, os quais
alavancam a forte tipagem incorporada por Java aos métodos e parâmetros das interfaces re-
motas e locais para apanhar erros comuns em tempo de compilação. O desenvolvedor deve
tomar cuidado extra para carregar uma mensagem JMS com o conteúdo apropriado requeridos
pelo seu MDB destinatário. Uma solução para este problema é encapsular todos os dados da
mensagem JMS em um objeto de transferência de dados customizado e serializá-lo na mensa-
gem JMS.
䡲 Beans orientados a mensagens não têm valores de retorno. Já que as chamadas
MDB são assíncronas, o Message Façade não pode ser usado em casos de uso que demandem
um valor de retorno após a execução. Portanto, usar o Message Façade é, à primeira vista,
similar a usar o Session Façade, no qual todos os métodos dos Session Beans simplesmente
retornam void. Entretanto, é possível obter uma resposta de um Bean orientado a mensagens de
volta para o criador da mensagem usando JMS como mecanismo de transporte. Veja no livro
Mastering Enterprise Java Beans, Second Edition uma discussão sobre esse mecanismo.
䡲 Beans orientados a mensagens não propagam exceções de volta aos clientes.
Diferentemente dos Session e Entity Beans, os Beans orientados a mensagens não podem gerar
exceções na aplicação ou RemoteException a partir de qualquer um de seus métodos. Os MDBs
devem, portanto, lidar com todas as exceções apresentadas em algum formato específico da
aplicação (isto é, enviando uma mensagem eletrônica para o usuário se algo tiver dado errado,
colocando os erros em um log de administração, e assim por diante).
Padrões de arquitetura da camada EJB 33
Padrões relacionados
Session Façade
EJB COMMAND
Um cliente EJB precisa executar lógica de negócio para completar um caso de uso.
Como um desenvolvedor pode implementar a lógica de negócio de
um caso de uso de uma maneira leve, desacoplando o cliente do EJB e
executando o caso de uso em uma transação e em uma chamada de rede?
* * *
Uma decisão fundamental de arquitetura ao projetar um sistema EJB é onde colocar a lógica de
negócio. A lógica de negócio de um caso de uso é a lógica que delega ao método apropriado no seu
modelo de domínio ou executa lógica que opera sobre diversos outros Entity Beans e/ou Session Beans
(lógica de workflow).
Colocar a lógica de negócio no cliente (servlets, applets e assim por diante) tem sérias conse-
qüências negativas, afetando o desempenho e a manutenção, conforme explicado no padrão Session
Façade. Estes problemas podem ser corrigidos pelo uso do padrão Session Façade, que requer que a
lógica de negócio seja colocada em métodos em Session Beans, sendo que cada método em um Session
Bean mapeia uma unidade particular de trabalho, ou caso de uso. Fazendo isso, há um escudo entre o
cliente e o modelo de objetos no servidor, e os casos de uso são executados em uma transação e em
uma ida e volta pela rede.
O padrão Session Façade é uma base no desenvolvimento EJB, mas também apresenta as pró-
prias fraquezas. Chamar a fachada de sessão diretamente a partir do cliente pode causar dependências
entre as equipes do cliente e do servidor em um projeto grande e complicar o código do cliente devido
ao forte acoplamento ao EJB, conforme discutido no padrão Business Delegate. Estes problemas po-
dem ser minorados pelo uso de encarregados de negócios, o que acrescenta uma camada de objetos
que encapsulam todo o acesso na camada EJB. Business Delegates podem ajudar a manter o código
cliente simples, minimizando as dependências entre cliente e servidor.
Portanto, o padrão Session Façade, combinado com o padrão Business Delegate, fornece uma
prática melhor para escrever lógica de negócio em um formato que desacopla o cliente dos detalhes de
implementação do servidor e permite a execução de casos de uso em uma chamada de rede e em uma
transação. Como sempre, há compensações:
34 CAPÍTULO 1
Usando o exemplo do transferFunds, o cliente definiria as IDs da conta cujos fundos são retirados,
a conta na qual depositar o dinheiro e a quantidade a transferir. Depois de chamar execute do Command
transferFunds, o cliente pode obter o saldo final das contas, conforme mostrado na Figura 1.8.
É provável que uma das mais abrangentes implementações do padrão Command seja o fra-
mework Command da IBM, o qual vai com o Websphere, parte dos padrões IBM para comércio eletrô-
nico. Há muitas maneiras diferentes de implementar o padrão EJB Command, mas todas elas têm os
mesmos três elementos:
䡲 Command Beans. Uma classe simples Java Bean com gets, sets e um método execute que
contém a lógica de negócio necessária para executar um caso de uso. Os Command Beans são a
única parte do padrão Command que precisa ser escrita pelos desenvolvedores da aplicação. Os
outros componentes explicados abaixo podem ser reusados pelo projeto.
䡲 Lógica de roteamento no lado do cliente. Normalmente, um framework de classes é
responsável por tomar um Command e enviá-lo para o servidor EJB remoto. Esta lógica de
roteamento é geralmente invisível para o cliente e é disparada pela chamada do método execute
de um Command. A lógica/framework de roteamento é um conjunto genérico de classes que
pode ser reusado pelos projetos.
䡲 Servidor de Comandos Remoto. O Servidor de Comandos Remoto é um serviço que sim-
plesmente aceita comandos e os executa. Aplicada ao EJB, a classe CommandServer é um Ses-
sion Bean sem estado que aceita um Command como parâmetro e o executa localmente. O
CommandServer também é genérico, podendo ser completamente reusado pelos projetos.
As interações entre o cliente e esses três componentes são ilustradas na Figura 1.9. Neste exem-
plo, o cliente chama um método executeCommand no componente de lógica de roteamento. No fra-
mework Command da IBM, o cliente só precisa chamar execute no próprio Command, já que a chamada
do método na verdade será recebida pela superclasse do Command, a qual é parte do framework de
lógica de roteamento.
Por trás da cena, o CommandExecutor delega a chamada para um EJBCommandTarget (não
mostrado na Figura 1.9, já que faz parte da lógica de roteamento), o qual é codificado com conheci-
mento do EJB e sabe como enviar o Command para o Session Bean sem estado CommandServer. Ao
receber o Command, o CommandServer simplesmente chama o método execute no Command, o qual
então cuida da lógica de negócio.
Os benefícios do padrão Command são:
䡲 Facilita o Desenvolvimento Rápido de Aplicações (RAD) devido ao processo
leve de desenvolvimento e distribuição. Escrever um caso de uso como um Command
Bean torna consideravelmente mais fácil e rápido distribuir e testar do que escrevê-lo como
método de um Session Bean. As alterações freqüentes podem ser feitas em uma classe Java
simples, de forma oposta a um EJB completo.
Sob vários aspectos, o padrão Command parece ser a solução final, combinando os benefícios
dos padrões Session Façade e Business Delegate com uma infra-estrutura mais leve. Entretanto, os
benefícios são, como sempre, contrabalanceados por desvantagens importantes:
䡲 Controle de transação com granularidade muito grossa. Já que os Commands são
apenas Java Beans simples, não há maneira automática de marcar um Command para rodar
sob um determinado nível de isolamento ou de ajuste de uma transação, como você pode fazer
com métodos de Session Beans. Os comandos podem ser executados apenas sob o ajuste da
transação do CommandServer que os executa. Para evitar isso, é preciso distribuir diversos
Session Beans do servidor de Commands com diferentes nomes jndi e ajustes de transação
(configurados nos descritores de localização). O componente da lógica de roteamento precisa
ser configurado para enviar certos Commands para certos servidores de Commands. Isto signi-
fica que se pode desejar enviar todos os Commands só de leitura para Session Beans que rodam
sem transações, ao passo em que os Commands de atualização poderiam ser executados em um
servidor de Commands rodando com tx_requires e nível de isolamento serializável.
䡲 Comandos não têm estado. O objeto Command não pode armazenar qualquer estado no
Session Bean que o executa. Portanto, com o padrão Command, não é possível o armazenamen-
to de estados na camada EJB.
䡲 Tratamento de erros desajeitado. Tendo em vista que o framework de Commands é
genérico pode-se gerar apenas uma CommandException a partir de um Command. Isto signifi-
ca que as exceções da aplicação, como NoMoneyInAccountException, precisam ser apanhadas e
envolvidas em uma CommandException. Os clientes precisam então buscar exceções particula-
res dentro do objeto Command. Como as exceções não são declaradas explicitamente, os clien-
tes perdem o benefício da checagem do tratamento de exceções em tempo de compilação.
䡲 Comandos podem se tornar impossíveis de gerenciar em projetos grandes.
Um projeto grande pode explodir com milhares de Commands, muitos dos quais têm partes
de lógica de negócio duplicadas, especialmente quando várias equipes de projeto estão usan-
do o mesmo modelo de domínio como fundo. Isto dificulta a manutenção da camada de
lógica de negócio, em oposição ao padrão Session Façade, em que os casos de uso são imple-
mentados como métodos de Session Beans, satisfatoriamente agrupados em um pequeno
número de Session Beans. Esta proliferação de classes pode ser um problema sério em proje-
tos grandes.
䡲 ejb-jar do CommandServer acoplado a Command Beans e outros EJBs. Como os
Command Beans são executados dentro do ambiente dos Session Beans CommandServer, as
classes de Command Beans precisam ser distribuídas com o Session Bean CommandServer (no
mesmo ejb-jar ou EAR) para que os Command Beans sejam desserializados e executados. Isto
significa que toda vez que um Command Bean é alterado, o EAR ou ejb-jar do Session Bean
38 CAPÍTULO 1
CommandServer precisará ser redistribuído (de forma que os carregadores de classe dos Com-
mandServers possam ler as novas versões de todos os Commands incluídos) para testar as
alterações, ou reiniciado completamente, se o seu servidor de aplicação não suportar distribui-
ção a quente. Além disso, os Command Beans precisam ter visibilidade de qualquer home,
remota ou não, e interfaces locais que possam usar na sua lógica de negócio. Isto requer que o
CommandServer seja colocado no mesmo EAR dos outros EJBs acessados por qualquer um dos
seus Command Beans ou que as interfaces dos EJBs acessados sejam colocadas no mesmo paco-
te com o ejb-jar do CommandServer.
Os padrões Command e Session Façade proporcionam dois benefícios importantes: agem como
uma fachada e são executados em um ciclo da rede. Uma vantagem importante do padrão Command
sobre o Session Façade é que ele desacopla o cliente do EJB, o que pode também ser obtido aplicando-
se o padrão Business Delegate em conjunto com o Session Façade. Então, como o desenvolvedor pode
escolher entre um e o outro? É útil pensar em Commands como Session Beans mais baratos. Eles são
mais leves, resultando em um processo de desenvolvimento inicial mais rápido, o que, em contrapar-
tida, pode diminuir a facilidade de manutenção com o passar do tempo.
Padrões relacionados
Command (Gamma et al., 1995)
Data Transfer HashMap
Um sistema J2EE que usa objetos de transferência de dados (DTOs — Data Transfer Objects)
descobre que sua camada DTO tende a mudar com freqüência.
Como deve ser implementada a lógica de criação e uso de objetos de
transferência de dados para minimizar o impacto de mudanças freqüen-
tes na camada DTO sobre o resto do sistema?
* * *
Os objetos de transferência de dados tendem a mudar freqüentemente. Os DTOs do domínio
mudam sempre que os objetos do domínio mudam (acrescentando um novo atributo em um Entity
Bean e assim por diante). Os DTOs customizados são apenas depósitos de dados específicos para um
determinado caso de uso, servindo para transportar dados através de uma rede. Eles podem mudar
com a mesma freqüência da visão da apresentação de sua aplicação. Uma aplicação de média a grande
poderia ter dezenas, até centenas de diferentes objetos de transferência de dados, cada um dos quais
requereria lógica customizada para ser criado. Torna-se, então, uma questão fundamental como e
onde esta lógica deve ser implementada para que se desacople e proteja o resto deste sistema das
mudanças nos objetos de transferência de dados.
Uma solução comum empregada em aplicações EJB 1.X é colocar métodos getXXXDTO/setXXXDTO
diretamente nos Entity Beans. Neste cenário, o Entity Bean seria responsável por povoar este objeto de
transferência de dados e por atualizar a si próprio baseado nos atributos do set DTO. O problema desta
abordagem é que ela acopla fortemente a camada do objeto de transferência de dados à camada de
Entity Beans. Isto significa que colocar o código de criação de objetos de transferência de dados espe-
cíficos para um caso de uso em um Entity Bean poderia causar sérias dependências entre seus Entity
Beans e clientes em aplicações médias a grandes. Cada vez que uma página Web mudasse e fosse
necessária uma visão diferente do modelo de dados, você teria que acrescentar um novo método a um
Entity Bean, recompilar seu Entity Bean e redistribuir suas interfaces remotas para qualquer cliente
que as estivesse usando.
Espera-se que Entity Beans sejam componentes de negócio reusáveis, que possam ser montados
separadamente para criar uma aplicação. Para criar componentes de negócio realmente reusáveis, é
Padrões de arquitetura da camada EJB 39
importante manter uma separação rígida entre as lógicas da sua aplicação e de negócio, permitindo que
as duas se desenvolvam separadamente. Deve-se encontrar outra solução para a criação e o uso de Entity
Beans, de modo a conseguir desacoplar a lógica relacionada ao DTO dos outros componentes do sistema.
Portanto:
Coloque a responsabilidade pela criação e pelo uso de objetos de
transferência de dados em uma fábrica de objetos de transferência de da-
dos.
A fábrica de objetos de transferência de dados separa a lógica relacionada aos objetos de trans-
ferência de dados (parte do domínio da aplicação) dos outros componentes do seu sistema, como
Entity Beans (parte do domínio de negócio). Quando novas visões ou diferentes subconjuntos de
dados no lado servidor se tornarem necessários, novos métodos de criação de DTOs poderão ser
adicionados a um DTOFactory, em vez de serem colocados em um Entity Bean. Estes novos métodos
irão interagir com a camada de Entity Bean (ou qualquer outra fonte de dados, como conectores,
JDBC e outras), chamando métodos de recuperação e percorrendo relacionamentos conforme neces-
sário para gerar objetos de transferência customizados ou de domínio. A vantagem desta abordagem
é que os próprios Entity Beans não precisam conhecer estas diferentes visões dos seus dados. Na
verdade, nenhum código em um Entity Bean precisa ser alterado.
Por exemplo, considere uma aplicação automotiva que permita aos usuários navegar por infor-
mações sobre carros e seus fabricantes. Deste modo, a aplicação terá um modelo de domínio que
consistirá (entre outros) em um Entity Bean Car e um Manufacturer. Essa aplicação terá uma interface
de usuário com várias páginas diferentes que permitam aos usuários navegar por diferentes proprie-
dades dos carros e de seus fabricantes, incluindo diferentes subconjuntos de atributos de um carro
(características do motor, da lataria, do chassi, etc.) e dados espalhados por diversos Entity Beans
(informação sobre o carro e seu fabricante, etc.). Estes diversos conjuntos de dados deveriam ser
transferidos para o cliente usando DTOs customizados. Entretanto, em vez de colocar os métodos Java
requeridos para criar estes diferentes DTOs em um Entity Bean Car ou Manufacturer, eles seriam
colocados em um DTOFactory como o da Figura 1.10.
O CarDTOFactory agora se torna um ponto individual onde reside a lógica de DTO específica
para este caso de uso, ajudando a desacoplar os clientes do modelo do domínio. Os Entity Beans no
modelo do domínio estão agora livres para ser objetos de domínio, expondo aos clientes apenas os
métodos de negócio, não a lógica de DTO de gets e sets, o que realmente não tem nada a ver com o
conceito do negócio incorporado por esse modelo de domínio em particular.
Há duas maneiras básicas de implementação do padrão DTO Factory, dependendo de o seu
cliente ser um Session Bean ou um cliente não EJB, como um servlet. Quando usado por trás de uma
fachada de Session Beans, a fábrica de DTO pode ser implementada como uma classe Java simples que
apenas armazena a lógica de criação e uso de diferentes objetos de transferência de dados em seus
métodos. Este tipo de fábrica se presta bem ao reuso, porque os objetos de transferência de dados que
ela gera podem ser reusados em diferentes Session Beans e/ou em diferentes projetos.
Quando usada a partir de um cliente não EJB, a fábrica de DTO deve ser implementada como
um Session Bean sem estado. Uma interação típica entre este cliente e o objeto de transferência de
dados está delineada na Figura 1.11. Neste caso, um cliente servlet quer obter um DTO customizado
chamado CarAndManufacturerDTO e, então, procura um CarDTOFactory para esse objeto. O Car-
DTOFactory então cria e povoa o DTO chamando métodos get sobre o Entity Bean Car e seu Entity
Bean Manufacturer relacionado através de suas interfaces locais.
Fábricas de objetos de transferência de dados podem ser usadas para criar facilmente qualquer
tipo de DTO. Mesmo hierarquias complexas de Aggregate DTOs (DTOs de domínio que contêm outros
DTOs de domínio) podem ser criadas para mapear diferentes fatias do modelo de objetos Entity Bean
do lado servidor. Pode-se criar hierarquias complexas de objetos de transferência de dados escreven-
do-se explicitamente lógica que saiba como navegar (e copiar) uma fatia de uma hierarquia de Entity
Beans que seja específica para um caso de uso. Estas hierarquias de DTOs podem ser todas criadas
inicialmente no servidor e passadas para o cliente em uma chamada de rede.
Um benefício importante que resulta desta prática é que os Entity Beans de nossa aplicação são
agora totalmente reusáveis. Por exemplo, imagine duas equipes de desenvolvimento separadas em
uma corporação trabalhando em aplicações separadas. Ambas podem reusar os mesmos Entity Beans
componentes de negócio (a propósito, um ótimo exemplo de reuso EJB) usando fábricas de objetos de
transferência de dados separadas. As equipes poderiam obter reuso completo, cada uma mantendo a
própria fábrica de DTO separada, que distribuiria DTOs específicos de casos de usos fatias do estado
do Entity Bean — independentemente da outra equipe. Cada uma mantendo a própria fábrica DTO,
elas poderiam também desenvolver e distribuir as próprias aplicações completamente independentes
uma da outra. Este conceito está ilustrado na Figura 1.12.
Perceba que o padrão Data Transfer Object Factory não significa a criação de uma fábrica de
DTO para cada classe de Entity Bean. Por exemplo, você não precisa necessariamente criar um Car-
DTOFactory para um Entity Bean Car. Isto resultaria na explosão das fábricas VO. Onde os requisitos
permitirem, pode ser mais simples e direto criar uma fábrica DTO para um conjunto inteiro de Entity
Beans e/ou outras fontes de dados do lado servidor.
As fábricas DTO fornecem uma maneira de ler dados do servidor, mas e a atualização dos
dados? Técnicas similares às usadas na leitura de dados do lado servidor podem ser usadas para
FIGURA 1.11 Uso de uma fábrica de Car DTO como um Session Bean.
Padrões de arquitetura da camada EJB 41
FIGURA 1.12 Obtenção de reuso de Entity Beans com fábricas de objetos de transferência de dados.
atualizar dados. Isto quer dizer que os clientes podem passar um DTO de domínio ou um DTO custo-
mizado para o servidor, no qual ele pode, por sua vez, desempenhar operações de criação, leitura,
atualização e remoção (operações CRUD — Create, Read, Update e Delete) em Entity Beans ou em
outros depósitos de dados no lado servidor.
Para os DTOs de domínio (os quais são normalmente feitos mutáveis), o cliente executará suas
atualizações localmente sobre um DTO e então atualizará o servidor passando um DTO de domínio
para um método updateXXXEntity em uma fábrica de DTO, a qual copiaria os atributos do DTO para o
Entity Bean apropriado, usando métodos set de granularidade fina na interface local do Entity Bean.
Os clientes podem, de forma semelhante, criar Entity Beans povoando localmente um DTO de domí-
nio e o passando para um método createXXXEntity na fábrica.
Usando o exemplo anterior, se o administrador da aplicação quisesse atualizar um carro ou
fabricante em particular, estas atualizações seriam feitas em interfaces de usuário separadas (uma
para o carro e uma para o fabricante). As atualizações seriam executadas e seria enviado um DTO de
domínio Car ou um Manufacturer de volta para o servidor para atualização em uma única transação,
conforme mostrado na Figura 1.13.
Para executar qualquer tipo de atualização maior que a atualização CRUD dos objetos do domí-
nio, o servidor deve ser atualizado, passando DTOs customizados para a fachada de sessão/mensa-
gem. Lembre-se que a fachada deve conter toda a lógica de negócio necessária para executar casos de
uso em uma aplicação, como realizar uma encomenda no Amazon ou transferir fundos em um banco.
Para estes tipos de operações, o cliente normalmente criará um DTO customizado que contenha todos
os dados necessários para executar a atualização e passar este DTO para a fachada, a qual por sua vez
irá criar, atualizar ou remover qualquer número de recursos no lado servidor.
As vantagens da abordagem da fábrica de objetos de transferência de dados são muitas:
䡲 Facilita a manutenção. Separando a lógica de sua aplicação (casos de uso) e seu modelo de
objetos de dados (Entity Beans), ambos podem evoluir isoladadamente. Os Entity Beans não
precisarão mais ser alterados e recompilados quando as necessidades do cliente mudarem.
䡲 Encoraja o reuso de Entity Beans. Os Entity Beans podem ser reusados por outros proje-
tos, já que diferentes fábricas DTO podem ser escritas para satisfazer às necessidades de dife-
rentes aplicações.
䡲 Permite a criação de diagramas complexos de DTOs. Escrevendo a lógica de cria-
ção de DTOs logo no início, os desenvolvedores podem criar diagramas/hierarquias
complexos(as) de DTOs que podem ser usados(as) para transferir os dados de hierarquias
complexas de Entity Beans contendo relacionamentos um para um, um para muitos, muitos
para muitos e cíclicos, além de combinações desses relacionamentos. Isto fornece aos clientes
maior controle sobre que partes dos dados dos Entity Beans eles precisam mostrar. Para clien-
42 CAPÍTULO 1
FIGURA 1.13 Atualização de dados usando uma fábrica de objetos de transferência de dados.
tes não Web, como aplicações Java e applets, a habilidade de obter dados não tabulares é
especialmente importante.
䡲 Melhora o desempenho. Quando a fábrica DTO é usada como uma fachada de sessão, os
atributos de diversos Entity Beans podem ser passados para o cliente em apenas uma chamada
de rede.
O padrão Data Transfer Object Factory pode construir sistemas flexíveis e passíveis de manuten-
ção, fornecendo um método simples e consistente para criar complexos objetos de transferência de
dados e passá-los ao cliente em uma grande chamada de rede, sem causar dependências entre os
objetos de transferência de dados e outros componentes em um sistema J2EE.
Padrões relacionados
Session Façade
Data Transfer Object
Value Object Assembler (Alur et al., 2001)
cia de dados em interfaces remotas (para Entity Beans EJB 1.X). Com o primeiro, os métodos na
fachada de sessão ou na fábrica de objetos de transferência de dados interagem com os Entity Beans,
chamando diversos métodos de gravação e leitura de granularidade fina para acessar e manipular
atributos, conforme requerido por cada caso de uso. Usando o segundo modo, os objetos de transfe-
rência de dados são usados para minimizar as chamadas de rede associadas à comunicação com as
interfaces remotas. O uso de DTOs como mecanismo de manipulação de Entity Beans é um padrão
comum de otimização da comunicação com Entity Beans EJB 1.X.
A perda acarretada pelo uso de DTOs para acessar EJB 1.X Entity Beans é a diminuição da
capacidade de manutenção da camada dos Entity Beans (veja o padrão DTO Factory). Com o adven-
to do EJB 2.0, as interfaces locais permitiram a extração da lógica de criação e uso do DTO para
uma fábrica de objetos de transferência de dados. Neste caso, a fábrica de DTOs interage com um
Entity Bean por meio de gets/sets de granularidade fina na interface local, atenuando alguns proble-
mas do uso de DTOs.
Infelizmente, os Entity Beans EJB 1.X não têm interfaces locais. Como conseqüência, a lógica de
criação/uso de DTOs não pode ser extraída dos Entity Beans para uma fábrica de DTOs (porque é ruim
para o desempenho uma fábrica de DTOs fazer diversas chamadas de granularidade fina para a inter-
face remota de um Entity Bean). É necessário algum outro mecanismo que permita acesso a uma
grande quantidade de dados do Entity Bean por meio da interface remota, sem se misturar com a
lógica de criação/uso do DTO.
Mesmo para interfaces locais, em alguns casos, expor diversos métodos get/set de granularida-
de fina não é uma boa idéia:
䡲 Não escala bem de pequenos para grandes Entity Beans. Imagine um Entity Bean de
ações/títulos em uma aplicação financeira. Esse Entity Bean poderia bem ter duzentos atribu-
tos. Escrever e expor métodos de leitura e gravação para todos esses atributos poderia resultar
em um pesadelo de codificação tediosa e em uma explosão no tamanho da interface.
䡲 Resulta em clientes altamente acoplados e com código explícito. Os clientes Entity
Bean (como a fachada de sessão) precisam ser altamente acoplados à interface do Entity Bean,
tornando-a sensível a quaisquer mudanças que possam ocorrer — como a adição ou remoção de
um atributo.
O ponto a considerar é a necessidade de algum outro mecanismo para acessar dados de Entity
Beans, que possa permitir a uma fábrica de DTOs usar a interface remota para pegar dinamicamente
subconjuntos diferentes do estado do Entity Bean em uma grande chamada de rede e também ajudar
a desacoplar clientes Entity Bean dos acessadores de atributos do Entity Bean, ao usar uma interface
local.
Portanto:
Abstraia a lógica de acesso a atributos de um Entity Bean em uma
interface de acesso de atributos genérica, usando HashMaps para passar
atributos tipo valor-chave para Entity Beans e destes.
A interface de acesso de atributos é implementada pelos Entity Beans na interface local ou
remota e tem esta aparência:
AttributeAccess fornece uma interface genérica que permite que conjuntos de dados arbitrários
sejam lidos ou gravados em um Entity Bean dinamicamente. A interface permite a Entity Beans EJB
1.X usar uma fábrica de DTOs para extrair a lógica de criação de DTOs e otimizar as chamadas remo-
tas, bem como permitir a Entity Beans EJB 2.X simplificar suas interfaces locais, eliminando a necessi-
dade de métodos get/set de granularidade fina. A única dependência entre o cliente e o código do
44 CAPÍTULO 1
Entity Bean são as convenções de nomes colocadas nas chaves usadas para identificar os atributos,
descritas mais adiante neste padrão.
A fachada de sessão ou a fábrica de DTOs pode acessar os atributos de um Entity Bean pela
interface de acesso aos atributos. A Figura 1.14 ilustra um caso típico: um cliente está interessado em
juntar um subconjunto dos dados de um Entity Bean “Car” relacionados a seu motor. Ele chama o
método getCarEngineData na fachada de sessão, o qual, por sua vez, pede a um Entity Bean os atribu-
tos exatos que fazem parte do motor do carro, primeiramente criando um conjunto que inclui os
valores-chave dos atributos de interesse (potência, volume e outros) e então passando este conjunto
ao método getAttributes(conjunto) no Entity Bean, o qual irá retornar um HashMap com este subcon-
junto exato.
Após receber o HashMap povoado do Entity Bean Car, o Session Bean pode:
1. Retornar o HashMap para um cliente remoto. Neste caso, o Session Bean usa o Hash-
Map como um contêiner serializável para transferir dados por meio da rede (conforme descrito
no padrão Data Transfer HashMap no Capítulo 2).
2. Converter o HashMap em um DTO e retorná-lo. Sendo uma fábrica de DTOs, o Session
Bean pode extrair os valores do HashMap e adicioná-los a um objeto de transferência de dados,
retornando o DTO para o cliente.
A opção depende do desenvolvedor. Como um mecanismo de transferência de dados, os Hash-
Maps apresentam muitas vantagens sobre os objetos de transferência de dados (como descrito no
padrão Data Transfer HashMap), mas, em contrapartida, aumenta significativamente a complexidade.
Se a interface de acesso a atributos for usada por trás de uma fábrica de DTOs, as dependências entre
nomes de chaves/valores podem ser mantidas no servidor, onde, de qualquer maneira, os Session
Beans precisam estar cientes dos Entity Beans.
Usando a interface de acesso a atributos, um Session Bean pode, portanto, decidir dinamica-
mente de quais subconjuntos de dados do Entity Bean ele precisa em tempo de execução, eliminando
a necessidade de programação manual em tempo de desenho dos objetos de transferência de dados.
Do mesmo modo que a própria interface, a implementação da interface de acesso a atributos é
genérica. Sob Bean-Managed Persistence (BMP — Persistência Gerenciada por Beans), o Entity Bean
pode ser mais simplificado, armazenando todos os seus atributos em um HashMap interno e privado,
em vez de depender, de codificação explícita dos atributos como geralmente ocorre. Para Entity Beans
grandes, esta otimização pode simplificar muito o código do Entity Bean. Usando este HashMap inter-
/**
* Retorna pares de chave/valor de atributos de Entity Beans
* @return java.util.Map
*/
/**
* Usado pelos clientes para especificar os atributos nos quais eles estão
interessados
* @return java.util.Map
* @param keysofAttributes o nome dos atributos nos quais o cliente está
* interessado
*/
while ( key.hasNext () )
{
aKey = keys.next ();
aMap.put ( aKey, this.attributes.get ( aKey) );
}
/**
* Usado pelos clientes para atualizar atributos particulares no Entity Bean
* @param keyValuePairs java.util.Map
*/
public void setAttributes (Map keyValuePairs)
{
Iterator entries = keyValuePairs.entrySet ().iterator ();
Map.Entry anEntry = null;
䡲 Defina variáveis do tipo static final na interface local ou remota do Entity Bean.
Um cliente de Entity Bean pode fazer chamadas a ele usando referências a variáveis static final
contendo a chave correta para obter um atributo. Por exemplo, para recuperar os atributos de
um Entity Bean Employee, o Session Bean poderia usar o seguinte código:
Esta abordagem funciona muito bem com a da fábrica de DTOs, em que um Session Bean busca
seus atributos diretamente no Entity Bean, com a intenção de retornar um objeto de transferência
de dados codificado explicitamente para o cliente, em vez de um HashMap. Neste caso, apenas o
Session Bean e o Entity Bean precisam concordar os nomes das chaves dos atributos, tornando a
interface local/remota um bom local para localizar os nomes dos atributos. Esta abordagem falha
ao usar o padrão Data Transfer HashMap, pois o cliente também precisa conhecer os nomes dos
valores-chave, mas não tem acesso à interface local/remota do Entity Bean.
䡲 Classe compartilhada com variáveis do tipo static final. Aqui, podemos criar uma
classe compartilhada pelas classes do cliente e pelas classes do servidor, o que é usado para
encapsular as strings utilizadas para povoar e ler strings de um HashMap por trás de uma
variável do tipo static final codificada explicitamente, acessível para o cliente e para o servidor.
Por exemplo, um cliente buscaria um hashmap para um atributo da seguinte forma:
accountMap.get (Names.ACCOUNTBALANCE);
Uma desvantagem desse método é que, se os mapeamentos das chaves precisassem ser atuali-
zados ou receber acréscimos, a nova classe precisaria ser redistribuída para o cliente e o servi-
dor (e seus JVM precisariam, ser assim reinicializados).
䡲 Coloque o contrato do Attribute em uma árvore JNDI. Nesta abordagem, um único
exemplar de cada tipo é mantido, colocando-se uma classe contendo as chaves em uma árvore
JNDI, acessível para o cliente e para o servidor. Os códigos do cliente e o do servidor não
precisariam ser recompilados ou reinicializados quando as chaves sofressem alteração/atuali-
zação, pois um objeto central em uma árvore JNDI conteria sempre a cópia mais recente das
chaves. A desvantagem desta solução é o overhead causado ao obter o contrato da árvore JNDI
cada vez que os valores das chaves forem requeridos.
䡲 Baixo custo de manutenção no decorrer do tempo. É possível criar novas visões dos
dados no lado servidor sem necessidade de qualquer programação no lado servidor. Os clientes
podem decidir dinamicamente que atributos mostrar.
䡲 Permite a adição dinâmica de atributos em tempo de execução. Ao usar BMP, este
padrão pode facilmente ser estendido para permitir a capacidade de adição e remoção dinâmi-
ca de atributos de um Entity Bean. Isto pode ser obtido acrescentando-se um método addAttri-
bute e um removeAttribute à interface, os quais simplesmente executam operações no atributo
HashMap.
Como acontece com todo padrão, usar o Generic Attribute Access tem suas desvantagens:
䡲 Overhead adicional por chamada de método. Para cada chamada de atributo, os clien-
tes devem usar uma chave de atributo para identificá-lo. Finalmente, os atributos precisam
sofrer conversão para seus tipos apropriados após terem sido extraídos do objeto HashMap.
䡲 Necessidade de manutenção de um contrato para chaves de atributos. Como as
strings requerem atributos, os clientes precisam se lembrar das strings-chave usadas para iden-
tificar os atributos. Definir um contrato de atributo-chave (discutido anteriormente neste pa-
drão) pode aliviar estas dependências.
䡲 Perda de checagem de tipificação/tempo de compilação. Quando usamos DTOs, os
valores passados por gets ou sets são sempre do tipo correto. Quaisquer erros seriam passados
em tempo de compilação. Ao utilizarmos o padrão Generic Attribute Access, o acesso aos atribu-
tos deve ser gerenciado pelo cliente em tempo de execução por meio da conversão de objetos
para seus tipos corretos e da associação dos tipos corretos dos atributos à chave correta.
De um modo geral, o padrão Generic Attribute Access fornece um método genérico de gerencia-
mento do estado dos Entity Beans, eliminando a grande quantidade de código repetitivo associado ao
acesso a dados de Entity Beans específicos do domínio.
Padrões relacionados
Property Container (Carey et al., 2000)
Data Transfer HashMap
BUSINESS INTERFACE
A especificação EJB determina que uma classe Enterprise Bean forneça uma implementação de
todos os métodos declarados nas interfaces locais ou remotas, mas o Bean não pode implementar estas
interfaces diretamente.
Como as inconsistências entre os métodos das interfaces locais e
remotas e a implementação do Enterprise Bean podem ser descobertas
em tempo de compilação?
* * *
Um dos erros mais comuns durante o processo de desenvolvimento EJB é a falta de consistência
entre as definições dos métodos de negócio nas interfaces remotas ou locais e as implementações na
classe Enterprise Bean. A especificação EJB requer que o Enterprise Bean implemente de modo ade-
quado todas as assinaturas de métodos de negócio definidas na interface local/remota, mas não forne-
ce uma maneira automática de detecção de problemas em tempo de compilação. Muitos tipos de erros
podem acontecer a partir deste desacoplamento entre a definição da interface e a implementação,
incluindo erros de nomes de métodos, tipos de parâmetros, exceções, parâmetros inconsistentes e
outros. Como resultado, estes tipos de erros não podem ser detectados em tempo de compilação, e o
Padrões de arquitetura da camada EJB 49
Portanto:
Crie uma superinterface, chamada interface de negócio, que define
todos os métodos de negócio. Deixe tanto a interface local/remota quan-
to a classe Enterprise Bean implementar esta interface, forçando checa-
gens de consistência em tempo de compilação.
Interface de negócio é uma interface Java simples que define as assinaturas de método para
todos os métodos de negócio que um Enterprise Bean decidir expor. É implementada pela interface
local ou remota e pela classe Enterprise Bean, conforme mostrado na Figura 1.16. Criando esta supe-
rinterface, erros de consistência entre as definições de assinaturas de método na interface local/remo-
ta, bem como a classe Enterprise Bean, podem ser apanhados em tempo de compilação.
A interface de negócio não implementa javax.ejb.EJBObject ou javax.ejb.EJBLocalObject, de modo
que o desenvolvedor da classe Bean não tem de implementar métodos vazios. Além disso, o desenvol-
vedor não pode converter a classe Bean diretamente para sua interface local ou remota, o que evita
que ele passe this para seus clientes.
O padrão Business Interface difere ligeiramente, dependendo de o Enterprise Bean expor seus
métodos de negócio na interface local ou remota. Se o Entity Bean expuser a interface remota, todas as
assinaturas de método na interface de negócio precisarão gerar java.rmi.RemoteException (mas não
precisarão estender java.rmi.Remote). Observe que as implementações de métodos na classe Enterpri-
se Bean não devem gerar RemoteException: isso foi condenado pela especificação EJB. Em vez disso, os
métodos de negócio podem gerar EJBException de dentro do corpo de um método sem declará-lo na
cláusula “throws”, visto que EJBException é uma subclasse de RuntimeException.
Ao usar a interface de negócio com uma interface local, aquela não precisa implementar qual-
quer outra interface, e as assinaturas dos métodos de negócio podem ser escritas sem quaisquer regras
especiais.
Há um efeito colateral perigoso no uso do padrão Business Interface: Para métodos cujos valo-
res de retorno são a interface local/remota do próprio Bean, implementar a interface de negócio
permite ao desenvolvedor do Bean retornar this sem que seja detectado qualquer problema em tempo
de compilação. Isto é possível porque tanto a classe Bean quanto a interface local/remota implemen-
tam a interface de negócio. Retornar this é sempre pego em tempo de compilação quando o padrão
Business Interface não é usado (já que a classe Bean não implementa a interface local/remota). Assim,
os desenvolvedores de Beans que usam um padrão de interface de negócio devem redobrar a atenção
para não retornar this ou erros imprevisíveis em tempo de execução.
O padrão Business Interface é comum no desenvolvimento EJB. Ele permite aos desenvolvedo-
res apanhar erros comuns de programação em tempo de compilação, assegurando a consistência entre
a definição e a implementação de métodos de negócio.