Você está na página 1de 30

INTRODUÇÃO 21

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.

Como um cliente EJB pode executar a lógica de negócio de um caso


de uso em uma transação e em uma chamada de rede?
***
Para executar a lógica de negócio de um caso de uso típico, em geral, diversos objetos no lado
servidor (como Session Beans ou Entity Beans) precisam ser acessados e possivelmente modificados.
O problema é que diversas chamadas de granularidade fina a Session ou Entity Beans adicionam o
overhead causado por diversas chamadas de rede (e possivelmente diversas transações), além de con-
tribuírem para dificultar a manutenção do código, já que o acesso a dados e à lógica de negócio ficam
espalhados pelos clientes.
Considere um cenário de um banco online onde um servlet recebe um pedido de transferência
de fundos de uma conta para outra em nome de um cliente Web. Neste cenário (conforme mostrado
na Figura 1.1), o servlet deve checar para se assegurar de que o usuário tenha autorização, retirar os
fundos de um Entity Bean de conta bancária e depositá-los em outro.
Ao executar métodos nas interfaces Home e remotas dos Entity Beans, esta abordagem não
suportará aumentos sérios de carga, pois o cenário inteiro requer pelo menos seis chamadas de rede:
três para encontrar os Entity Beans apropriados e mais três para realmente transferir os fundos. Além
disso, tendo em vista que Entity Beans são entes transacionais, cada chamada de método em uma
entidade exigirá uma transação separada no lado do servidor, demandando sincronização da entidade
remota com seus dados armazenados e manutenção por parte do servidor de aplicação.
O pior é que esta abordagem não garantirá a segurança do dinheiro do cliente. Se algo der
errado com o depósito, esse dinheiro já terá sido retirado e estará perdido. A checagem da autorização
do usuário, a retirada e o depósito são todos executados completamente à parte e, se o depósito falhar,
a retirada não será desfeita, resultando em um estado inconsistente. Neste caso, o problema é que, ao
se chamar um método de um Entity Bean diretamente, cada chamada de método é uma unidade de
trabalho separada e uma transação separada.

FIGURA 1.1 Cliente transferindo fundos entre Entity Beans.


Padrões de arquitetura da camada EJB 25

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

FIGURA 1.2 Os benefícios arquitetônicos de Session Façade.

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.

FIGURA 1.3 Os benefícios de desempenho do Session Façade.


Padrões de arquitetura da camada EJB 27

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-

FIGURA 1.4 Aparência da arquitetura agrupando-se casos de uso em Session Beans.


28 CAPÍTULO 1

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

FIGURA 1.5 Caso de uso Reserve Seat.

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

deste MDB é encapsular toda a lógica de negócio/workflow relacionada à reserva de lugares em um


vôo e executar assincronamente, conforme mostrado na Figura 1.6.
Aqui temos um servlet cliente criando uma mensagem Java Message Service (JMS) e passando
os parâmetros necessários. O servlet constrói uma mensagem contendo todos os parâmetros necessá-
rios (a chave primária do usuário, o número do vôo, a chave primária do avião) e a envia para um
destino JMS criado para o caso de uso Reserve Seat. Ao receber a mensagem no destino apropriado, o
cliente ficará livre para prosseguir (mostrar a próxima página Web). O contêiner do Bean orientado a
mensagens tentará passar a mensagem para o próximo Bean orientado à mensagem ReserveSeat
disponível. Se todos os MDBs ReserveSeat no grupo estiverem sendo usados no momento da recepção
da mensagem, o servidor JMS deverá esperar até que o próximo esteja disponível. Se este caso de uso
tivesse sido executado por meio de uma fachada de sessão, um grupo de Session Beans completamen-
te usado teria sido a única falha e o cliente teria que tentar de novo manualmente.
Uma vez que um MDB esteja disponível, o contêiner executará o método onMessage( ). Neste
ponto, o Bean orientado a mensagens ReserveSeat irá registrar o usuário na empresa aérea, checar se
os assentos estão disponíveis e reservar um lugar linearmente através do processo de execução do caso
de uso. Enquanto este processo, que consome tempo, estiver ocorrendo, o usuário final ficará livre
para navegar pelo site e trabalhar no seu negócio.
Uma vantagem importante que o padrão Message Façade tem sobre o Session Façade é que se
pode garantir casos de usos executados assincronamente. Isto significa que, se a transação falhar em
algum ponto (talvez o sistema de reservas saia do ar ou qualquer outra falha de sistema ocorra), a
transação será desfeita e a mensagem JMS será colocada de volta na fila. Uma nova tentativa de
execução da transação será feita mais tarde, sem o conhecimento do cliente.
Este comportamento por-trás-da-cena também apresenta um problema. Como o cliente será
notificado se o caso de uso falhar ou se for bem-sucedido? Por exemplo, se um lugar não puder ser
reservado pelo fato de o avião estar lotado, o cliente precisaria ser notificado. Em um modelo síncrono
(usando uma fachada de sessão), o cliente saberia imediatamente. Em um modelo assíncrono, o clien-
te não estaria mais esperando para ver se o caso de uso foi bem-sucedido e precisaria ser alertado de
alguma forma específica para a aplicação. A solução mais comum é via correio eletrônico. Se o caso de
uso for bem-sucedido ou falhar, o sistema notificará o usuário por e-mail. Algumas companhias podem
implementar o sistema de modo que alguém fizesse uma ligação telefônica, e assim por diante. Se os
requisitos da aplicação permitirem, algumas aplicações poderão usar um modelo compartilhado. Isto
significa que o usuário final receberá um local aonde poderá ir para checar o status de seus pedidos, de
maneira similar ao número de rastreamento usado por serviços de entrega modernos.

