2. Arquitetura de uma aplicação Java em camadas
Uma aplicação Java é frequentemente dividida em camadas, cada uma com uma função bem definida. Consideremos uma arquitetura comum, a de três camadas:
![]() |
- a camada [1], aqui denominada [ui] (Interface do Usuário), é a camada que interage com o usuário, por meio de uma interface gráfica Swing, uma interface de console ou uma interface web. Sua função é fornecer dados provenientes do usuário à camada [2] ou apresentar ao usuário os dados fornecidos pela camada [2].
- A camada [2], aqui denominada [metier], é a camada que aplica as regras de negócio, c.a.d. a lógica específica da aplicação, sem se preocupar com a origem dos dados que lhe são fornecidos nem com o destino dos resultados que ela produz.
- a camada [3], aqui denominada [DAO] (Data Access Object), é a camada que fornece à camada [2] dados pré-registrados (arquivos, bancos de dados, ...) e que grava alguns dos resultados fornecidos pela camada [2].
Existem diferentes possibilidades para implementar a camada [DAO]. Vamos examinar algumas delas:
![]() |
A camada [JDBC] acima é a camada padrão utilizada em Java para acessar bancos de dados. Ela isola a camada [DAO] da camada SGBD, que gerencia o banco de dados. Teoricamente, é possível alterar a camada SGBD sem alterar o código da camada [DAO]. Apesar dessa vantagem, a camada API JDBC apresenta algumas desvantagens:
- todas as operações na camada SGBD podem lançar a exceção controlada (checked) SQLException. Isso obriga o código chamador (a camada [DAO], neste caso) a envolvê-las em blocos try/catch, tornando o código bastante pesado.
- a camada [DAO] não é totalmente independente da SGBD. Estas, por exemplo, possuem métodos proprietários para a geração automática de valores de chaves primárias que a camada [DAO] não pode ignorar. Assim, ao inserir um registro:
- com o Oracle, a camada [DAO] deve primeiro obter um valor para a chave primária do registro e, em seguida, inseri-lo.
- com o SQL Server, a camada [DAO] insere o registro, ao qual é atribuído automaticamente um valor de chave primária pela camada SGBD, valor esse repassado à camada [DAO].
Essas diferenças podem ser eliminadas por meio do uso de procedimentos armazenados. No exemplo anterior, a camada [DAO] chamará um procedimento armazenado no Oracle ou no SQL Server, que levará em conta as particularidades do SGBD. Essas particularidades ficarão ocultas na camada [DAO]. No entanto, embora a alteração do SGBD não implique reescrever a camada [DAO], ela implica, ainda assim, reescrever os procedimentos armazenados. Isso pode não ser considerado um obstáculo insuperável.
Vários esforços foram realizados para isolar a camada [DAO] dos aspectos proprietários da SGBD. Uma solução que obteve grande sucesso nessa área nos últimos anos é a do Hibernate:
![]() |
A camada [Hibernate] se posiciona entre a camada [DAO], escrita pelo desenvolvedor, e a camada [JDBC]. O Hibernate é um ORM (Mapeador Objeto-Relacional), uma ferramenta que faz a ponte entre o mundo relacional dos bancos de dados e o mundo dos objetos manipulados pelo Java. O desenvolvedor da camada [DAO] não vê mais a camada [JDBC] nem as tabelas do banco de dados cujo conteúdo deseja explorar. Ele vê apenas a representação objeto do banco de dados, fornecida pela camada [Hibernate]. A ponte entre as tabelas do banco de dados e os objetos manipulados pela camada [DAO] é estabelecida principalmente de duas maneiras:
- por meio de arquivos de configuração do tipo XML
- por meio de anotações Java no código, técnica disponível apenas a partir da versão 1.5 da camada JDK
A camada [Hibernate] é uma camada de abstração que se propõe a ser a mais transparente possível. O ideal é que o desenvolvedor da camada [DAO] possa ignorar completamente o fato de estar trabalhando com um banco de dados. Isso é possível se não for ele quem escrever a configuração que faz a ponte entre o mundo relacional e o mundo dos objetos. A configuração dessa ponte é bastante delicada e requer certa familiaridade.
A camada [4] dos objetos, espelho da BD, é chamada de “contexto de persistência”. Uma camada [DAO] baseada no Hibernate realiza ações de persistência (CRUD: criar, ler, atualizar, excluir) nos objetos do contexto de persistência, ações traduzidas pelo Hibernate em comandos SQL executados pela camada JDBC. Para as ações de consulta ao banco de dados (o SQL Select), o Hibernate fornece ao desenvolvedor uma linguagem HQL (Hibernate Query Language) para consultar o contexto de persistência [4] e não a própria BD.
O Hibernate é popular, mas complexo de dominar. A curva de aprendizado, frequentemente apresentada como fácil, é, na verdade, bastante íngreme. Assim que se tem um banco de dados com tabelas que apresentam relações um-para-muitos ou muitos-para-muitos, a configuração da ponte relacional/objetos não está ao alcance de qualquer iniciante. Erros de configuração podem levar a aplicativos com baixo desempenho.
Diante do sucesso dos produtos ORM, a Sun, criadora do Java, decidiu padronizar uma camada ORM por meio de uma especificação chamada JPA (Java Persistence API), lançada junto com o Java 5. A especificação JPA foi implementada por diversos produtos: Hibernate, Toplink, EclipseLink, OpenJpa, ... Com o JPA, a arquitetura anterior passa a ser a seguinte:
![]() |
A camada [DAO] agora interage com a especificação JPA, um conjunto de interfaces. O desenvolvedor ganhou em termos de padronização. Antes, se ele alterasse sua camada ORM, precisava também alterar sua camada [DAO], que havia sido escrita para interagir com um ORM específico. Agora, ele escreverá uma camada [DAO] que se comunicará com uma camada JPA. Independentemente do produto que a implemente, a interface da camada JPA apresentada à camada [DAO] permanece a mesma.
Neste documento, utilizaremos uma camada [DAO] baseada em uma camada JPA/Hibernate ou JPA/EclipseLink. Além disso, utilizaremos o framework Spring 2.8 para interligar essas camadas entre si.
![]() |
O grande diferencial do Spring é que ele permite conectar as camadas por meio de configuração, e não no código. Assim, se a implementação JPA / Hibernate precisar ser substituída por uma implementação do Hibernate sem JPA — por exemplo, porque a aplicação está sendo executada em um ambiente JDK 1.4 que não suporta JPA —, essa mudança na implementação da camada [DAO] não tem impacto no código da camada [métier]. Apenas o arquivo de configuração do Spring, que vincula as camadas entre si, precisa ser modificado.
Com o Java EE 5, existe outra solução: implementar as camadas [metier] e [DAO] com EJB3 (Enterprise Java Bean versão 3):
![]() |
Veremos que essa solução não é muito diferente daquela que utiliza o Spring. O ambiente Java EE5 está disponível em servidores conhecidos como servidores de aplicativos, tais como o Sun Application Server 9.x (Glassfish), Jboss Application Server, Oracle Container for Java (OC4J), etc. Um servidor de aplicativos é, essencialmente, um servidor de aplicativos web. Existem também ambientes EE 5 chamados de “stand-alone”, c.a.d. que podem ser utilizados fora de um servidor de aplicativos. É o caso de JBoss, EJB3 ou OpenEJB.
Em um ambiente EE5, as camadas são implementadas por objetos chamados EJB (Enterprise Java Bean). Nas versões anteriores do EE, os EJB (EJB e 2.x) eram considerados difíceis de implementar, testar e, às vezes, de baixo desempenho. Distinguem-se os EJB2.x “entity” e os EJB2.x “session”. Resumindo, um EJB2.x “entity” é a representação de uma linha de uma tabela de banco de dados, e um EJB2.x “session” é um objeto utilizado para implementar as camadas [metier], [DAO] de uma arquitetura multicamadas. Uma das principais críticas feitas às camadas implementadas com EJB é que elas só podem ser utilizadas dentro de contêineres EJB, um serviço fornecido pelo ambiente EE. Esse ambiente, mais complexo de implementar do que um ambiente SE (Standard Edition), pode desestimular o desenvolvedor a realizar testes com frequência. No entanto, existem ambientes de desenvolvimento Java que facilitam o uso de um servidor de aplicativos ao automatizar a implantação dos EJB no servidor: Eclipse, NetBeans, JDeveloper, IntelliJ e IDEA. Aqui, utilizaremos o NetBeans 6.8 e o servidor de aplicativos GlassFish v3.
O framework Spring surgiu como resposta à complexidade dos EJB2. O Spring fornece, em um ambiente SE, um número significativo dos serviços normalmente oferecidos pelos ambientes EE. Assim, na seção “Persistência de dados”, o Spring fornece os pools de conexão e os gerenciadores de transações de que as aplicações necessitam. O surgimento do Spring promoveu a cultura dos testes unitários, que se tornaram mais fáceis de implementar no contexto SE do que no contexto EE. O Spring permite a implementação das camadas de uma aplicação por meio de objetos Java clássicos (POJO, Plain Old/Ordinary Java Object), possibilitando a reutilização desses objetos em outro contexto. Por fim, ele integra diversas ferramentas de terceiros de maneira bastante transparente, notadamente ferramentas de persistência como Hibernate, EclipseLink, Ibatis, ...
O Java EE5 foi projetado para corrigir as lacunas da especificação EJB2. Os EJB 2.x passaram a ser os EJB3. Estes são POJOs marcados por anotações que os tornam objetos específicos quando estão dentro de um contêiner EJB3. Nesse contêiner, o EJB3 poderá se beneficiar dos serviços do contêiner (pool de conexões, gerenciador de transações, etc.). Fora do contêiner EJB3, o EJB3 torna-se um objeto Java normal. Suas anotações EJB são ignoradas.
Acima, representamos o Spring e um contêiner EJB3 como uma possível infraestrutura (framework) para nossa arquitetura multicamadas. É essa infraestrutura que fornecerá os serviços de que precisamos: um pool de conexões e um gerenciador de transações.
- Com o Spring, as camadas serão implementadas com POJOs. Esses terão acesso aos serviços do Spring (pool de conexões, gerenciador de transações) por meio da injeção de dependências nesses POJOs: durante a construção deles, o Spring injeta referências aos serviços de que eles precisarão.
- Com o contêiner EJB3, as camadas serão implementadas com EJB. Uma arquitetura em camadas implementada com EJB3 difere pouco daquelas implementadas com POJO instanciados pelo Spring. Encontraremos muitas semelhanças.
- Para concluir, apresentaremos um exemplo de aplicação web multicamadas:
![]() |






