Februar 2016

Band 31, Nummer 2

Innovation – Nebenprodukte der Architektur von UXDD

Von Dino Esposito | Februar 2016

Dino EspositoJahre vergehen, und ich bin froh, dass ich ein oder zwei effektive Möglichkeiten für das Arbeiten mit Software kenne und empfehlen kann. Lassen Sie sich vom Softwaremarketing nicht irreführen. Software wird ständig verbessert, aber Software ist nicht das Kerngeschäft der meisten Unternehmen da draußen. Das bedeutet, dass für Unternehmen überhaupt nichts dagegen spricht, dieselbe Software viele Jahre zu nutzen. Laut Tiobe.com-Index vom Sommer 2014 war Visual Basic 6 noch immer unter den zehn am meisten verwendeten Programmiersprachen. Anderthalb Jahre später ist es jedoch um einige Plätze nach hinten gerutscht. Dies scheint den Trend zu bestätigen, dass die schweigende Mehrheit der Unternehmen dazu neigt, vollständige Umarbeitungen von Software so weit wie möglich hinauszuzögern. Realistisch gesehen, weist die schweigende Mehrheit der Unternehmen einen Rückstand von mindestens fünf Jahren bei der von ihnen noch immer genutzten Softwaretechnologie auf. Doch wenn es für Unternehmen Zeit wird, diese rückständigen Systeme zu aktualisieren, werden Softwarearchitekten einen Blick auf Software auf dem neuesten Stand der Technik werfen.

Softwarearchitektur heute

Was Softwarearchitektur angeht, schwirren derzeit verschiedene Stichwörter herum. Eines davon ist „CQRS“, ein Akronym für „Command and Query Responsibility Segregation“ (Zuständigkeitstrennung für Befehle und Abfragen), ein anderes ist „Polyglot Persistence“. CQRS habe ich einer Reihe von Artikeln in der Kolumne „Innovation“ vorgestellt. Als Einstieg empfiehlt sich die Lektüre von „CQRS für allgemeine Anwendungen“ (msdn.com/magazine/mt147237) in der MSDN Magazine-Ausgabe vom Juni 2015. CQRS ist ein Leitfaden für Softwarearchitektur mit der einfachen Empfehlung, den Code, der den Zustand des Systems ändert, vom Code zu trennen, der diesen liest und Berichte dazu erstellt. Polyglot Persistence bezieht sich allgemein auf die aufkommende Praxis, mehrere Technologien für beständige Datenspeicherung (Persistenz) im Kontext derselben Datenzugriffsschicht zu nutzen. Der Ausdruck „Polyglot Persistence“ fasst in zwei Worten die komplexen technischen Lösungen zusammen, die von sozialen Netzwerken für den Umgang mit den gewaltigen Datenmengen genutzt werden, die sie tagtäglich verwalten müssen. Das Wesentliche bei Polyglot Persistence ist der Einsatz der Persistenztechnologie, die für die Art und Menge der vorhandenen Daten ideal zu sein scheint.

Während CQRS und Polyglot Persistence erkennbare Vorzüge und potenziell einen Nutzen haben, ist es mitunter schwierig, ihre Relevanz im Kontext einer komplexen Unternehmensanwendung zu erkennen, die aus der Asche einer großen Visual Basic 6-, Windows Forms- oder Web Forms-Codebasis wiederauferstehen soll. Die Relevanz ist jedoch nicht bestreitbar, und sie zu ignorieren, kann für das Projekt und Unternehmen langfristig von Nachteil sein. Zugleich ist Softwarearchitektur kein Glaubensakt. CQRS mag eine tolle Sache sein, doch müssen Sie erst den richtigen Blickwinkel finden, damit es in Ihr Szenario passt.

Wenn Sie allerdings durch die UXDD-Brille einen Blick auf Softwarearchitektur werfen, finden Sie einfach eine passende Lösung für sowohl CQRS als auch Polyglot Persistence.

Aufgabenbasiertes Design

