Lassen Sie uns Java-basierte Webanwendungsarchitekturen teilen!
Es gibt viele verschiedene Architekturen für Webanwendungen, die mit Java implementiert werden sollen. Die Antworten auf diese Frage können als Bibliothek verschiedener Webanwendungsdesigns mit ihren Vor- und Nachteilen dienen. Obwohl mir klar ist, dass die Antworten subjektiv sein werden, versuchen wir, so objektiv wie möglich zu sein und die Vor- und Nachteile zu motivieren, die wir auflisten.
Verwenden Sie die von Ihnen bevorzugte Detailebene zur Beschreibung Ihrer Architektur. Damit Ihre Antwort von Wert ist, müssen Sie zumindest die wichtigsten Technologien und Ideen beschreiben, die in der von Ihnen beschriebenen Architektur verwendet werden. Und zu guter Letzt, wann sollten wir Ihre Architektur verwenden?
Ich fange an ...
Überblick über die Architektur
Wir verwenden eine dreistufige Architektur, die auf offenen Standards von Sun wie Java EE, Java Persistence API, Servlet und Java Server Pages basiert.
- Beharrlichkeit
- Unternehmen
- Präsentation
Die möglichen Kommunikationsflüsse zwischen den Schichten werden dargestellt durch:
Persistence <-> Business <-> Presentation
Dies bedeutet beispielsweise, dass die Präsentationsschicht niemals Persistenzoperationen aufruft oder ausführt, sondern immer über die Geschäftsschicht. Diese Architektur soll die Anforderungen einer hochverfügbaren Webanwendung erfüllen.
Beharrlichkeit
Führt CRUD- Persistenzvorgänge zum Erstellen, Lesen, Aktualisieren und Löschen aus . In unserem Fall verwenden wir ( Java Persistence API ) JPA und verwenden derzeit Hibernate als unseren Persistenzanbieter und verwenden dessen EntityManager .
Diese Schicht ist in mehrere Klassen eingeteilt, wobei jede Klasse beschäftigt sich mit einer bestimmten Art von Einheiten (dh Einheiten zu einem Einkaufswagen im Zusammenhang könnten durch eine einzige Persistenz Klasse behandelt werden) und wird verwendet , um ein und nur ein Manager .
Zusätzlich speichert diese Schicht auch JPA - Entitäten , die Dinge sind wie Account
, ShoppingCart
usw.
Unternehmen
Die gesamte Logik, die an die Funktionalität der Webanwendung gebunden ist, befindet sich in dieser Schicht. Diese Funktion kann eine Geldüberweisung für einen Kunden initiieren, der ein Produkt online mit seiner Kreditkarte bezahlen möchte. Es könnte genauso gut sein, einen neuen Benutzer zu erstellen, einen Benutzer zu löschen oder das Ergebnis eines Kampfes in einem webbasierten Spiel zu berechnen.
Diese Ebene ist in mehrere Klassen unterteilt, und jede dieser Klassen wird mit @Stateless
einem SLSB ( Stateless Session Bean ) versehen. Jeder SLSB wird als Manager bezeichnet, und beispielsweise kann ein Manager eine Klasse sein, die wie erwähnt mit Anmerkungen versehen ist AccountManager
.
Wenn AccountManager
CRUD-Operationen ausgeführt werden müssen, werden die entsprechenden Aufrufe an eine Instanz von ausgeführt AccountManagerPersistence
, die eine Klasse in der Persistenzschicht ist. Eine grobe Skizze von zwei Methoden in AccountManager
könnte sein:
...
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
}
Wir verwenden Container-Manager-Transaktionen, damit wir keine eigenen Transaktionsabgrenzungen vornehmen müssen. Was im Grunde genommen unter der Haube passiert, ist, dass wir bei der Eingabe der SLSB-Methode eine Transaktion initiieren und diese unmittelbar vor dem Beenden der Methode festschreiben (oder zurücksetzen). Es ist ein Beispiel für eine Konvention über die Konfiguration, aber wir brauchten bisher nur die Standardeinstellung Erforderlich.
So erklärt das Java EE 5-Lernprogramm von Sun das erforderliche Transaktionsattribut für Enterprise JavaBeans (EJBs):
Wenn der Client innerhalb einer Transaktion ausgeführt wird und die Enterprise-Bean-Methode aufruft, wird die Methode innerhalb der Transaktion des Clients ausgeführt. Wenn der Client keiner Transaktion zugeordnet ist, startet der Container eine neue Transaktion, bevor die Methode ausgeführt wird.
Das Attribut Erforderlich ist das implizite Transaktionsattribut für alle Enterprise-Bean-Methoden, die mit einer vom Container verwalteten Transaktionsabgrenzung ausgeführt werden. Normalerweise legen Sie das Attribut Erforderlich nur fest, wenn Sie ein anderes Transaktionsattribut überschreiben müssen. Da Transaktionsattribute deklarativ sind, können Sie sie später problemlos ändern.
Präsentation
Unsere Präsentationsschicht ist verantwortlich für ... Präsentation! Es ist für die Benutzeroberfläche verantwortlich und zeigt dem Benutzer Informationen an, indem es HTML-Seiten erstellt und Benutzereingaben über GET- und POST-Anforderungen empfängt. Wir verwenden derzeit die alte JSP- Kombination ( Servlet + Java Server Pages ).
Die Schicht ruft Methoden in Managern der Geschäftsschicht auf, um vom Benutzer angeforderte Vorgänge auszuführen und Informationen zu erhalten, die auf der Webseite angezeigt werden sollen. Manchmal handelt es sich bei den von der Geschäftsschicht empfangenen Informationen um weniger komplexe Typen wie String
's und int
egers, manchmal um JPA-Entitäten .
Vor- und Nachteile mit der Architektur
Vorteile
- Wenn wir in dieser Ebene alles haben, was mit einer bestimmten Art der Persistenz zu tun hat, können wir nur von der Verwendung von JPA zu etwas anderem wechseln, ohne etwas in der Business-Ebene neu schreiben zu müssen.
- Es fällt uns leicht, unsere Präsentationsebene in etwas anderes zu tauschen, und es ist wahrscheinlich, dass wir es tun, wenn wir etwas Besseres finden.
- Es ist schön, den EJB-Container Transaktionsgrenzen verwalten zu lassen.
- Die Verwendung von Servlets + JPA ist (zunächst) einfach und die Technologien sind weit verbreitet und auf vielen Servern implementiert.
- Die Verwendung von Java EE soll es uns erleichtern, ein Hochverfügbarkeitssystem mit Lastausgleich und Failover zu erstellen . Beides haben wir das Gefühl, dass wir es haben müssen.
Nachteile
- Mit JPA können Sie häufig verwendete Abfragen als benannte Abfragen speichern, indem Sie die
@NamedQuery
Annotation für die JPA-Entitätsklasse verwenden. Wenn Sie wie in unserer Architektur so viel wie möglich mit der Persistenz in den Persistenzklassen zu tun haben, werden die Stellen verteilt, an denen Sie möglicherweise Abfragen finden, die auch die JPA-Entitäten einschließen. Es wird schwieriger sein, einen Überblick über Persistenzoperationen zu erhalten, und es wird daher schwieriger sein, diese zu warten. - Wir haben JPA-Entitäten als Teil unserer Persistenzschicht. Aber
Account
undShoppingCart
, sind sie nicht wirklich Geschäftsobjekte? Dies geschieht auf diese Weise, da Sie diese Klassen berühren und in Entitäten umwandeln müssen, mit denen JPA umgehen kann. - Die JPA-Entitäten, die auch unsere Geschäftsobjekte sind, werden wie Datenübertragungsobjekte ( DTOs ) erstellt, die auch als Value Objects (VOs) bezeichnet werden. Dies führt zu einem anämischen Domänenmodell, da die Geschäftsobjekte außer Zugriffsmethoden keine eigene Logik haben. Die gesamte Logik wird von unseren Managern in der Geschäftsschicht ausgeführt, was zu einem prozeduraleren Programmierstil führt. Es ist kein gutes objektorientiertes Design, aber vielleicht ist das kein Problem? (Schließlich ist die Objektorientierung nicht das einzige Programmierparadigma, das Ergebnisse geliefert hat.)
- Die Verwendung von EJB und Java EE führt zu einer gewissen Komplexität. Und wir können nicht nur Tomcat verwenden (das Hinzufügen eines EJB-Mikrocontainers ist nicht nur Tomcat).
- Es gibt viele Probleme bei der Verwendung von Servlets + JPA. Verwenden Sie Google, um weitere Informationen zu diesen Problemen zu erhalten.
- Da die Transaktionen beim Verlassen der Business-Schicht geschlossen werden, können keine Informationen von JPA-Entitäten geladen werden, die so konfiguriert sind, dass sie aus der Datenbank geladen werden, wenn sie
fetch=FetchType.LAZY
innerhalb der Präsentationsschicht benötigt (verwendet ) werden. Es wird eine Ausnahme ausgelöst. Bevor wir eine Entität zurückgeben, die diese Art von Feldern enthält, müssen wir sicherstellen, dass wir die entsprechenden Getter aufrufen. Eine andere Möglichkeit besteht darin, Java Persistence Query Language ( JPQL ) zu verwenden und aFETCH JOIN
. Beide Optionen sind jedoch etwas umständlich.