Vamos compartilhar arquiteturas de aplicativos da Web baseadas em Java!
Existem muitas arquiteturas diferentes para aplicativos da web que devem ser implementadas usando Java. As respostas a essa pergunta podem servir como uma biblioteca de vários designs de aplicativos da Web com seus prós e contras. Embora eu perceba que as respostas serão subjetivas, vamos tentar ser o mais objetivos possível e motivar os prós e os contras que listamos.
Use o nível de detalhe que você preferir para descrever sua arquitetura. Para que sua resposta seja de qualquer valor, você precisará pelo menos descrever as principais tecnologias e idéias usadas na arquitetura que descreve. E por último mas não menos importante, quando devemos usar sua arquitetura?
Vou começar...
Visão geral da arquitetura
Utilizamos uma arquitetura de três camadas com base nos padrões abertos da Sun, como Java EE, Java Persistence API, Servlet e Java Server Pages.
- Persistência
- O negócio
- Apresentação
Os possíveis fluxos de comunicação entre as camadas são representados por:
Persistence <-> Business <-> Presentation
O que, por exemplo, significa que a camada de apresentação nunca chama ou executa operações de persistência, mas sempre através da camada de negócios. Essa arquitetura destina-se a atender às demandas de um aplicativo da web de alta disponibilidade.
Persistência
Executa operações de persistência de criação, leitura, atualização e exclusão ( CRUD ). No nosso caso, estamos usando o JPA ( Java Persistence API ) e atualmente usamos o Hibernate como nosso provedor de persistência e usamos seu EntityManager .
Essa camada é dividida em várias classes, onde cada classe lida com um certo tipo de entidades (ou seja, entidades relacionadas a um carrinho de compras podem ser tratadas por uma única classe de persistência) e é usada por um e apenas um gerente .
Além disso, essa camada também armazena entidades JPAAccount
, como coisas ShoppingCart
etc.
O negócio
Toda a lógica vinculada à funcionalidade do aplicativo da web está localizada nessa camada. Essa funcionalidade pode estar iniciando uma transferência de dinheiro para um cliente que deseja pagar por um produto on-line usando seu cartão de crédito. Também poderia estar criando um novo usuário, excluindo um usuário ou calculando o resultado de uma batalha em um jogo baseado na Web.
Essa camada é dividida em várias classes e cada uma dessas classes é anotada @Stateless
para se tornar um SLSB ( Stateless Session Bean ). Cada SLSB é chamado de gerente e, por exemplo, um gerente pode ser uma classe anotada como mencionado AccountManager
.
Quando é AccountManager
necessário executar operações CRUD, ele faz as chamadas apropriadas para uma instância de AccountManagerPersistence
, que é uma classe na camada de persistência. Um esboço aproximado de dois métodos em AccountManager
poderia ser:
...
public void makeExpiredAccountsInactive() {
AccountManagerPersistence amp = new AccountManagerPersistence(...)
// Calls persistence layer
List<Account> expiredAccounts = amp.getAllExpiredAccounts();
for(Account account : expiredAccounts) {
this.makeAccountInactive(account)
}
}
public void makeAccountInactive(Account account) {
AccountManagerPersistence amp = new AccountManagerPersistence(...)
account.deactivate();
amp.storeUpdatedAccount(account); // Calls persistence layer
}
Usamos transações de gerenciador de contêineres, para que não tenhamos que fazer a demarcação de transações. O que basicamente acontece sob o capô é que iniciamos uma transação ao entrar no método SLSB e a confirmamos (ou reverte-a) imediatamente antes de sair do método. É um exemplo de convenção sobre configuração, mas ainda não precisamos de nada além do padrão Obrigatório.
Aqui está como o Tutorial do Java EE 5 da Sun explica o atributo de transação Requerido para Enterprise JavaBeans (EJB's):
Se o cliente estiver executando dentro de uma transação e chamar o método do bean corporativo, o método será executado dentro da transação do cliente. Se o cliente não estiver associado a uma transação, o contêiner iniciará uma nova transação antes de executar o método.
O atributo Requerido é o atributo de transação implícita para todos os métodos de enterprise bean em execução com demarcação de transação gerenciada por contêiner. Normalmente, você não define o atributo Requerido, a menos que precise substituir outro atributo de transação. Como os atributos da transação são declarativos, você pode alterá-los facilmente mais tarde.
Apresentação
Nossa camada de apresentação é responsável por ... apresentação! Ele é responsável pela interface do usuário e mostra informações ao usuário criando páginas HTML e recebendo entrada do usuário por meio de solicitações GET e POST. Atualmente, estamos usando a antiga combinação Servlet + Java Server Pages ( JSP ).
A camada chama métodos nos gerentes da camada de negócios para executar operações solicitadas pelo usuário e receber informações para mostrar na página da web. Às vezes, as informações recebidas da camada de negócios são de tipos menos complexos como String
's' e ' int
egers ' e , outras vezes, entidades JPA .
Prós e contras da arquitetura
Prós
- Ter tudo relacionado a uma maneira específica de persistência nessa camada significa apenas que podemos mudar do JPA para outra coisa, sem precisar reescrever nada na camada de negócios.
- É fácil trocarmos nossa camada de apresentação por outra e é provável que o façamos se encontrarmos algo melhor.
- É bom deixar o contêiner EJB gerenciar limites de transação.
- O uso do Servlet + JPA é fácil (para começar) e as tecnologias são amplamente usadas e implementadas em muitos servidores.
- O uso do Java EE deve facilitar a criação de um sistema de alta disponibilidade com balanceamento de carga e failover . Ambos sentimos que devemos ter.
Contras
- Usando o JPA, você pode armazenar consultas frequentemente usadas como consultas nomeadas, usando a
@NamedQuery
anotação na classe de entidade JPA. Se você tiver o máximo possível de persistência nas classes de persistência, como em nossa arquitetura, isso espalhará os locais onde você poderá encontrar consultas para incluir também as entidades JPA. Será mais difícil visualizar as operações de persistência e, portanto, mais difíceis de manter. - Temos entidades JPA como parte de nossa camada de persistência. Mas
Account
eShoppingCart
eles não são realmente objetos de negócios? Isso é feito assim que você precisa tocar nessas classes e transformá-las em entidades com as quais a JPA sabe lidar. - As entidades JPA, que também são nossos objetos de negócios, são criadas como objetos de transferência de dados ( DTOs ), também conhecidos como objetos de valor (VOs). Isso resulta em um modelo de domínio anêmico, pois os objetos de negócios não têm lógica própria, exceto métodos de acessador. Toda a lógica é feita por nossos gerentes na camada de negócios, o que resulta em um estilo de programação mais processual. Não é um bom design orientado a objetos, mas talvez isso não seja um problema? (Afinal, a orientação a objetos não é o único paradigma de programação que produziu resultados.)
- O uso de EJB e Java EE apresenta um pouco de complexidade. E não podemos usar o Tomcat puramente (adicionar um micro-contêiner EJB não é puramente o Tomcat).
- Existem muitos problemas com o uso do Servlet + JPA. Use o Google para obter mais informações sobre esses problemas.
- Como as transações são fechadas ao sair da camada de negócios, não podemos carregar nenhuma informação das entidades JPA configuradas para serem carregadas do banco de dados quando necessário (usando
fetch=FetchType.LAZY
) de dentro da camada de apresentação. Isso acionará uma exceção. Antes de retornar uma entidade que contém esses tipos de campos, precisamos chamar os getter relevantes. Outra opção é usar o Java Persistence Query Language ( JPQL ) e executar umFETCH JOIN
. No entanto, essas duas opções são um pouco complicadas.