Blog
JPA-Implementierungsmuster: Service-Fassaden und Datenübertragungsobjekte

In meinem letzten Blog über JPA-Implementierungsmuster habe ich das Thema der DTO- und Service Facade-Muster angesprochen. In diesem Blog werde ich untersuchen, warum wir solche Muster überhaupt brauchen und diese Muster und das DAO-Muster in den breiteren Kontext der JPA-Anwendungsarchitektur stellen. Wenn es etwas gibt, das ich bei der ersten Implementierung von JPA gelernt habe, dann ist es die Tatsache, dass einige der Muster der "alten Schule" für die Architektur von Unternehmensanwendungen immer noch gelten, auch wenn einige Leute sie für nicht mehr notwendig erklärt haben:
- Die DAO wurde für tot erklärt, weil Sie den EntityManager genauso gut direkt aufrufen können. Die Schnittstelle ist gut genug und der Wechsel von JPA zu einer anderen Persistenzimplementierung wird durch die DAO-Abstraktion nicht gerade erleichtert.
- DTOs wurden als überflüssig erachtet, weil Sie Ihre Domänenobjekte auch direkt in der Präsentationsschicht verwenden können. Ermöglicht wird dies durch eine Kombination aus dem offenen EntityManager im View-Pattern, Tag-Bibliotheken zur Anzeige Ihrer Domänenobjekte in JSPs und Datenbindungs-Dienstprogrammen zur Abbildung von HTTP-Anfrageparametern auf Domänenobjekte.
- Und schließlich scheinen auch Service-Fassaden aus der Mode gekommen zu sein. Stattdessen können Sie den Controller die Dienste, die er benötigt, direkt aufrufen lassen oder, noch einfacher, die Geschäftslogik direkt enthalten.
Die resultierende Anwendungsarchitektur sieht in etwa so aus wie dieses Diagramm der Spring Web MVC-Anwendungsarchitektur:
Warum sich also mit DAOs, DTOs und Service-Fassaden herumschlagen?
Ich habe bereits dargelegt, warum das DAO-Muster in der JPA-Anwendungsarchitektur immer noch relevant ist. Ein ähnliches Argument lässt sich auch für die DTO- und Service-Fassaden-Muster anführen. Die oben gezeigte Architektur ohne DTO und ohne Service Facade funktioniert zwar recht gut für einfache Webanwendungen, hat aber zwei große Nachteile:
- Wenn Sie Ihre Anwendung anderen Nicht-HTML-Clients zur Verfügung stellen möchten (denken Sie an Webservices mit SOAP, Flex-Frontends mit AMF oder Ajax-Anwendungen mit JSON), benötigen Sie eine klarer definierte Schnittstelle. Diese Schnittstelle legt fest, welche Dienste dem Client zur Verfügung gestellt werden und welche Typen für die Ein- und Ausgabe verwendet werden. Service-Fassaden bzw. DTOs helfen Ihnen bei der Definition dieser Schnittstelle.
- Wenn Sie Domänenobjekte direkt in der Präsentationsschicht verwenden, müssen diese Domänenobjekte alle ihre Felder als öffentliche Eigenschaften mit Gettern und Settern offenlegen. Wenn die Felder nicht als solche offengelegt werden, können die Tag-Bibliotheken sie nicht darstellen und der Datenbindungscode kann sie nicht setzen. Wie Allen Holub schon früher argumentiert hat, sind Getter und Setter böse. (Übrigens interpretieren einige Leute Holubs Artikel als Vorwand, um alle Felder öffentlich zu machen. Der Artikel besagt, dass öffentliche Getter und Setter nicht besser sind, aber das bedeutet nicht, dass Ihr Code nur öffentliche Felder haben sollte. Stattdessen plädiert der Artikel für die Verwendung des "tell, don't ask"-Ansatzes für OO).
Der Grund, warum ich herausgefunden habe, warum Service-Facaden und DTOs immer noch nützlich sind, ist, dass die Anwendung, die mein Team und ich entwickeln, ein Flex-Frontend und eine Befehlszeilenschnittstelle hat, die über AMF bzw. Hessian mit dem Kern kommunizieren. Der einzige HTML-Code, den unsere Anwendung erzeugt, lädt die SWF-Datei für das Flex-Frontend! Wir begannen ohne DAOs, DTOs und Service-Fassaden, wie man es tun würde, fügten sie aber alle zu unserer Architektur hinzu, damit dies funktioniert. Und als zusätzlichen Bonus haben wir eine gut definierte Schnittstelle zwischen der Dienstebene und der Präsentationsschicht.
Vor- und Nachteile von DTOs
Natürlich wäre es dumm zu sagen, dass Sie in Ihrer Architektur immer DTOs verwenden sollten. Es kommt wie immer darauf an :-) Damit Sie sich selbst eine Meinung bilden können, werde ich eine Reihe von Vor- und Nachteilen der Verwendung von DTOs auflisten:
- Nachteil: DTOs führen zu doppeltem Code. Dies ist vor allem dann der Fall, wenn Ihre DTOs genau dieselben Felder haben wie Ihre Domänenobjekte, und noch mehr, wenn sie beide Getter und Setter für diese Felder haben. Mit DTOs in Ihrer Architektur können Sie jedoch die Getter und Setter in Ihren Domänenobjekten loswerden.
- Nachteil: DTOs erfordern, dass Sie Code schreiben, um Eigenschaften hin und her zu kopieren. Einige Leute haben vorgeschlagen, ein Java Bean-Mapper-Framework wie Dozer, Apache Commons BeanUtils oder die BeanUtils-Klasse des Spring Frameworks zu verwenden, aber dazu müssen Sie Getter und Setter zu Ihren Beans hinzufügen, und das wollten wir einfach nicht mehr!
- Pro/Kontra: DTOs machen es unmöglich, die EntityManger.merge zu verwenden, um ihren Zustand in Ihre persistenten Objekte zu kopieren. Stattdessen müssen Sie das DIY-Merge-Muster anwenden, das ich in meinem Blog über das Speichern von (losgelösten) Entitäten beschrieben habe. Natürlich ist es nicht gerade von Vorteil, wenn Sie gezwungen sind, etwas auf eine bestimmte Art und Weise zu tun, die nicht zu 100% zufriedenstellend ist. Aber zumindest arbeiten das DTO und die DIY-Zusammenführung gut zusammen.
- Pro: DTOs stellen sicher, dass Sie nicht von unerwarteten Problemen mit trägem Laden in Ihrer Präsentationsschicht betroffen sind. Oder im Falle von Remote-Aufrufen schützen sie Sie vor Problemen mit "lazy loading" bei der Serialisierung für den Transport oder, noch seltsamer, auf dem Client.
- Pro: Das DTO-Muster zwingt Sie dazu, über die Schnittstelle Ihrer Anwendung nachzudenken. Sie können Ihre DTOs reichhaltiger gestalten als Ihre einfachen Domänenobjekte, indem Sie ihnen z.B. Sicherheitsinformationen hinzufügen. Oder Sie können Informationen aus mehreren Domänenobjekten in einem DTO zusammenfassen, um die Benutzeroberfläche einfacher zu gestalten.
Bevor wir uns den Service Facades zuwenden, sollten Sie einen Blick auf den Blog von Anirudh Vyas werfen, der sich mit dem häufigen Missbrauch des DTO-Musters beschäftigt.
Vor- und Nachteile von Servicefassaden
Genau wie bei DTOs gibt es Fälle, in denen Service-Facaden sehr sinnvoll sind, und es gibt Fälle, in denen sie nur unnötigen Overhead verursachen. Werfen wir einen Blick auf einige Vor- und Nachteile:
- Contra: Service Facades fügen eine zusätzliche Schicht hinzu, die außer der Delegierung an den eigentlichen Dienst nicht viel tut. Dieses Argument ist vor allem für Java EE-Entwickler interessant, die schlechte Erinnerungen an die EJB 1.0-basierten mehrschichtigen Architekturen haben, die wir zu Beginn dieses Jahrhunderts aufgebaut haben ;-)
- Pro: Service-Facaden können (sollten!) für die Zuordnung von DTOs zu Domänenobjekten und zurück verantwortlich gemacht werden. Service Facades werden mit DTOs als Argumenten aufgerufen, ordnen sie Domänenobjekten zu, rufen den eigentlichen Dienst (oder die Dienste) auf, ordnen das Ergebnis wieder DTOs zu und geben diese DTOs an den Client zurück. Um sicherzustellen, dass Ihre Service-Facaden nur das Prinzip der einzigen Verantwortung einhalten, sollten Sie die Mapping-Logik in separate DTO2DOMapper- und DO2DTOMapper-Klassen auslagern.
- Pro: Service-Fassaden können als Transaktionsgrenze Ihrer Anwendung fungieren, d.h. eine Transaktion wird für die Dauer einer Anfrage an die Service-Fassade gestartet. Anstatt die Transaktionsattribute aller Ihrer Dienste definieren zu müssen, können Sie davon ausgehen, dass alle Dienstaufrufe über die Service-Fassade erfolgen. Die Festlegung Ihrer Service-Fassade als Transaktionsgrenze ist eigentlich obligatorisch, wenn Sie während der Bearbeitung einer Anfrage mehr als einen Dienst aufrufen möchten oder wenn Sie möchten, dass die Service-Fassade träge geladene Domänenobjekte auf DTOs abbildet. In diesem Fall erhalten Sie etwas, das dem
offenen EntityManager im View-Muster ähnelt. Es wird eine Transaktion gestartet, die von dem Moment, in dem die eingehenden DTOs in Domänenobjekte übersetzt werden, bis zu dem Moment dauert, in dem die resultierenden DTOs an den Client zurückgegeben werden. (Wenn Sie übrigens erzwingen möchten, dass alle Dienstaufrufe über die Service-Fassade laufen, können Sie deren Transaktionsattribut aufMANDATORY setzen. Wenn beim Aufruf eines solchen Dienstes nicht bereits eine Transaktion gestartet wurde, wird eine Ausnahme ausgelöst) - Pro: Das Service-Facade-Muster zwingt Sie dazu, über die Schnittstelle Ihrer Anwendung nachzudenken. Anstatt den Client alle Ihre Dienste aufrufen zu lassen, können Sie entscheiden, welche Dienste Sie tatsächlich bereitstellen möchten.
Hmm, mir fallen nicht viele Nachteile des Service Facade-Musters ein ;-) Abgesehen von dem Argument des "Overheads" und das ist eine subjektive Sache. Sie können gerne weitere Nachteile dieses Musters in den Kommentarbereich schreiben!
Auswirkungen auf die Anwendungsarchitektur
Wenn wir die Muster DAO, DTO und Service Facade anwenden, erhalten wir eine JPA-Anwendungsarchitektur, die wie folgt aussieht:
Wenn eine Anfrage gestellt wird, läuft die Abfolge der Ereignisse in etwa so ab:
- Ein Service-Client sendet eine Anfrage an die Service-Fassade. Alle gesendeten Objekte sind DTOs.
- Eine Transaktion wird gestartet.
- Die Service-Fassade ruft den DTO2DOMapper auf, um die eingehenden DTOs auf Domänenobjekte abzubilden. Der DTO2DOMapper kann eine oder mehrere DAOs aufrufen, um Domänenobjekte aus der Datenbank zu laden.
- Die Service-Fassade ruft einen oder mehrere Dienste auf, um die eigentliche Geschäftslogik auszuführen.
- Die Service-Fassade gibt die Rückgabewerte an den DO2DTOMapper weiter und erhält die DTOs zurück. Der DO2DTOMapper kann einen oder mehrere Dienste oder DAOs aufrufen, um die DTOs anzureichern.
- Die Transaktion wird bestätigt. Oder zurückgerollt, falls eine Ausnahme aufgetreten ist.
- Die Service-Fassade übergibt die DTOs an den Client.
- Der Service-Client erhält die DTOs.
Wenn Sie dieses Diagramm nehmen und "DTO2DOMapper" durch "Datenbindung", "DO2DTOMapper" durch "View Rendering" und "Service Facade" durch "Front Controller" ersetzen, erhalten Sie die ursprüngliche Web MVC-Architektur, mit der wir begonnen haben. Der große Unterschied besteht darin, dass die eingehenden "DTOs" Anfrageparameter sind, während die ausgehenden "DTOs" HTML sind:
Damit schließt sich der Kreis in diesem Blog, so dass jetzt ein guter Zeitpunkt ist, um abzuschließen :-) Wie Sie sehen, gibt es keine eindeutige Antwort auf die Frage, ob Sie DTOs und Service-Fassaden verwenden sollten oder nicht. Es hängt zum Beispiel davon ab, was Sie mit Ihrer Anwendung erreichen wollen:
- Handelt es sich um eine reine HTML-Anwendung oder möchten Sie sie über verschiedene Protokolle bereitstellen?
- Wie eng sollen Ihre Kunden und Ihre Dienstleistungen miteinander verbunden sein?
- Möchten Sie, dass Ihre Domänenobjekte keine Getter und Setter haben?
Ich bin sehr daran interessiert zu erfahren, was Sie über die Gültigkeit dieser Muster in der modernen Java EE-Architektur denken. Wann würden Sie sie anwenden? Oder wann nicht und warum nicht? Wir sehen uns im nächsten Blog, in dem ich versuchen werde, das Thema der Vererbung in JPA zu behandeln. P.S. Letzte Woche habe ich den Jungs von info.nl eine Präsentation zu diesem Thema gegeben und sie haben mir ein interessantes Feedback gegeben, das zur Entstehung dieses Blogs beigetragen hat. Vielen Dank dafür, Jungs! Eine Liste aller Blogs zu JPA-Implementierungsmustern finden Sie in der Zusammenfassung der JPA-Implementierungsmuster.
Verfasst von
Vincent Partington
Unsere Ideen
Weitere Blogs
Contact