FIGURA 1.6 Caso de uso Reserve Seat com um Message Façade.


32 CAPÍTULO 1

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

O Message Façade é um padrão muito poderoso para a construção de aplicações desacopladas


e altamente escaláveis. Um sistema EJB típico provavelmente usaria uma combinação dos padrões
Session Façade e Message Façade. O Session Façade é a melhor opção para operações do tipo “leitura”,
em que o cliente requer algum dado do servidor, ou quando o cliente precisa esperar visivelmente até
que um caso de uso tenha sido completado. O Message Façade é a melhor opção para operações de
atualização, cujos resultados o cliente não precisa ver instantaneamente.
Os benefícios relacionados a escalabilidade e tolerância a falhas que o padrão Message Faça-
de tem em relação ao padrão Session Façade são significativos. Em termos de desempenho, um
sistema baseado em mensagens irá escalar melhor do que uma abordagem de Session Beans cluste-
rizados, porque os Beans de mensagens puxam trabalho em vez de fazerem com que o trabalho seja
empurrado para eles. Esta abordagem escala melhor quando clusterizamos, porque otimiza o uso
dos recursos do sistema.
Os desenvolvedores devem avaliar com cuidado cada caso de uso em seus projetos, perguntan-
do a si mesmos se o caso de uso é de natureza síncrona ou assíncrona. Este será um fator decisivo na
preferência de um padrão a outro.

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

䡲 Processo mais lento de desenvolvimento. Devido ao fato de a lógica do caso de uso


