Kommunikation in den Zeiten von AJAX

Client-Server-Kommunikation mit AJAX
Kommentare

AJAX-Techniken können auf unzählige Art und Weise dazu verwendet werden, herkömmliche, seitenflussorientierte Webanwendungen aufzupolieren. Wesentlich interessanter ist jedoch, dass immer mehr Teile des Web 2.0 zu so genannten SPAs (Single Page Applications) werden, d.h. zu Applikationen, die lediglich das Internet und dessen Protokolle benutzen, um am Ende etwas zu sein, das mehr unserer Vorstellung von Rich Clients als der von hyperverlinkten Webseiten entspricht.

Diese SPA-Sicht der Dinge rückt die Verbindung zwischen Anwendungs- und Präsentationslogik ins Rampenlicht. Denn eine der schwierigsten Entscheidungen, die wir bei der Verteilung von Programmcode zwischen Client und Server treffen müssen, ist: Welchen Code wollen wir nur auf dem Server haben, und welchen Code entwickeln wir für – und deployen ihn auf – den Client? An den beiden äußeren Enden des denkbaren Lösungsspektrums stehen die SOA-Hardliner und die Lobbyisten der Composable Widgets. Die erste Gruppe setzt sich dafür ein, möglichst die ganze Integrationslogik auf dem Client zu haben und lediglich applikationsunabhängige Serviceaufrufe an den Server zu delegieren. Die zweite Gruppe hingegen erledigt sogar das HTML Rendering der Nutzeroberfläche auf dem Server – gewöhnlich begleitet von hinter den Kulissen generiertem JavaScript-Code – und verknüpft die Client Widgets mit ihren Gegenstücken auf dem Server. Typische Vertreter dieser Idee sind AJAX-befähigte JSF-Implementierungen. In der Praxis bringen beide Extreme einige Probleme mit sich:

  • Die Erstellung von „Webbzwonull-Benutzererfahrungen“ mit HTML, CSS, JavaScript und DOM verträgt sich nicht nahtlos mit dem Ansatz, eine Menge grafischer Standardkomponenten – Widgets – zu einer Applikation zusammenzuklicken. Hinzu kommt, dass die enge Kopplung zwischen client- und serverseitigen Komponenten zu einer hochfrequenten und feingranularen Kommunikation führt, die zudem überwiegend synchron ist. Dies bringt uns neue Schwierigkeiten im Netzlatenz-Bereich, der serverseitigen Sessionverwaltung und dem eigtl. asynchronen XMLHttpRequest-Objekt.
  • Wenn man jedoch „nur“ Dienste auf dem Server aufruft und deren Ergebnisse auf dem Client zusammenbaut, dann muss man große Teile des Domänenmodells und der Domänenlogik auf dem Client zur Verfügung stellen. Mit anderen Worten: Ein nichttriviales Mapping zwischen serverseitiger Sprache und JavaScript wird notwendig. Darüber hinaus stellen generische Services meist größere Informationsbrocken zur Verfügung, als für die Darstellung benötigt werden, d.h., der Bandbreitenbedarf wird größer. Auch ist interpretiertes JavaScript – zumindest zum heutigen Zeitpunkt – nicht für größere Berechnungen geeignet und giert nach den knappen Ressourcen des Clients.
Ein alter Hut

Die Schwächen der beiden Extremansätze legen die Suche nach einer Alternative nahe; eine Alternative, die es erlaubt, Präsentationsaspekte im Browser zu haben, die ganze Logik jedoch im Server auszuführen. Erreicht man dies, so gibt es gleich noch einen Bonus: die einfache Testbarkeit der Applikationslogik unabhängig von jeglicher Client-Technologie. Glücklicherweise muss man nicht lange nach einem entsprechenden Entwurfsmuster fahnden, denn schon lange versteckt der gewiefte Entwickler alle domänen- und applikationsspezifischen Berechnungen hinter einer Applikationsfassade (Application Facade). Zwei spezifischen Varianten der Applikationsfassade begegnet man immer wieder: Presentation Model und Model View Presenter (MVP). (Martin Fowler unterscheidet neuerdings zwei Aspekte von MVP und nennt diese Patterns Supervising Controller und Passive View.) Beiden gemeinsam ist ein ausgeprägtes Verlangen nach Entkopplung von Benutzeroberfläche und Domänenlogik. Und beide können – in Theorie und Praxis – für AJAX-basierte SPAs eingesetzt werden.