Formen von UXDD-Workflows kommen bereits in Unternehmen sämtlicher Größen zum Einsatz, und ich persönlich habe Varianten davon sowohl in kleinen Teams von bis zu 10 Leuten als auch in Großkonzernen kennengelernt. Wenn dies geschieht, gibt es ein UX-Team, das Bildschirme zusammen mit Endkunden ausarbeitet, die intern oder extern sein können. Das UX-Team produziert einige farbenfrohe Artefakte in Form von PDF- oder HTML-Dateien, die an das Entwicklungsteam übermittelt werden. Das Problem ist, dass das Entwicklungsteam diese Artefakte zumeist solange ignoriert, bis der gesamte Back-End-Stapel entworfen und erstellt wurde. Allzu oft wird das Back-End auf eine Weise entworfen, die die auf der Darstellungsebene tatsächlich ausgeführten Aufgaben nicht oder kaum berücksichtigt. Als Softwarearchitekten kümmern wir uns gewissenhaft um die Geschäftslogikaufgaben und durchdenken Möglichkeiten zur Optimierung und Generalisierung der Implementierung. In der Regel kümmern wir uns nicht um Engpässe und eine suboptimale Datenpräsentation und -navigation. Wenn wir dann später feststellen, dass die Benutzer der tatsächlichen Oberfläche das Arbeiten mit der Anwendung alles andere als ideal finden, ist es meist zu spät, um Änderungen wirtschaftlich zu implementieren (siehe Abbildung 1).

Vergleich des Entwurfs von Softwaresystemen: Von unten nach oben oder von oben nach unten
Abbildung 1: Vergleich des Entwurfs von Softwaresystemen: Von unten nach oben oder von oben nach unten

Machen wir uns nichts vor. Einmal abgesehen von seiner unbestrittenen Effektivität für die Datenspeicherung ist das relationale Modell für Softwarearchitektur irreführend. Oder zumindest begann es, nach seinen ersten beiden Dekaden irreführend zu werden. Bereits Mitte der 1990-er Jahre war die Diskrepanz zwischen relationalen und konzeptuellen Datenmodellen eine Tatsache, die zum Experimentieren mit O/RM-Tools führte. Generationen von Softwarearchitekten (nun Manager) sind mit der Vorstellung aufgewachsen, dass alles, was zählt, ein solides Datenbankfundament ist. Es gibt keinen Zweifel, dass eine solide Datenbank die Grundlage jeder soliden Software ist. Das Problem ist die Komplexität und Menge an Domänen- und Anwendungslogik. Es war keine große Sache, solange es möglich war, Teile dieser Logik in gespeicherten Prozeduren hinzuzufügen. Jenseits dieser Schwelle wandten sich Softwarearchitekten der klassischen Drei-Schichten-Architektur (Präsentations-, Geschäfts- und Datenschicht) und später der DDD-Schichtenarchitektur (Domain-Driven Design) zu: Präsentation, Anwendung, Domäne und Infrastruktur. Unabhängig von der Anzahl der Schichten handelte es sich im Wesentlichen um ein Design von unten nach oben.

Ein solches Design funktioniert einwandfrei, solange mindestens eine der folgenden zwei Bedingungen erfüllt ist:

  1. Die ideale Benutzeroberfläche ist sehr nahe am relationalen Modell.
  2. Ihre Benutzer sind dumm und passiv.

Das Softwaredesign erscheint wie eine exakte Wissenschaft, wenn Sie sich Tutorials ansehen, mit denen eine neue coole Technologie oder ein Framework beworben werden soll. Doch wenn Sie aus Ihrer keimfreien Testumgebung in die reale Welt gelangen, unterliegt das Softwaredesign der Herrschaft von Furcht, Zweifeln und Unsicherheit und wird zum Niemandsland, in dem es nur eine Regel gibt: Das kommt ganz darauf an.