(que pode mudar freqüentemente) ser executada em um Session Bean, toda vez que um caso de
uso precisar ser alterado (isto é, para acrescentar um parâmetro a um método ou retornar um
atributo adicional), o método do Session Bean que implementa esse caso de uso poderá precisar
ser alterado. O processo de alteração de um Session Bean não é trivial — uma mudança requer
muitas vezes a edição de três arquivos diferentes (interface, classe Bean, descritor de localiza-
ção), bem como a redistribuição no servidor EJB e a possível reinicialização do servidor. Além
disso, o encarregado de negócios que encapsula o Session Bean alterado no cliente normalmen-
te precisará também ser alterado.
䡲 A divisão de trabalho em um projeto grande é mais difícil. Dependendo das estra-
tégias usadas para dividir o trabalho entre os desenvolvedores em um projeto, a fachada de
sessão é muitas vezes um gargalo com o qual diferentes equipes ou desenvolvedores irão lutar,
já que ele pode estar sujeito a freqüentes alterações à medida que o projeto progride.
䡲 Os recursos do servidor muitas vezes são controlados por apenas uma equipe
em uma corporação grande. Em corporações grandes com conjuntos de EJBs distribuídos
já estabelecidos e funcionando, pode ser difícil para as equipes trabalhando em outros projetos
efetuar mudanças em classes preexistentes.
Resumindo, desenvolver com uma fachada de sessão e encarregados de negócios pode resultar
em diversos ciclos altera-distribui-testa, o que pode se tornar um gargalo em um projeto grande. O
ponto crucial do problema é que a lógica de negócio está sendo colocada em uma camada de sessão
EJB, cujo desenvolvimento pode ser bem pesado.
Portanto:
Use o padrão Command para envolver lógica de negócio em Com-
mand Beans leves que desacoplem o cliente do EJB, executem em uma
chamada de rede e ajam como uma fachada para a camada EJB.
Um Command Bean é apenas uma classe Java comum com gets, sets e um método execute,
conforme descrito no padrão Command original (Gamma et al., 1995). Aplicado ao EJB, o padrão
Command fornece uma solução leve para a obtenção dos mesmos benefícios dos padrões Session
Façade e Business Delegate: uma fachada que esconde o modelo de objetos na camada EJB, a execu-
ção de um caso de uso em uma transação e em uma chamada de rede, e o desacoplamento completo
entre o cliente e o EJB. O padrão Command consegue isto fornecendo aos clientes classes com as quais
eles interagem localmente, mas que na verdade são executadas dentro de um servidor EJB remoto,
transparente ao cliente.
Os Commands são usados para encapsular unidades individuais de trabalho em uma aplicação.
Um caso de uso como placeOrder, transferFunds e outros teria sua lógica de negócio/workflow encap-
sulada em um Command especial feito apenas para aquele caso de uso, como mostrado na Figura 1.7.
A interação do cliente com um Command é muito simples. Uma vez que o cliente obtenha um
Command (seja criando um ou obtendo de uma fábrica, dependendo da implementação), ele simples-
mente define atributos no Command, até que este contenha todos os dados necessários para executar
um caso de uso. Neste ponto, o cliente pode chamar o método execute do Command e então simples-
mente executar gets no mesmo até que tenha recuperado todos os dados resultantes da execução do
Command/caso de uso.
Quando o cliente executa o Command, coisas interessantes acontecem por trás da cena. Em vez
de ser executado localmente, ele é na verdade transferido para um servidor EJB remoto e executado
dentro do JVM do servidor EJB. Assim, todos os EJBs chamados pelo Command durante a execução de
seu caso de uso ocorrem dentro do próprio servidor EJB. Quando o Command completa sua execução,
é retornado para o cliente, o qual pode então chamar métodos get para recuperar dados. Fazendo o
Command executar dentro do servidor EJB, o caso de uso pode ser executado dentro de apenas uma
transação. A mecânica de implementação deste comportamento será explicada mais adiante, na dis-
cussão deste padrão.
Padrões de arquitetura da camada EJB 35

FIGURA 1.7 A visão do cliente do Transfer Funds Command.

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

FIGURA 1.8 Usando um Transfer Funds Command.


36 CAPÍTULO 1

ú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.

FIGURA 1.9 Interações do padrão Command.


Padrões de arquitetura da camada EJB 37

䡲 Separa a lógica de negócio da lógica de apresentação. Os comandos agem com uma


fachada para o modelo de objetos no servidor, encapsulando lógica de negócio dentro de Com-
mands e expondo apenas uma interface simples de Command para os clientes usarem. Esta
separação permite que o cliente e o servidor sejam desenvolvidos separadamente.
䡲 Força a execução dos casos de uso em um único ciclo. Tendo em vista que o Com-
mand na verdade é executado no servidor EJB, é necessária apenas uma chamada de rede (e
transação) para completar um caso de uso complicado.
䡲 Desacopla o cliente do EJB. Os clientes são completamente desacoplados dos detalhes da
implementação do servidor — tudo o que eles vêem é o Command Bean, o qual parece uma
classe local.
䡲 Comandos podem ser executados localmente ou produzir dados fictícios. No
início de um projeto, podem ser criados comandos vazios ou falsos, permitindo que os desenvol-
vedores da camada de apresentação escrevam, compilem e testem seu código independente-
mente da lógica de negócio e da equipe EJB.

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

DATA TRANSFER OBJECT FACTORY

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

FIGURA 1.10 CarDTOFactory.


40 CAPÍTULO 1

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)

GENERIC ATTRIBUTE ACCESS

O cliente de um Entity Bean precisa acessar os atributos deste.