Presentation Model

Ein kleines Beispiel soll das Grundprinzip verdeutlichen. Stellen wir uns eine Applikation vor, die anhand eines Such-Strings nach Büchern sucht und die Ergebnisliste anzeigt. Ein UML-Diagramm der Applikationsfassade und der Buchklasse sieht wie in Abbildung 1 aus.

Abb. 1: Presentation Model

Die Implementierungsdetails der eigentlichen Suche interessieren den Entwickler der Benutzeroberfläche nicht im Geringsten. Alles, was der grafische Applikationsteil tun muss, ist den Such-String aus einem Eingabefeld zu nehmen, die searchTitles-Methode aufzurufen und schließlich das Resultat (die Liste von Book-Objekten) in gewünschter Form anzuzeigen. Diese Fassadenart nennt sich Presentation Model. Die View, d.h. der für die Präsentation zuständige Teil der Applikation, holt sich alle Daten, die sie benötigt, aus dem Model und schickt alle vom Anwender vorgenommenen Änderungen zurück; beides geschieht über das Fassaden-Interface (BookSearchFacade). Der Programm- und Datenfluss ist dabei sehr offensichtlich – abgesehen von einer kleinen Komplikation: Da die View Änderungen im Zustand des Models nicht mitbekommt, muss er entweder in regelmäßigen Abständen das Model danach fragen (Polling) oder selbst zum ständigen „Beobachter“ werden. So kommt es zum bekannten Observer-Entwurfsmuster (Abb. 2).

Abb. 2: Observer Pattern

Bei einer Zustandsänderung im Model wird die View mittels changeOccurred davon benachrichtigt und kann im Anschluss das Model wiederum nach den Details fragen; meist können sich beliebig viele Beobachter des Models registrieren (registerObserver). Das ist im Grunde alles, was man über das Presentation Model wissen muss – zumindest für den Augenblick.

Model View Presenter

Die andere Fassadenvariante, Model View Presenter, möchte dem GUI (alias Dialog) noch weniger Verantwortung übertragen. Übrig bleibt nur noch die Aufgabe, jegliche Benutzereingaben an die Fassade (alias Presenter) zu signalisieren. Der Presenter ist seinerseits dafür zuständig, die entsprechende Logik an und mit den Domänenobjekten durchzuführen, um anschließend zu entscheiden, wie – und ob überhaupt – die Anzeige (alias View) angepasst werden muss. Bei einer solchen Verteilung der Verantwortung sieht das Klassendiagramm ein wenig anders aus (Abb. 3).

Abb. 3: Model View Presenter

Dieser Weg, View und Applikation voneinander zu entkoppeln, wirkt auf den ersten Blick umständlicher und komplizierter als ein geradliniges Presentation Model. Auf den zweiten Blick ergeben sich jedoch einige Vorteile:

  • MVP stellt die größtmögliche Trennung zwischen jeglicher Logik – auch der Präsentationslogik – und der eigentlichen Dialogimplementierung her.
  • Indem wir die View durch ein Dummy- oder Mock-Objekt ersetzen, können wir Applikationslogik und sogar einen Teil der Präsentationslogik automatisiert – beispielsweise mit JUnit – testen, ohne dabei einen Umweg über das echte User Interface gehen zu müssen.
  • Die View muss weder die Fassade „pollen“ noch muss sie Daten von ihr holen. Alles Nötige wird ihr von außen, vom Presenter, zugeschoben.
  • Die Kommunikation zwischen View und Fassade ist fast vollständig asynchron. Die meisten oder gar alle Methoden an der Presenter-Schnittstelle haben keinen Rückgabewert; und der Presenter verschickt lediglich Nachrichten über gewünschte Anzeigeänderungen an die View.

Die beiden letztgenannten Argumente wiegen im AJAX-Szenario besonders schwer. In verteilten Applikation bedeutet der Verzicht auf Polling das Vermeiden unnötigen Netzwerkverkehrs. Darüber hinaus ist die Kommunikation in AJAX-Applikationen per definitionem asynchron. MVP scheint daher wunderbar geeignet, um die View-Implementierung (BookSearchDialog) im Browser zu betreiben und die Presenter-Implementierung (BookSearcher) dem Server zu überlassen.