Bei UXDD geht es einfach darum, dass Sie erst dann ernsthaft mit der Entwicklung loslegen, wenn Sie über einen eindeutigen Beleg (sprich: Drahtmodelle) der idealen Architektur der Informationen und idealen Interaktion zwischen Benutzern und System verfügen. Ein Design von oben nach unten, das mit Drahtmodellen der Darstellungsschicht beginnt, gewährleistet, dass Sie dem wirklich nahe kommen, was Benutzer verlangt haben. Das Risiko, den berüchtigten Spruch „Das ist aber nicht, was wir wollten“ zu hören, wird dadurch drastisch verringert. Beachten Sie, dass wenn ein Drahtmodell mit „falsch“ etikettiert wird, dies keine Sache von Vorlieben ist, sondern eher eine geschäftliche Angelegenheit. Wer das Feedback auf Drahtmodelle ignoriert, nimmt bewusst Fehler im resultierenden System in Kauf.

Abbildung 2 fasst die aktuelle Inkongruenz bei der Softwarearchitektur zusammen. Wir haben einerseits Drahtmodelle, die die ideale Darstellungsschicht beschreiben, und wir haben andererseits den Code zum Implementieren der Geschäftslogik, wie sie verstanden wurde. Leider passen die beiden in den meisten Fällen nicht zusammen. Wenn Softwareprojekte selten die Erwartungen erfüllen, insbesondere aus finanzieller Sicht, ist dann nicht vielleicht die Inkongruenz bei der Softwarearchitektur Schuld?

Inkongruenz bei der Softwarearchitektur
Abbildung 2: Inkongruenz bei der Softwarearchitektur

Untersuchen der Inkongruenz bei der Softwarearchitektur

Es stellt sich heraus, dass ein umfassendes Domänenmodell, welches das gesamte Spektrum der Domänenlogik abdeckt, gelinde gesagt überhaupt keinen Sinn ergibt. Die Vorstellung von Domänenmodellen sowie der gesamte DDD-Ansatz kamen auf, um Komplexität im Kern von Software in den Griff zu bekommen, doch das war vor mehr als 10 Jahren. Seitdem hat sich viel verändert. Während einige Kernelemente von DDD, insbesondere die strategische Designkonzepte und Tools, heute nützlicher als je zuvor sind, ist ein objektorientiertes Domänenmodell, das übergreifend alle Lese- und Schreibaspekte deutlich abgegrenzter Kontexte abdeckt, schwierig zu entwickeln und unrealistisch.

Zugleich wird die Vorstellung eines umfassenden Domänenmodells mit einem Von-unten-nach-oben-Prinzip der Software in Einklang gebracht, bei dem Sie verstehen, was Benutzer wünschen, ein Modell ausarbeiten, das Modell programmieren und anschließend mit einer Benutzeroberfläche versehen. So seltsam es auch klingen mag: erfolgreiche Software ist eine ansprechende und effektive Benutzerumgebung, die auf einer Art magischer Blackbox (MBB) aufsetzt.

Wenn Sie eine Entwicklung mit Drahtmodellen starten, wissen Sie genau, was die MBB tatsächlich unterstützen muss. Und Sie wissen auch, dass solange die MBB ihren Job macht, das System ganz nahe daran ist, was Benutzer formell verlangt haben. Es bleibt kaum noch Raum für Annahmen von Entwicklern, und die meisten Iterationen mit Kunden sind in der Anfangsphase des Projekts angesiedelt. Änderungen sind billig, und sobald die UX-Tests bestanden wurden, hat alles, was noch zu tun ist, die Relevanz eines Implementierungsdetails.

UXDD legt den Schwerpunkt auf Aufgaben und Ereignisse. Beim Design von oben nach unten ist der Ausgangspunkt eine Benutzeraktion wie ein Klick oder eine Auswahl. Jede Benutzeraktion ist an einen Einstiegspunkt in die Anwendungsschicht gebunden. In ASP.NET MVC bedeutet dies beispielsweise, dass jede Controllermethode (Darstellungsschicht) an einen Einstiegspunkt in einer Anwendungsschichtklasse gebunden ist (siehe Abbildung 3).

Abbildung 3: CQRS-Controller

public class SomeController
{  
  public ActionResult SomeQueryTask(InputModel input)
  {
    var service = new QueryStack.SomeService();
    var model = service.GetActionViewModel(input);
    return View(model);
  }
  public ActionResult SomeCommandTask(InputModel input)
  {
    var service = new CommandStack.SomeService();
    service.GetActionViewModel(input);
    RedirectToAction(...);
  }
}

Die beiden „SomeService“-Klassen sind der Abschnitt der Anwendungsschicht, der im Teil der Darstellungsschicht sichtbar ist, die von der „SomeController“-Klasse dargestellt wird. Die beiden „SomeService“-Klassen sind ggf. Teil desselben physischen Projekts und derselben Assembly wie die Darstellung oder können unterschiedliche Assemblys und Projekte sein. Angesichts dessen geht die Anwendungsschicht aus Sicht der Architektur Hand in Hand mit der Darstellungsschicht. Dies bedeutet, dass wenn mehrere Darstellungsschichten erforderlich sind ( für Web, mobiles Web und mobile Apps), es akzeptabel ist, über mehrere Anwendungsschichten mit einhergehender eingeschränkter Wiederverwendbarkeit der Logik zu verfügen.

Sie verfügen über eine Einstiegsmethode in die Anwendungsschicht für jede mögliche Aufgabe, die der Benutzer auf der genehmigten Benutzeroberfläche ggf. auslöst. Wenn Sie die erhaltenen Drahtmodelle genau reproduzieren, kennen Sie die Eingabe in und Ausgabe aus jedem Bildschirm ganz genau. Da können Sie nichts falsch machen. Das Ergebnis ist vielleicht ineffizient oder langsam, aber nie falsch.

Die Anwendungsschicht tauscht Datenübermittlungsobjekte mit der Darstellungsschicht aus und orchestriert alle für die Aufgabe erforderlichen Workflows. Dabei muss die Anwendungsschicht üblicherweise Daten aus einem beständigen Speicher lesen und in diesen schreiben, auf externe Webdienste zugreifen und Berechnungen durchführen. Das ist bloß Domänenlogik, und Domänenlogik ist unabhängig vom Anwendungsfall unveränderlich. Ganz gleich, ob die Anforderung von einer Web- oder mobilen Darstellung stammt, wird die Geschäftsdomänenaufgabe bei voller Wiederverwendbarkeit gleich ausgeführt. Kurz gesagt, ist die Vorstellung von Geschäftslogik in zwei spezifischere Segmente unterteilt: Anwendungslogik als erforderliche Logik zum Implementieren bestimmter Darstellungsanwendungsfälle und Domänenlogik, die unabhängig von Darstellung und Anwendungsfällen unveränderlich ist.

Die Domänenlogik kann gemäß einer Vielzahl von Mustern entworfen werden, einschließlich Transaktionsskript und Tabellenmodul. In diesen Fällen sind die Geschäftsregeln ein Fremdkörper, der in die von Ihnen verwendeten Klassen injiziert wird. Geschäftsregeln bilden zumeist eine Rechenfunktion, die Sie aufrufen, um eine Antwort zur Durchführbarkeit einer Aktion zu erhalten. Das Domänenmodellmuster (häufig falsch als die wahre Essenz von DDD interpretiert) ist einfach ein Muster, das das Erstellen von Klassen vorschlägt, die sich wie reale Entitäten verhalten, nachfolgend die Geschäftsregeln integrieren und ihre Anwendung in den von ihnen gebotenen Methoden verbergen. Beim Entwerfen von Domänenlogik gibt es keinen richtigen oder falschen Weg. Es geht allein um Haltung und Effektivität.

Bei einem aufgabenbasiertem Entwurf erkennen Sie unmittelbar, ob die Aktion den Zustand des Systems ändern oder ihn meldet. Bei Verwenden eines CQRS-Entwurfs können Sie logischerweise Befehle von Abfragen trennen. Wenn sich beide in separaten Stapeln befinden, können Sie sie vorteilhafterweise getrennt und ohne Regressionsrisiko schreiben und optimieren, zudem mit potenziell vereinfachter Skalierbarkeit. CQRS passt wie angegossen in einen UXDD-Kontext.