Como o cliente de um Entity Bean pode acessar e manipular eficien-
temente os atributos deste de um modo genérico e em grandes quantida-
des?
* * *
Na prática comum, os Entity Beans são acessados por meio de métodos get/set de sua interface
local (para Entity Beans escritos em EJB 2.X em diante) ou por meio de grandes objetos de transferên-
Padrões de arquitetura da camada EJB 43

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:

public interface AttributeAccess {


public Map getAttributes (Collection keysOfAttributes);
public Map getAllAttributes ();
public void setAttributes (Map keyAndValuePairs);
}

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-

FIGURA 1.14 Uso da interface de acesso a atributos.


Padrões de arquitetura da camada EJB 45

no, a implementação dos métodos no AttributeAccess se torna, portanto, completamente genérica e


reusável por todos os Entity Beans BMP.

private java.util.HashMap attributes;

/**
* Retorna pares de chave/valor de atributos de Entity Beans
* @return java.util.Map
*/

public Map getAllAttributes ()


{
return (HashMap)attributes.clone ();
}

/**
* 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
*/

public Map getAttributes (Collection keysofAttributes)


{
Iterator keys = keysofAttributes.iterator ();
Object aKey = null;
HashMap aMap = new HashMap ();

while ( key.hasNext () )
{
aKey = keys.next ();
aMap.put ( aKey, this.attributes.get ( aKey) );
}

//o map agora tem todos os dados requisitados


return aMap;
}