Ein einfaches Programmiermodell

Das Leben eines Entwicklers wird jedoch nicht nur von den verwendeten Prinzipien und Entwurfsmustern bestimmt, sondern maßgeblich davon, wie einfach diese Prinzipien vom Programmiermodell seiner Umgebung (Bibliothek, Framework, Programmiersprache, IDE) unterstützt werden. Idealerweise möchte ich zur Realisierung der MVP-Ideen einige Dinge tun und andere nicht:

  • Ich definiere die Presenter-Schnittstelle (z.B. als Java-Interface), implementiere sie auf dem Server und habe auf dem Client ein Proxy-Objekt zur Verfügung, das ich sehr einfach verwenden kann, z.B. searcher.search(‚.*AJAX.*‘);
  • Ich definiere die View-Schnittstelle (z.B. als Java-Interface), implementiere sie für den Client und habe auf dem Server ein Proxy-Objekt zur Verfügung. Auch hier sollte die Verwendung möglichst einfach sein, z.B. searchView.displayList(listOfRetrievedBooks); .
  • Die Übersetzung von Aufrufen an die Fassade in XMLHttpRequest-Objekte sowie die Entgegennahme und Verteilung entsprechender „Rückrufe“ soll vollständig unsichtbar hinter den Kulissen erfolgen. Als Applikationsentwickler möchte ich mich damit nicht herumschlagen.
  • Das unvermeidbare (Un-)Marshalling und (De-)Serialisieren von Nachrichten, Ergebnisobjekten, Exceptions und DTOs soll automatisch geschehen und meine grauen Zellen nicht noch zusätzlich belasten.

Diese Wunschliste lässt sich unglücklicherweise – nach Kenntnisstand des Autors – mit existierenden Frameworks nicht so einfach erfüllen. DWR beispielsweise unterstützt zwar Methodenaufrufe inklusive Serialisierung und Deserialisierung von clientseitigem JavaScript ins serverseitige Java, doch schon der Rückruf zum Client ist ein recht neues Feature, und die MVP-artige Kopplung der Interfaces und Implementierung würde einiges an projektspezifischer Konfiguration und unter Umständen sogar Codierung erfordern. Aus diesem Grunde lag die Entwicklung eines kleinen MVP-spezifischen Frameworks auf der Hand, zunächst lediglich als Technologiestudie, mittlerweile als eigenständige Bibliothek. JayJax heißt dieses tausend und erste AJAX-Rahmenwerk (Kasten). Die Umsetzung des obigen Beispiels mit JayJax ist einfach …

JayJax Bei JayJax handelt es sich um ein kleines AJAX-Framework, das sich sowohl auf Client- als auch auf Serverseite tummelt. Das Projekt lebt auf SourceForge und hat vielfältige Ziele:
  • Unterstütze ein MVP-basiertes Kommunikationsmodell zwischen Web-Client und (Java)-Server auf möglichst einfache Weise.
  • Erlaube die Verwendung von Effekten, fertigen Komponenten und dem ganzen anderen coolen AJAX-Zeugs.
  • Erlaube die testgetriebene Entwicklung von Client- und Servercode.
  • Stütze dich dabei auf die zahlreichen guten, existierenden Frameworks und Bibliotheken.
  • Mache so wenig Einschränkungen wie möglich betreffend der clientseitigen Umsetzung des Views; vom handgeschriebenem JavaScript-Code über die Verwendung einer fertigen Komponentenbibliothek bis hin zum Java-nach-JavaScript-Crosscompiler soll alles mit JayJax kombinierbar sein.
  • Sei kompatibel zu allen (gängigen) Browsern.

JayJax verlangt eine Java 5-kompati-ble Servlet Engine (z.B. Tomcat 5.5). Das aktuelle Release (0.5.1) lässt natürlich noch Wünsche offen. MVP wird bereits gut unterstützt, die (getestete) Browser-Kompatibilität beschränkt sich bislang auf IE und Firefox. Als Basis der Implementierung dienen Prototype und script.aculo.us aber auch Dojo liegt im zukünftigen Fokus des Projekts. Das JayJax-Team, repräsentiert durch den Autor dieses Artikels, freut sich über Unterstützung und Beteiligung jeglicher Art.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -