Effiziente Entwicklung von Web-Benutzeroberflächen für Geschäftsanwendungen
Native Oberflächen sind Legacy!
Web-Oberflächen werden mittlerweile auch in Anwendungsbereichen erwartet, in denen es um die interne Nutzung eines Systems geht: Sachbearbeiter, Werker, Power-User. Die Vorteile für den Nutzer liegen auf der Hand: kein Installationsaufwand, prinzipielle Verfügbarkeit auf unterschiedlichen Devices, Integrierbarkeit in Portale, Standardisierung (Kommunikation, Security) im Frontendbereich. Eine nicht komplett web-basierte Anwendung mag so toll sein, wie sie will – sie hat einen gewaltigen Klotz am Bein, wenn es um ihren Erfolg in der unmittelbaren Zukunft geht.
Eine Menge von Softwarehäusern, die z. B. in den 90er Jahren entstanden sind, stehen vor einem großen Umstellungsdruck. Natürlich haben diese Softwarehäuser auch Erfahrungen mit der Web-Welt gesammelt – und dort Web-Erweiterungen an ihr Produkt "angeklebt", wo es die Szenarien erforderten. Aber die in nativen Technologien (C++, Java, C#, Qt) entwickelten Kerndialoge zur internen Nutzung blieben sehr oft noch unangetastet, wohl wissend, dass deren Umstellung eine andere Hausnummer sein würde...
Geschäftsanwendungen haben so ihre Eigenschaften
Geschäftsanwendungen sind in der Regel recht umfangreich: die Anzahl der Dialoge ist hoch, typischerweise im dreistelligen Bereich. Viele dieser Dialoge sind klassische, formularartige Masken mit dem Fokus auf konsistenter Dateneingabe. Hinzu kommen Listen, Auswertungen, Diagramme – aber auch eine Vielzahl spezieller und anspruchsvoller Dialoge: Plantafeln, Leitstände, grafische Überwachungen und und und.
- Eine Vielzahl der Dialoge folgt nicht dem Motto "Simplicity first!", sondern "Efficiency first!". Der Benutzer möchte alle Daten sehen, die ihm bei der Durchführung seiner Arbeit helfen – die Anzahl der Informationen in vielen Dialogen ist deswegen teilweise sehr hoch.
- Geschäftsanwendungen haben einen Lebenszyklus, der in der Regel deutlich über 10 Jahre hinaus geht. Ein Anwendungssystem – beispielsweise eine Fertigungssteuerung – wird beim Kunden nicht alle 5 Jahre ausgetauscht, sondern muss über wesentliche längere Zeiträume hinweg weiterentwickelt und gewartet werden.
- Die Entwicklungsteams hinter Geschäftsanwendungen haben in der Regel eine überschaubare Größe. Oft sieht man Teams von 10-15 Entwicklern, die seit den 90ern an einer Anwendung arbeiten und die Dank einer recht homogenen Technologie (z. B. "alles in Java") heute eine äußerst umfangreiche Anwendung betreiben.
Die Entwicklung eines Web-Frontends ist somit ein gewaltiges Vorhaben – im Falle einer bereits vorhandenen Anwendung kommt es einer Re-Implementierung gleich. Entsprechend hoch sind die Ansprüche an Effizienz und langfristige Investitionssicherheit.
Mainstream: client-zentrische Entwicklung von Web-Anwendungen
Funktionsweise
Die Architektur zu Web-Entwicklung folgt heute dem folgenden Mainstream: Man entwickle mit Hilfe von HTML- und JavaScript-Technologien ein client-seitiges Programm, das dann eigenständig im Browser arbeitet – die sogenannte "Single Page Application". Dieses Programm baut die Oberfläche aus HTML-Elementen für den Benutzer auf, setzt Eigenschaften von HTML-Elementen, reagiert auf deren Ereignisse und Datenänderungen. Es holt sich Daten über Schnittstellen (REST, Web-Service) von seinem Server, überführt diese in die Dialoge und schreibt gegebenenfalls wieder Daten über Schnittstellen zum Server zurück.
HTML/CSS
Die Grundlage von allem ist somit einerseits HTML/CSS. Jeder weiß mittlerweile, dass der direkte Umgang komplex und zudem aus Browser-Kompatibilitätssicht gefährlich ist.
Deswegen gibt es eine Vielzahl von client-seitigen Frameworks, die das Ziel verfolgen, diese Komplexität zu kapseln und die dem Entwickler ein Komponentenmodell anbieten, in dem das HTML-Geschehen mehr oder weniger verborgen bleibt. Bekannte Vertreter dieser Frameworks sind z. B. Angular, ReactJS, Sencha, Vue, Polymer und SAP UI5. Die Frameworks bieten einerseits ein JavaScript-Modularisierungskonzept als Grundlage der Bildung von Komponenten sowie darauf aufbauende Komponentenbibliotheken, in denen eine Vielzahl von Controls zur Verfügung gestellt werden, die weit über das von HTML angebotene Spektrum hinausgehen.
Ein Entwickeln einer client-seitigen Anwendung ohne Zuhilfenahme eines solchen Frameworks macht gar keinen Sinn: die Technologie-Aufwände, die man betreiben muss, um HTML/CSS überhaupt effizient zu entwickeln, sind schlicht zu hoch.
Aber aufgepasst: zum einen ist auch das Entwickeln von Dialogen unter Zuhilfenahme eines Frameworks zunächst komplex. Jedes Framework hat seine eigene Nutzungsart von JavaScript und HTML/CSS, in die man sich erst eingewöhnen muss und die dann mit der Entwicklung von "einfach gelerntem JavaScript" nicht mehr ganz so viel zu tun hat. In allen Frameworks kommt man als Entwickler auch immer wieder direkt mit HTML/CSS in Kontakt, spätestens dann, wenn es um die Entwicklung von komplexen Dialogen geht (z. B. interaktive Plantafel).
Zum anderen kommen alle Frameworks um Kernschwächen von HTML/CSS nicht herum – schließlich basieren sie ja darauf. Beispiel: im Bereich des vertikalen Anordnens hat HTML Schwächen, die darauf beruhen, dass HTML nicht bildschirm-orientiert "denkt", sondern immer in eine endlos lange Seite hineinzeichnet. Es ist z. B. für viele Java-UI-Entwickler erstaunlich, welche Schwierigkeiten es in einer HTML-Welt gibt, einen konstanten Header-Bereich, einen konstanten Footer-Bereich und einen scrollbaren Content-Bereich dazwischen zu definieren.
JavaScript
Die Web-Entwickler lieben JavaScript! So scheint es. Seien Sie gewarnt, bzw. besser: lernen Sie zu differenzieren, was die meisten Leute an JavaScript lieben. Schauen wir also zunächst auf die liebenswerten Aspekte:
- Zunächst einmal gilt: es gibt im Browser nur JavaScript als Programmiersprache und Laufzeitumgebung. Die einzige indirekte Möglichkeit, es zu umgehen, ist die Wahl einer Programmierumgebung, die eine andere, "bessere" Sprache zur Entwicklung anbietet, und die diese Sprache dann nach JavaScript hin transferiert (cross compiling). TypeScript von Microsoft macht das und Google hat das mal innerhalb des Google Web Toolkits (GWT) auf Basis von Java so getan.
- JavaScript läuft "sofort"! Als Entwickler muss man nicht eine Entwicklungsumgebung installieren, man braucht keinen Compile/Build-Prozess. Code speichern und direkt ausprobieren! Insbesondere aus J2EE-Java-Projekten kennt man die Szenarien, in denen kleine Codeänderungen erst nach Ablauf mächtiger Ant-/Maven-/Gradle-Builds und dann einem zeitintensiven Deployment auf dem Server überhaupt testbar sind.
- JavaScript ist schnell geworden. Dank Just-in-time-compilings hat es eine gute Performance. Aussagen, dass JavaSript zu langsam sei, um große, komplexe Aufgaben zu erledigen, gehören definitiv der Vergangenheit an.
Nun kommt das große "Aber...": JavaScript ist eine Skriptsprache mit gravierenden Nachteilen, und die liegen im Typkonzept der Skriptsprache:
- Altbekannt ist die fehlende Typisierung. Eine Variable in JavaScript kann einmal einen String-Wert haben, danach einen Zahlenwert und danach eine Objekt-Instanz beinhalten. Es gibt keinen Compiler, der die Typsicherheit überwacht. Diese fehlende Typisierung ist nur ein äußeres Merkmal des komplett dynamischen Typkonzeptes von JavaScript. Als Entwickler kann man jedem Objekt (naja, eigentlich sind Objekte ja Funktions-Instanzen...) beliebige Attribute "ad hoc" hinzufügen oder entziehen.
- Altbekannt ist des weiteren auch die fehlende sprachliche Möglichkeit der Vererbung. Der JavaScript-Weg heißt hier "Prototyping" – ein Ansatz, der hochflexibel ist, der der Einfachheit von Vererbungskonzepten aber nichts entgegensetzen kann. Über Prototyping können Strukturen aufgebaut werden, die ähnlich einer Vererbung funktionieren, was auch von einigen der aufgezählten Frameworks so gemacht wird. Damit liegt die Vererbung aber nicht in der Sprache, sondern im Framework.
Hier merkt man: JavaScript wurde nicht geschaffen, um Anwendungslogik strukturiert zu formulieren. Stattdessen wurde es geschaffen, um im Browser flexibel und manchmal wild zu scripten. An ein Feld oder auch an alle Felder ein Zusatzattribut und eine Zusatzmethode anzuhängen, das bedeutet in typisierten UI-Umgebungen (z. B. Java-Swing) ein doch recht aufwändiges Ableiten von entsprechenden Klassen. In JavaScript hängt man das neue Attribut und die neue Methode einfach zur Laufzeit an. Fertig!
Die Durchführung großer Entwicklungen mit einem großen Teil auch an anwendungslogischen Bestandteilen auf Basis von JavaScript hat somit eine kritische Komplexität – und erfordert eine außerordentlich hohe Disziplin aller Teilnehmer. Einfache Schreibfehler beim Referenzieren von Variablen können zu Fehlern führen, die erst zur Laufzeit bekannt werden. Das Tooling rund um JavaScript ist zwar besser geworden, kann aber auf Grund der Dynamik der Sprache immer noch einfache Fehlerkonstellationen nicht verhindern.
JavaScript-Entwicklungen beginnen oft im kleinen, überschaubaren Rahmen – mit einem "JavaScript-Crack", der alle Zusammenhänge im Kopf hat. Die Herausforderungen werden ungemein größer wenn die Projekte wachsen – und die Effizienz ungemein schlechter. Große Projekte sind aus meiner persönlichen Sicht über JavaScript nicht befriedigend abbildbar – hier schaffen auf JavaScript aufgesetzte Sprachen wie "TypeScript" wesentlich bessere Voraussetzungen.
Angular, ReactJS, Sencha, Vue, Polymer, SAP UI5...
Bitte erwarten Sie jetzt keine Aussage: dieses Framework ist das beste! Jedes hat seine spezifische Historie und seine Anhängerschaft. Die Frameworks unterscheiden sich zum einen durch die Tiefe, in der sie den Entwickler unterstützen: manche konzentrieren sich auf die "View"-Ebene (z. B. ReactJS) und stellen dem Entwickler frei, wie nun konkret die UI-Komponenten an die Anwendungslogik im Client angeschlossen wird. Andere (z. B. Angular) gehen tiefer und beinhalten dann MVC-Konzepte und weitergehenden Funktionen im Bereich der logischen Verarbeitung.
Die Frameworks unterscheiden sich zum anderen im Umgang mit JavaScript: Angular beispielsweise ist nicht direkt auf JavaScript aufgesetzt, sondern auf dem bereits erwähnten "TypeScript".
Effizienzbetrachtung
Das heutige Mainstream-Entwicklungsmodell der Entwicklung von eigenständigen Clients zusammen mit den gängigen Frameworks bietet enorme Erleichterungen bei der Entwicklung entsprechender Anwendungen – verglichen mit dem nativen Umgang mit HTML/CSS.
Trotzdem ist es ein großes Stück davon entfernt, eine effiziente, einfache Grundlage für komplexe Anwendungs-Clients mit hunderten von Dialogen zu sein. Der von den Anwendungs-UI-Entwicklern geforderte "Skillset" ist umfangreich, die fehlende Compile-Sicherheit im JavaScript-Bereich ist ein deutliches Effizienzmanko. Anwendungsentwicklungen, die beispielsweise von einem Java-Client auf einen Web-Client umsteigen, sind mit dieser Komplexität oft überfordert.
Langzeitbetrachtung – Framework-Volatilität
Schauen wir uns mal einen der großen Framework-Bauer an – Google: Im Jahre 2006 veröffentlichte Google das "Google Web Toolkit (GWT)". Im Jahre 2012 wurde GWT an die Community übergeben, da Google das Framework "Dart" ausrief und sich auf dieses konzentrieren wollte. Von Dart spricht heute kaum noch ein Mensch. Im Jahre 2009 wurde zudem die Linie "AngularJS" ausgerufen. Im Jahre 2016 kam dann "Angular 2" als Nachfolger, allerdings inkompatibel zu "Angular".
Es gibt natürlich noch Maintenance-Releases für die ausgedienten Frameworks. Aber die Problematik ist klar ersichtlich: weiterhin ist der Bereich der Web-Entwicklung einer, in dem permanent neue Framework-Hypes durch die Gassen getrieben werden. Das Framework, auf das ich heute meine Anwendungsentwicklung setze, wird in 5-10 Jahren veraltet sein – und der Hype wird woanders liegen! Aus Sicht der Entwicklung einer umfangreichen Geschäftsanwendung ist das natürlich ein gehöriges Problem – die Entwicklung der Kerndialoge ist einfach zu kostenintensiv, um sie alle Jahre einem Frameworkwechsel zu unterziehen.
Server-zentrische Entwicklung von Web-Anwendungen
Das Gegenstück zur client-zentrischen Entwicklung ist die server-zentrische Entwicklung. Diese ist heute nicht der Mainstream, aber die vergangenen Jahrzehnte haben gezeigt, dass dieser Mainstream einem permanenten Zyklus zu unterliegen scheint: immer, wenn man im Client etwas "machen" kann, dann zieht es alle Entwickler magisch in den Client. Dann sehen plötzlich wiederum alle, dass "vorne" viel zu viel Logik läuft, die viel zu große Mengen an Daten anfordert und man besinnt sich zurück auf den server-zentrischen Ansatz.
Funktionsweise
Im Browser läuft ein JavaScript-Programm, der "Web-Renderer". Dieses Programm ist vom Typus her eine "Single Page Application", hat aber mit "Application" nicht viel zu tun, da es ein technisches, anwendungsunabhängiges Programm ist.
Der Web-Renderer bekommt vom Server die Beschreibung einer darzustellenden Oberfläche geschickt, z. B. als XML-Definition. Er interpretiert diese Beschreibung und rendert entsprechend den Dialog in HTML-Komponenten aus – mit welcher Control-Bibliothek auch immer. Der Web-Renderer wartet nun auf Aktionen des Benutzers innerhalb des aufgebauten Dialoges. Je nach Art der Aktion wird die Aktion im Client gepuffert (z. B. Eingaben in "normalen" Feldern) oder die Aktion führt zum Auslösen einer Kommunikation zum Server. Bei einer Kommunikation werden alle gepufferten Aktionen sowie die auslösende Aktion in einem Schwung zum Server kommuniziert.
Die Serververarbeitung erhält nun also die Aktionen, die der Benutzer in dem Dialog durchgeführt hat und arbeitet diese in die Anwendung ein. Danach schickt sie die Dialogbeschreibung des nun aktualisierten Dialoges zum Web-Renderer zurück. Dies erfolgt im Delta-Verfahren, so dass nicht immer die komplette Dialogbeschreibung gesendet wird, sondern nur Änderungen an dieser. Der Web-Renderer interpretiert nun die aktualisierte Dialogbeschreibung und arbeitet diese in den bestehenden Dialog ein.
Der Web-Renderer ist nichts anderes als ein technisches, generisches Programm, das vom Server kommende Dialoge anzeigt und dann die Eingaben des Benutzers zurück zum Server schickt. Die eigentliche Interaktionsverarbeitung findet somit auf dem Server statt: hier wird definiert, was genau als Dialog zum Client geschickt wird und wie auf Aktionen des Benutzers reagiert wird.
Framework benötigt
Die server-zentrische Entwicklung benötigt ein Framework, das die beschriebene Verarbeitung übernimmt. Es besteht aus:
- Dem in JavaScript entwickelten Web-Renderer und
- einem server-seitigen Gegenstück, in dem die Definition und Verarbeitung der Dialoge in einfacher Weise ermöglicht wird.
Beispiel
Eine Reihe von Softwareentwicklungen haben sich ein solches oder ähnliches Framework selbst geschrieben – aber es gibt auch frei verfügbare Frameworks. Im Java-Lager ist Vaadin hier wohl der bekannteste Name, im deutschsprachigen Raum kennt man zudem den "CaptainCasa Enterprise Client".
Wie sieht eine konkrete Entwicklung einer "Hello World!"-Seite mit einem solchen Framework – hier: "CaptainCasa Enterprise Client" – aus?
Der Entwickler definiert die Seite auf dem Server auf Basis einer Control-Bibliothek – entweder dynamisch oder per XML-Definition:
<t:rowtitlebar text="Some demo..." />
<t:rowbodypane >
<t:row >
<t:foldablepane rowdistance="5" text="Hello world!" width="100%" >
<t:row>
<t:label text="Your Name" width="120" />
<t:field text="#{d.DemoHelloWorld.name}" width="200" />
</t:row>
<t:row>
<t:coldistance id="g_ccpreview_9" width="120" />
<t:button actionListener="#{d.DemoHelloWorld.onHello}" text="Hello!" />
</t:row>
<t:rowdistance height="30" />
<t:row id="g_ccpreview_12" >
<t:label text="Result" width="120" />
<t:field enabled="false" text="#{d.DemoHelloWorld.output}"
width="100%" />
</t:row>
</t:foldablepane>
</t:row>
</t:rowbodypane>
<t:rowstatusbar/>
In dem Layout gibt es sogenannte Expressions, die auf eine server-seitige Java-Klasse zeigen. Felder sind mit Java-Properties verbunden, Buttons mit Java-Methoden:
public class DemoHelloWorld extends DemoBasePageBean
{
String m_name;
String m_output;
public DemoHelloWorld()
{
}
public void setName(String value) { m_name = value; }
public String getName() { return m_name; }
public String getOutput() { return m_output; }
public void onHello(ActionEvent ae)
{
if (m_name == null)
m_output = "No name set.";
else
m_output = "Hello World, "+m_name+"!";
}
}
Die Entwicklung der Web-Oberfläche geschieht somit ohne eine Zeile Programmierung von JavaScript und ohne jegliche Kenntnis von HTML/ CSS/ Web-Frameworks.
Unmittelbare Effizienzvorteile
Auf einige, vor allem Web-Entwickler, wirkt ein solches Framework erst einmal einengend, raubt es ihnen doch die Möglichkeit, selbst im Browser mit HTML und JavaScript zu entwickeln – und das ist ja schließlich etwas "ziemlich cooles"! "Gestandene" Anwendungsentwickler erkennen aber die Effizienzvorteile:
- Die Anwendungsentwicklung selbst hat praktisch nichts mehr mit der Komplexität der Client-Entwicklung zu tun. Sie definiert und programmiert die Dialoge nur noch auf Serverseite. Die Anwendungsentwicklung macht Web-Dialoge, muss aber beispielsweise keinerlei JavaScript beherrschen.
- Interaktionsverarbeitung und logische Verarbeitung laufen in unmittelbarer Nähe auf dem Server – und müssen nicht über externe Schnittstellen miteinander kommunizieren.
- Die Kommunikation zwischen Client und Server erfolgt automatisch mit einer Blockung: eine Benutzeraktion (z. B. Benutzer drückt Button) ist genau mit einem Roundtrip verbunden. Die Lauffähigkeit sowohl über schnelle als auch über langsame Verbindungen ist somit garantiert.
- Der Datenaustausch zwischen Browser und Server ist aus Security-Sicht einfach zu behandeln. Es gibt nur die eine externe Schnittstelle: der Kommunikationskanal zwischen Web-Renderer und Server.
- Das explizite "Skillset" zur Entwicklung von Web-Dialogen ist in der Hand von wenigen Spezialisten und muss nicht auf die gesamte Anwendungsentwicklung ausgebreitet werden.
Langfristige Effizienzvorteile
Die explizite Abkopplung eines Web-Renderes von einem Server über ein explizites Protokoll bringt langfristige Stabilität und Effizienz. So ist es nun möglich, den Web-Renderer gegen einen anderen Renderer auszutauschen, ohne dass die dahinterliegende Anwendung auf dem Server davon etwas merkt.
Was zunächst nach "theoretisch richtig" klingt, geschieht auch in der Praxis. Der bereits erwähnte CaptainCasa Enterprise Client wurde beispielsweise im Jahre 2007 auf dem Renderer-Verfahren aufgebaut – damals allerdings mit einem java-swing-basierten Renderer. Im Jahre 2012 erfolgte dann die Bereitstellung eines JavaFX-Renderers, der auf dem gleichen Protokoll basierte. Dann im Jahre 2015 wurde der Web-Renderer zur Verfügung gestellt. Eine Anwendung, die also im Jahr 2007 entwickelt wurde und damals auf dem Java-Swing-Renderer lief, läuft somit heute ohne Umstellung im Web.
Nachteile der server-zentrischen Dialogentwicklung
Auch die Problemseite eines Server-zentrischen Ansatzes soll natürlich hier erwähnt werden:
- Aus Sicht der Netzwerknutzung kann die Tatsache, dass die Anwendungsinteraktion vom Server gesteuert wird, dazu führen, dass die Anzahl der Roundtrips zwischen Client und Server potenziell höher ist. Es gibt zwar weniger "Heavy-weight-roundtrips" mit großen Datenmengen, aber dafür potenziell mehr "Light-weight-roundtrips" mit geringen Datenmengen.
- Jeder Benutzer belegt zur Laufzeit im Server eine gewisse Menge an Speicher (Session state). Dieses wiederum ist bei der Skalierbarkeit einer Anwendung zu beachten. Eine Skalierbarkeit über tausende von Benutzern ist problemlos, wenn es um das Abwickeln von vielen Millionen von Benutzern geht, ist dies ein K.O. Kriterium. Dann weiß man aber auch, warum man die Effizienzvorteile aufgibt und warum man wirklich direkt im Web-Client entwickeln muss!
Eignung für Geschäftsanwendungen
Der server-zentrische Ansatz ist hervorragend geeignet, um die Kerndialoge (Sachbearbeiter, Werker, Power-User) einer Geschäftsanwendung effizient und über Jahre hin stabil zu entwickeln. Er ermöglicht bestehenden z. B. Java-Entwicklungsteams einen schnellen Sprung in die Web-Welt, ohne mit deren Komplexität direkt konfrontiert zu werden.
Der server-zentrische Ansatz ist ein effizientes Mittel zur Abkopplung der Anwendungsentwicklung von der Framework-Volatilität im Web-Bereich. Nachteile des server-seitigen Ansatzes spielen im Kernbereich von Anwendungen in der Regel keine signifikante Rolle, insbesondere ist eine Skalierbarkeit im Millionen-Nutzer-Bereich hier nicht gegeben.
Fazit
Augen auf beim Entwickeln von Geschäftsanwendungen im Web: es gibt verschiedene Ansätze und es gibt eine gehörige Volatilität von Meinungen und Hypes! Der Gedanke "Alles mit einem Framework" ist im Web ein falscher Ansatz.
- Der Bereich von internen Kerndialogen ist geprägt von Langfristigkeit, Stabilität, Performance, Effizienz und optischer Gleichheit über hunderten von Dialogen. Hierfür eignet sich der server-zentrische Ansatz.
- Der Bereich von externen Dialogen z. B. für Endkonsumenten ist geprägt durch explizites Design, hohe Änderungshäufigkeit, "coole Gadgets" und eine Skalierbarkeit in potenziell sehr hohen Zugriffszahlen. Und hier ist der client-zentrische Ansatz vorteilhaft.