/**
* 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;

while ( entries.hasNext ())


{
anEntry = (Map.Entry)entries.next ();
this.attributes.put (anEntry.getKey (), anEntry.getValue ());
}
}
46 CAPÍTULO 1

No CMP (Container-Managed Persistence), não é possível usar um mapa interno de atributos, já


que a implementação das classes do Entity Bean é abstraída atrás de métodos get e set gerados por um
contêiner. Quando não for possível usar um mapa interno, a interface de acesso a atributos pode ser
implementada genericamente usando a API de Reflexão Java. Isto significa que, no setAttributes, a
reflexão pode ser executada sobre o valor-chave dos atributos que o cliente queira configurar. Especi-
ficamente, se o valor-chave for XXX, a implementação de setAttribute tentará chamar setXXX (...) do
Entity Bean. Do mesmo modo, no método getAttributes, a reflexão pode ser usada para encontrar
todos os métodos get em um Entity Bean, chamá-los e povoar um mapa para o cliente. Se o desenvol-
vedor preferir não usar a API de Reflexão, a implementação do método Attribute Access não poderá
ser feita genericamente para CMP. O desenvolvedor precisará entrelaçar sua implementação de Attri-
bute Access como declarações IF que chamem métodos get/set explícitos no Entity Bean, dependendo
do valor-chave.
Para tornar o código da implementação de Attribute Access reusável por todos os Entity
Beans, pode-se usar uma superclasse para implementar os métodos da interface, que são completa-
mente genéricos, se se usar o método reflexivo ou o mapa interno de atributos. Todos os Entity
Beans que quiserem fazer uso dos serviços do Attribute Access precisam apenas ser suas subclasses.
Desta forma, expõem automaticamente esta interface unificada para seus atributos, sem necessida-
de de código extra.
A peça final do quebra-cabeças é como nomear as chaves que identificam os atributos de um
Entity Bean. Não apenas os atributos precisam ser identificados por uma chave, mas a lógica que
acessa o Entity Bean por meio da interface de acesso a atributos e o próprio Entity Bean precisam
concordar com as convenções de nomeações. É necessário algum tipo de “contrato” entre o cliente e o
servidor. Diversas possibilidades são discutidas:
䡲 Estabeleça uma convenção de nomeações consistente e bem documentada. O
cliente e o Entity Bean podem concordar uma convenção de nomeações bem documentada e
consistente para os atributos. Para um Entity Bean Account, “com.bank.Account.accountName”,
ou simplesmente “accountName” seriam exemplos de convenções consistentes. A desvantagem
desta abordagem é que o contrato existe apenas nas mentes dos desenvolvedores e no papel. É
fácil, durante o desenvolvimento, errar o nome do atributo, resultando em erros de desenvolvi-
mento custosos e difíceis de encontrar.

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

//Solicitar os atributos pessoais do empregado


Collection aCollection = new ArrayList ();
aCollection.add (Employee.NAME);
aCollection.add (Employee.EMAIL);
aCollection.add(Employee.SEX);
aCollection.add(Employee.SSN);

Map aMap = employee.getAttributes (aCollection);

em que a interface local do Entity Bean contém as seguintes definições:

public interface Employee extends EJBLocalObject, AttributeAccess


{

//Já que os atributos estão armazenados em um hashmap no Entity Bean,


//precisamos de um lugar central para armazenar as 'chaves' usadas para
//referenciar atributos, de modo que os clientes e o Entity Bean não
Padrões de arquitetura da camada EJB 47

//precisem ser codificados explicitamente com conhecimento das


//strings-chave dos atributos
public final static String ID = "EMPLOYEEID";
public final static String NAME = "NAME";
public final static String EMAIL = "EMAIL";
public final static String AGE = "AGE";
public final static String SSN = "SSN";
public final static String SEX = "SEX";
...
}

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);

Assim, a classe compartilhada chamada Names teria o aspecto:

public class Names {

public final static String ACCOUNTBALANCE = "BALANCE";


...
}

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.

O padrão Generic Attribute Access tem muitas vantagens:


䡲 Uma interface para todos os Entity Beans. Os clientes de Entity Beans podem manipu-
lá-los consistentemente por meio da interface de acesso aos atributos, simplificando o código do
cliente. Os Entity Beans também são simplificados, porque o acesso aos atributos pode ser
encapsulado em uma superclasse.
䡲 Escala bem em Entity Beans grandes. Se um Entity Bean tiver vinte ou dois mil atribu-
tos, a lógica de acesso aos atributos será simplificada para apenas algumas linhas.
48 CAPÍTULO 1

䡲 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

desenvolvedor EJB deve realizar manualmente a manutenção da consistência entre a definição da


interface e a implementação do Bean.
Os erros podem ser detectados somente com a ferramenta de pós-compilação proprietária do
seu vendedor de servidor EJB. Essas ferramentas são normalmente usadas para testar a conformidade
de classes Java compiladas à especificação EJB antes de empacotá-las e distribuí-las. Tais ferramentas
de pós-compilação normalmente são lentas e difíceis de usar, sendo menos viáveis para práticas de
compilação incremental que os desenvolvedores muitas vezes usam para detectar os erros logo no
início. Como resultado, os erros de desenvolvimento são apanhados mais tarde durante este processo.
Uma solução seria fazer o Enterprise Bean implementar diretamente a interface local ou remota
na classe Bean, o que forçaria a consistência entre a definição de métodos e suas implementações,
usando qualquer compilador Java padrão. Infelizmente, a especificação EJB não recomenda esta prá-
tica, e com um bom motivo: a interface remota estende a interface javax.ejb.EJBObject e a interface
local implementa a interface javax.EJBLocalObject, conforme mostrado na Figura 1.15. Essas interfa-
ces definem métodos extras (isIdentical, getPrimaryKey, remove, etc.), que devem ser implementados
pelos stubs EJBObject e EJBLocalObject, não pela classe Enterprise Bean.
Para fazer seu Bean compilar, você teria que abarrotar a classe Enterprise Bean escrevendo
implementações vazias destes métodos extras. Além disso, se ela implementasse diretamente a inter-
face local ou remota, o Bean poderia ser convertido diretamente para uma dessas interfaces, permitin-
do ao desenvolvedor passar uma instância de this para um cliente. Este comportamento não é permitido
pela especificação EJB. Para passar uma referência, o Bean precisa primeiro obter uma referência para
si próprio, chamando getEJBObject ou getEJBLocalObject da interface SessionContext ou EntityContext.
Os desenvolvedores EJB não devem implementar interfaces locais ou remotas diretamente em
sua classe Enterprise Bean, mas precisam de um mecanismo que permita uma confirmação em tempo
de compilação da consistência entre as definições de métodos nas interfaces remota e local e as imple-
mentações na classe Bean.

FIGURA 1.15 Interfaces EJBObject e EJBLocalObject.


50 CAPÍTULO 1

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

FIGURA 1.16 Interface de negócio para Beans locais e remotos.


Padrões de arquitetura da camada EJB 51

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.

Você também pode gostar