Endlich Persistenz!

Wenn wir von oben nach unten vorgehen, ist Persistenz das letzte unserer Anliegen. Verstehen Sie das nicht falsch: Alle Anwendungen benötigen eine effektive beständige Datenspeicherung und Daten, die effektiv abgefragt werden können. Das letzte Ihrer Anliegen bedeutet nicht, dass Sie die Persistenz vernachlässigen. Es bedeutet nur, dass in einem benutzerorientierten, interaktiven System der Entwurf der Dauerhaftigkeit erst Bedeutung erlangt, nachdem andere Aspekte, insbesondere die Benutzererfahrung (UX) und Domänenlogik, geklärt wurden. Aus diesen Gründen unterliegen Sie keinerlei Einschränkungen hinsichtlich der zu verwendenden Datenbanktechnologie. Darüber hinaus können Sie, wenn Sie ein CQRS-Design haben, mühelos über mehrere Persistenzebenen verfügen, eine für Befehle und eine für Abfragen, die unabhängig voneinander verwaltet werden und sogar (falls hilfreich) auf verschiedenen Speichertechnologien und -paradigmen basieren können. Wenn Sie beispielsweise wissen, dass an einem bestimmten Punkt Analysen der Benutzeroberfläche gemeldet werden müssen, können Sie ein Event-Sourcing-Framework einführen und alle Aktualisierungen und relevanten Aktionen als Ereignis nachverfolgen. Dadurch erhalten Sie ein vollständiges Protokoll aller Vorkommnisse im System, das an einem bestimmten Punkt als Ausgangspunkt einer selbst erstellten Business Intelligence-Analyse dienen kann. Wenn Sie gleichzeitig vordefinierte Daten brauchen, die der Benutzer rasch nutzen können soll, halten Sie den Ereignisspeicher synchron mit einer Projektion der Daten, die Ihre Darstellungsanforderungen erfüllt. Eine Projektion von Daten ist in diesem Kontext der nach einer Liste von Aktionen generierte Zustand. Dies entspricht dem Wunsch, den Kontostand (die Projektion) abzufragen, der aus einer Liste von Transaktionen (Ereignissen) resultiert. Der Abfragestapel besteht oft aus einfachen SQL Server-Sichten, während der Befehlsstapel auf relationalen Tabellen, einem NoSQL- oder Ereignisspeicher (einschließlich beliebiger Kombinationen davon) basieren kann.

Dies wird mitunter auch als Polyglot Persistence bezeichnet.

Zusammenfassung

Ob es Ihnen gefällt oder nicht: Benutzer beurteilen das System hauptsächlich nach dem Bauchgefühl ihrer direkten Erfahrung. Der entscheidende Faktor für Kostensenkungen bei Softwareprojekten ist, dafür zu sorgen, dass Sie von Anfang an genau das erstellen, was Benutzer wünschen. Es geht also nicht nur um den richtigen Weg, sondern den richtigen Weg von Anfang an. Bei UXDD wissen Sie so früh wie möglich, für welche Ausgabe Sie das System entwickeln. Dies vereinfacht Korrekturen, wenn Sie das System bereitstellen und die Benutzer es nicht mögen. Darüber hinaus orientiert sich ein Design von oben nach unten logischerweise an modernen Mustern wie CQRS und Polyglot Persistence, die nicht bloß Schlagworte, sondern bewährte Methoden zum Entwickeln effektiver Software sind.


Dino Esposito ist Mitautor von „Microsoft .NET: Architecting Applications for the Enterprise“ (Microsoft Press, 2014) und „Modern Web Applications“ (Microsoft Press, 2016). Esposito ist Technical Evangelist für die Microsoft .NET Framework- und Android-Plattformen bei JetBrains und spricht häufig auf Branchenveranstaltungen weltweit. Auf software2cents.wordpress.com und auf Twitter (@despos) lässt er uns wissen, welche Softwarevision er verfolgt.

Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: Jon Arne Saeteras