Partageons les architectures d'applications Web basées sur Java!
Il existe de nombreuses architectures différentes pour les applications Web qui doivent être implémentées à l'aide de Java. Les réponses à cette question peuvent servir de bibliothèque de diverses conceptions d'applications Web avec leurs avantages et leurs inconvénients. Bien que je réalise que les réponses seront subjectives, essayons d'être aussi objectifs que possible et motiver les avantages et les inconvénients que nous énumérons.
Utilisez le niveau de détail que vous préférez pour décrire votre architecture. Pour que votre réponse ait une quelconque valeur, vous devrez au moins décrire les principales technologies et idées utilisées dans l'architecture que vous décrivez. Et enfin, quand devons-nous utiliser votre architecture?
Je vais commencer...
Vue d'ensemble de l'architecture
Nous utilisons une architecture à 3 niveaux basée sur des standards ouverts de Sun comme Java EE, Java Persistence API, Servlet et Java Server Pages.
- Persistance
- Affaires
- Présentation
Les flux de communication possibles entre les couches sont représentés par:
Persistence <-> Business <-> Presentation
Ce qui signifie par exemple que la couche de présentation n'appelle ni n'effectue aucune opération de persistance, elle le fait toujours via la couche de gestion. Cette architecture est destinée à répondre aux exigences d'une application Web haute disponibilité.
Persistance
Effectue créer, lire, mettre à jour et de suppression ( CRUD ) la persistance des opérations. Dans notre cas, nous utilisons ( Java Persistence API ) JPA et nous utilisons actuellement Hibernate comme fournisseur de persistance et utilisons son EntityManager .
Cette couche est divisée en plusieurs classes, où chaque classe traite un certain type d'entités (c'est-à-dire que les entités liées à un panier peuvent être gérées par une seule classe de persistance) et est utilisée par un et un seul gestionnaire .
De plus, cette couche stocke également des entités JPA qui sont des choses comme Account
, ShoppingCart
etc.
Affaires
Toute la logique liée à la fonctionnalité de l'application Web se trouve dans cette couche. Cette fonctionnalité pourrait être le lancement d'un transfert d'argent pour un client qui souhaite payer un produit en ligne en utilisant sa carte de crédit. Cela pourrait tout aussi bien être la création d'un nouvel utilisateur, la suppression d'un utilisateur ou le calcul du résultat d'une bataille dans un jeu Web.
Cette couche est divisée en plusieurs classes et chacune de ces classes est annotée @Stateless
pour devenir un Stateless Session Bean (SLSB). Chaque SLSB est appelé un gestionnaire et par exemple un gestionnaire pourrait être une classe annotée comme mentionné appelé AccountManager
.
Lorsqu'il AccountManager
doit effectuer des opérations CRUD, il effectue les appels appropriés à une instance de AccountManagerPersistence
, qui est une classe de la couche de persistance. Une esquisse approximative de deux méthodes AccountManager
pourrait être:
...
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
}
Nous utilisons des transactions de gestionnaire de conteneurs afin de ne pas avoir à faire la démarcation des transactions nous-mêmes. Ce qui se passe essentiellement sous le capot, c'est que nous initions une transaction lors de la saisie de la méthode SLSB et la validons (ou la restituons) immédiatement avant de quitter la méthode. C'est un exemple de convention sur la configuration, mais nous n'avons encore besoin de rien d'autre que de la valeur par défaut, Required.
Voici comment le didacticiel Java EE 5 de Sun explique l' attribut de transaction requis pour Enterprise JavaBeans (EJB):
Si le client s'exécute dans une transaction et appelle la méthode du bean entreprise, la méthode s'exécute dans la transaction du client. Si le client n'est pas associé à une transaction, le conteneur démarre une nouvelle transaction avant d'exécuter la méthode.
L'attribut Required est l'attribut de transaction implicite pour toutes les méthodes de bean entreprise exécutées avec la démarcation de transaction gérée par le conteneur. En général, vous ne définissez pas l'attribut Obligatoire, sauf si vous devez remplacer un autre attribut de transaction. Les attributs de transaction étant déclaratifs, vous pouvez facilement les modifier ultérieurement.
Présentation
Notre couche présentation est en charge de ... la présentation! Il est responsable de l'interface utilisateur et affiche des informations à l'utilisateur en créant des pages HTML et en recevant les entrées de l'utilisateur via des requêtes GET et POST. Nous utilisons actuellement l'ancienne combinaison Servlet + Java Server Pages ( JSP ).
La couche appelle des méthodes dans les gestionnaires de la couche de gestion pour effectuer les opérations demandées par l'utilisateur et pour recevoir des informations à afficher dans la page Web. Parfois, les informations reçues de la couche de gestion sont de types moins complexes que String
les s et int
egers, et parfois les entités JPA .
Avantages et inconvénients de l'architecture
Avantages
- Avoir tout ce qui est lié à une manière spécifique de faire de la persistance dans cette couche signifie seulement que nous pouvons passer de l'utilisation de JPA à autre chose, sans avoir à réécrire quoi que ce soit dans la couche métier.
- Il est facile pour nous d'échanger notre couche de présentation en quelque chose d'autre, et il est probable que nous le ferons si nous trouvons quelque chose de mieux.
- Laisser le conteneur EJB gérer les limites des transactions est bien.
- L'utilisation de Servlet + JPA est facile (pour commencer) et les technologies sont largement utilisées et implémentées dans de nombreux serveurs.
- L'utilisation de Java EE est censée nous faciliter la création d'un système à haute disponibilité avec équilibrage de charge et basculement . Les deux que nous pensons que nous devons avoir.
Les inconvénients
- En utilisant JPA, vous pouvez stocker des requêtes souvent utilisées sous forme de requêtes nommées en utilisant l'
@NamedQuery
annotation sur la classe d'entité JPA. Si vous avez autant que possible lié à la persistance dans les classes de persistance, comme dans notre architecture, cela répartira les emplacements où vous pouvez trouver des requêtes pour inclure également les entités JPA. Il sera plus difficile de visualiser les opérations de persistance et donc plus difficile à maintenir. - Nous avons des entités JPA dans notre couche de persistance. Mais
Account
etShoppingCart
, ne sont-ils pas vraiment des objets métier? Cela se fait de cette façon car vous devez toucher ces classes et les transformer en entités que JPA sait gérer. - Les entités JPA, qui sont également nos objets métier, sont créées comme des objets de transfert de données ( DTO ), également appelés objets de valeur (VO). Il en résulte un modèle de domaine anémique car les objets métier n'ont pas de logique propre à l'exception des méthodes d'accès. Toute la logique est réalisée par nos responsables dans la couche métier, ce qui se traduit par un style de programmation plus procédural. Ce n'est pas une bonne conception orientée objet, mais ce n'est peut-être pas un problème? (Après tout, l'orientation objet n'est pas le seul paradigme de programmation qui a donné des résultats.)
- L'utilisation d'EJB et de Java EE introduit un peu de complexité. Et nous ne pouvons pas utiliser uniquement Tomcat (l'ajout d'un micro-conteneur EJB n'est pas purement Tomcat).
- Il y a beaucoup de problèmes avec l'utilisation de Servlet + JPA. Utilisez Google pour plus d'informations sur ces problèmes.
- Comme les transactions sont fermées à la sortie de la couche de gestion, nous ne pouvons pas charger d'informations à partir d'entités JPA qui sont configurées pour être chargées à partir de la base de données lorsque cela est nécessaire (en utilisant
fetch=FetchType.LAZY
) depuis l'intérieur de la couche de présentation. Cela déclenchera une exception. Avant de renvoyer une entité contenant ces types de champs, nous devons nous assurer d'appeler le getter approprié. Une autre option consiste à utiliser le langage de requête de persistance Java ( JPQL ) et à effectuer unFETCH JOIN
. Cependant, ces deux options sont un peu lourdes.