comSysto bei XING

14
.
10
.
2016

comSysto auf dem Architecture Gathering 2016

Am 12. - 13. Oktober haben wir das Architecture Gathering in München besucht. Viel Prominenz aus dem deutschsprachigen Raum wie Carola Lilienthal und Eberhard Wolff war mit Talks vertreten. Die interessantesten Beiträge haben wir in einem kurzen Review zusammengefasst.

Christian

Software Engineer | Scrum Master

Otto

Solution Architect

Am 12.-13. Oktober 2016 fand in München zum zweiten Mal das Architecture Gathering, eine kleine deutschsprachige Konferenz, veranstaltet vom iSAQB, statt. Anders als beispielsweise auf der wesentlichen größeren W-JAX gibt es hier kaum klassische Technologie-Talks über die neuesten Hype-Frameworks - es dreht sich alles um die namensgebende Software-Architektur. In über 20 Vorträgen auf zwei Tracks konnten sich unsere 6 Kollegen zwei Tage lang ausgiebig diesem Thema widmen.

Stefan Tilkov bei seiner Keynote zur Eröffnung der Konferenz

Den Anfang machte am Mittwoch Stefan Tilkov mit seiner Keynote “Warum Architekten scheitern”, einer humoristischen Ansammlung von typischen “Architekten-Krankheiten” wie Domänen-Allergie und übertriebenem Hang zur Generalisierung - auch wir sind in diesem Bereich ohne Frage nicht immer ganz gesund… Christian Fischer warf etwas später am Vormittag eine für uns besonders spannende Frage auf: Wie verheiratet man Architektur eigentlich mit moderner und agiler Software-Entwicklung wie wir sie nicht nur bei comSysto leben und lieben? Sein thematischer Rundumschlag wurde am Nachmittag von Peter Götz und Sven Johann in einzelnen Bereichen vertieft. Das große Thema Microservices tauchte natürlich in diversen Sessions auf, beispielsweise um den Übergang vom Monolithen oder die Verbindung mit Domain Driven Design zu diskutieren. Ab und an wurde es dann aber doch etwas technischer, und hier drängt sich natürlich der Netflix-Stack auf. Eher an Einsteiger gerichtet erklärten Benjamin Wilms und Felix Braun wie Resilience mit Hystrix funktioniert. Und trotz der oft zitierten Weisheit “a fool with a tool is still a fool” durfte auch der detaillierte Blick auf Architekten-Werkzeuge zur statischen und dynamischen Analyse von Systemen von Elmar Jürgens nicht fehlen.

Den zweiten Tag eröffnete Christiane Floyd, die erste weibliche Informatik-Professorin in Deutschland. Ihr Vortrag beleuchtete aus einer recht philosophischen Perspektive vor allem die Ursprünge der Software-Architektur und die Beziehungen zu anderen Domänen wie dem klassischen Baugewerbe. Der Aufhänger in Oliver Zeigermanns Talk - der C64 - war ebenfalls eher historisch angehaucht. Inhaltlich war es dann aber eine interessante Abhandlung rund um Architektur im Web-Umfeld mit JavaScript. Auf die andere Seite des Stacks blickte Eberhard Wolff im Anschluss mit dem Thema Daten-Architekturen. Völlig Schicht- und technologie-unabhängig waren dagegen die Ausführungen von Carola Lilienthal, die sich einmal mehr mit dem Zusammenhang von kognitiven Mechanismen und guter Architektur in großen Systemen befasste. Zum Abschluss der Konferenz - quasi als Rausschmeißer - lieferte Michael Stal noch eine leicht autobiografische Reflektion über die Bedeutung von Design Patterns in der Softwareentwicklung.

Wir nehmen wie immer einige neue Impulse in unsere Projekte mit, stellen aber auch fest: viel von dem, was diverse Speaker als State-of-the-Art vorstellen setzen wir schon heute in produktiven Systemen um. Wie das bei comSysto konkret aussieht ist bei Architektur & Entwicklung beschrieben. Wer an diesem Punkt noch nicht genug hat: Einfach weiterlesen, die interessantesten Vorträge haben wir kurz und prägnant zusammengefasst.

Christian Fischer - Agile Software Architecture

Das Agile Manifest lehrt uns: “The best architectures [...] emerge from self-organizing teams.” Bleibt nur die Frage “Wie?”... Vor der eigentlichen Antwort führt Christian Fischer vier zentrale Problemfelder auf:

  1. Nicht-funktionale Anforderungen. Sie sind entscheidend für die Architektur, finden sich aber nur selten im Backlog eines rein fachlich denkenden Product Owners wieder. Die aus “lean” erstellten MVPs entstandenen Applikationen sind häufig technisch unreif.
  2. Entscheidungen. Wenn alle gleich sind und jeder seine eigenen Entscheidungen trifft, ist das gleichbedeutend mit Anarchie. Eine strikte “No Design Upfront”-Regel ist eher kontraproduktiv. Und selbst wenn gute Entscheidungen getroffen werden: Das Team-Gedächtnis ist oft löchrig, wenn es um die Nachvollziehbarkeit früherer Abwägungen geht.
  3. Dokumentation. Oft wird der Code selbst für die beste Dokumentation gehalten. Mindestens verworfene Optionen fehlen hier aber gänzlich. Auch Wiki-Müllhalden sind keine Lösung. Häufig gibt es kein konsistentes Dokumentations-Konzept.
  4. Technische Schulden. Kommen ähnlich wie nicht-funktionale Anforderungen in der Planung meist zu kurz. Insbesondere bei iterativ entwickelter Software wird der Umgang mit Technologien häufig erst während der Entwicklung erlernt; technische Schulden und Folgeaufwände entstehen dabei fast zwingend.

Um diese Probleme zu adressieren schlägt der Speaker ein “Architecture Gardening” als kontinuierliche Arbeit im Arbeitsalltag des Teams vor. Das umfasst konkret:

  1. Rituale. Architektur-Arbeit muss in die üblichen Meetings (z.B. Planning) und Artefakte (z.B. Definition-of-Done) integriert werden. Zum Team passende Aufgaben und Prozessschritte müssen gemeinsam im Rahmen von Retrospektiven erarbeitet werden.
  2. Führung. Gefordert ist hier Servant Leadership, d.h. Weiterbildung und Weitergabe von Wissen (z.B. bei Pair Programming und Code Reviews), Unterstützung von Entwicklern und Product Owner bei technischen Aspekten usw. Eine solche Rolle - ob implizit oder explizit - kann man nicht vergeben, man muss sie sich erarbeiten.
  3. Werkzeuge. Dokumentation sollte zusammen mit dem Code eingecheckt und einfach anpassbar sein. Frühere und anstehende Entscheidungen können in einem Decision Log modelliert werden. Stories zum Abbau von Schulden werden unter Berücksichtigung von Kosten, Nutzen, Risiken usw. betrachtet. Ein Dokumentations-Handbuch beschreibt auf Meta-Ebene, welche Dokumente wo und für wen zu finden sind.

Sven Johann - Managing Technical Debt

Technische Schulden gibt es immer, allein schon aus wirtschaftlichen Gründen. Ein perfektes System zu bauen lohnt sich in der Regel nicht. Schulden sind dabei nicht grundsätzlich schlecht! Wie bei einem Kredit für den Hausbau können sie eine valide Strategie sein - die aber aktiv und bewusst gemanagt werden muss. Der Hauptgrund für die Aufnahme von neuen Schulden ist in der Regel Zeitdruck; fehlendes Wissen oder schlicht Ignoranz können aber ebenfalls eine Rolle spielen. Sven Johann nennt einige Beispiele:

  • Die Hardware, um ein System zu betreiben, kann nur noch gebraucht bei ebay gekauft werden.
  • Das “Domänen-Modell” verteilt sich auf die Klassen Data und AdditionalData mit je 200 Attributen.
  • In der Applikation gibt es viel Code, den nur ein einziger Entwickler versteht - der dann das Team verlässt.

Das sind selbstverständlich Extremfälle, doch weniger stark ausgeprägt trifft man derartige Probleme häufig an. Wichtig ist dabei zu verstehen, dass vor allem strukturelle Schulden ein großes Problem sind: Während gelegentliche Code Smells trivial zu lösen sind kann es bei ausgewachsenen Architektur-Verletzungen oder veralteten Technologien schnell kritisch werden. Viele Teams gehen trotzdem die “Low-Hanging Fruits” mit großem Enthusiasmus an um z.B. die Sonar-Statistik zu verbessern und schieben die echten Probleme lange vor sich her.

Wie geht es besser? Der Abbau technischer Schulden sollte genauso betrieben werden wie die Entwicklung von fachlichen Anforderungen: Anpassungen mit einem guten Kosten-Nutzen-Verhältnis werden eingeplant. Kostentreiber sind dabei sowohl die Änderungshäufigkeit der problematischen Stelle als auch die Kritikalität einer Komponente bei Ausfällen. Größere Verbesserungsideen sollten zunächst mit einem kleinen Proof-of-Concept validiert und erst dann großflächig umgesetzt werden. Essentiell ist bei all diesen Überlegungen stets die Frage: Was soll überhaupt verbessert werden? Die Durchlaufzeit oder der Durchsatz von neuen Features? Die Verständlichkeit des Systems? Die Zahl der Bugs? Die Performance? Es ist unmöglich alle Probleme auf einmal zu lösen - wer ohne Strategie vorgeht schafft höchstens zufällig echten Mehrwert.

Benjamin Wilms / Felix Braun - Resilience mit Hystrix

Das bei Netflix entwickelte und eingesetzte Hystrix ist eine Implementierung des Circuit Breaker Patterns welches verhindert, dass sich einzelne Fehler in verteilten Systemen ausbreiten. Schlägt ein Aufruf fehl kann der Client auf einen vorher definierten Fallback zurückfallen der zumindest eine akzeptable User Experience sicherstellt. Nach einer kurzen Einführung und mit einer kurzen Live-Demo teilen die beiden Speaker einige ihre praktischen Erfahrungen:

  • Hystrix funktioniert ohne viel Einarbeitung sehr einfach.
  • Laufzeitkonfiguration, z.B. mit Archaius , ist sehr hilfreich für das Feintuning der Applikationen.
  • Exceptions werden von Hystrix gewrappt, was zu Aufwänden bei der Integration in bestehendes Exception Handling führen kann.
  • Hystrix Boundaries müssen sauber geschnitten werden, damit Fallbacks sinnvoll definiert werden können. Das hilft implizit auch bei der Erstellung einer guten Architektur.
  • Hystrix Commands sind Schreibarbeit - das ist laut Netflix aber Absicht, damit sich Entwickler bei Remote-Calls ausreichend Gedanken machen. Es gibt allerdings diverse Frameworks zur Vereinfachung, z.B. hystrix-javanica  mit Annotationen.
  • Das Hystrix Dashboard ist sehr hilfreich beim Echtzeitmonitoring. Mit Turbine lassen sich leicht individuelle Dashboard zusammenstellen. Netflix selbst setzt aber inzwischen fortgeschrittenere Tools ein und wartet das Dashboard nicht mehr aktiv.
  • Eine Historisierung der Monitoring-Daten ist essentiell um strukturelle Fehler analysieren und verstehen zu können.
  • Für Integration-Tests ist die Möglichkeit, einen Circuit Breaker auf “Forced Open” zu setzen, sehr hilfreich.

Die meisten dieser Aussagen können wir auf Basis unserer Labs und Projekte bestätigen. Zum Abschluss des Talks werden noch ein paar fortgeschrittene Features wie Request Collapsing und Request Caching angesprochen. Diese kommen aber in der Regel nur in sehr speziellen Use Cases zum Einsatz.

Oliver Zeigermann - JavaScript Architekturen

Programmierung für den C64 zeigt sehr anschaulich, wie man in der Softwareentwicklung mit einfachen Mitteln (C64 Basic) an harte Grenzen stößt und diese sich erst durch Architektur- oder Technologie-Änderungen (Assembler) überwinden lassen. Übertragen auf Web- und JavaScript-Architekturen beschreibt Oliver Zeigermann die folgenden Evolutions-Stufen und Grenzen, denen man früher oder später begegnen wird:

  1. Klassische serverseitige Web-Anwendung ohne AJAX und State im Client, vielleicht mit ein wenig jQuery zur Dynamisierung. Hier kommt man nicht weit, sobald man mehr Dynamik im Client braucht.
  2. jQuery ist eigentlich nur für punktuelle Manipulationen von bestehenden Elementen gut geeignet. Für echte Single-Page-Apps braucht es Template-Mechanismen im Client wie z.B. bei AngularJS.
  3. Schwierig werden kann es aber auch dort mit two-way-data-binding und dem Observer Pattern, welche zu Wildwuchs der Komponenten-Kommunikation führen können. React oder AngularJS 2 gehen hier einen Schritt zurück und setzen auf eine klare Struktur durch Einschränkung der Funktionalität.
  4. Ebenfalls problematisch ist verteilter Mutable State und die Vermischung von UI- und Framework-Logik. Eine mögliche Lösung ist das zentrale Halten des Zustands und die Entfernung von Logik aus den Komponenten selbst (z.B. Flux Pattern).
  5. Auch die Natur von Single-Page-Applikationen kann zu eher fachlichen Problemen führen, z.B. wenn ein Flackern beim Laden zu sehen ist oder die SEO Performance einbricht. Oliver Zeigermann nennt hier den Universal-Application-Ansatz von AngularJS 2. Eine andere Option ist das Prerendering der Seite am Server, das wir in Verbindung mit AngularJS 1 auch in einem comSysto-Projekt mit genau diesen Problemen einsetzen.
  6. REST erzeugt hohen Overhead bei vielen kleinen Requests. Schneidet man die Schnittstellen gröber werden unnötig viele Daten übertragen. Um dieses Problem zu lösen kann z.B. GraphQL eingesetzt werden.

Selbstverständlich wird es weitere Evolutions-Stufen geben, die heute noch nicht abzusehen sind. Viele der genannten Probleme und Lösungen teilen wir zum jetzigen Zeitpunkt aber vollkommen: unser Kollege Kristian Poslek beschreibt in seinem Blog Post zahlreiche ähnliche Konzepte.

Eberhard Wolff - Daten-Architekturen nicht nur für Microservices

Klassische Daten-Architekturen gehen von einer zentralen Datenbank mit vollständiger Konsistenz und hoher Wiederverwendung aus. Beim Einsatz von Microservices wird dieser Ansatz in der Regel recht schnell hinterfragt, da sich massive Deployment-Abhängigkeiten im Fall von Schema-Änderungen ergeben. Auch ein Datenzugriffs-Service, der die Datenbank kapselt, sorgt hier bestenfalls für eine geringfügige Verbesserung. Eine zumindest auf den ersten Blick attraktive Lösung ist das Replizieren von Daten, so dass jeder Service seine eigene Datenbasis hat. Die daraus in der Regel resultierende Redundanz ist dabei aber häufig nur schwer beherrschbar, Inkonsistenzen lassen sich unter Umständen kaum auflösen.

Aber ist das wirklich nötig? Durch die konsequente Anwendung von Domain-Driven Design lässt sich ein Modell pro Kontext statt einem universellen Datenmodell entwerfen. Da ein Microservice typischerweise einen Bounded Context umfasst, betreffen Schemaänderungen fast immer nur einzelne Services und Teams. Doch was ist mit zentralen Daten, die sich überschneiden? Die Abstimmung kann hier auf unterschiedlichen Stufen erfolgen (von geringem zu hohem Koordinationsaufwand):

  • Separate Ways: völlig getrennt
  • Conformist: ein Team entscheidet, das andere zieht Änderungen nach
  • Anti-Corruption Layer: Entkopplung der Schnittstelle, aber ggf. Anpassungen in dieser trennenden Schicht nötig
  • Customer / Supplier: bilaterale Abstimmung über Schnittstellen
  • Shared Kernel: eine gemeinsame Teilmenge der Domänen-Modelle, welche über Microservices hinweg geteilt wird
  • Shared Bounded Context: spätestens hier liegt die Grenze, bei der man Teams nicht mehr schneiden kann

Wie immer liefert Eberhard Wolff eine sehr pragmatische und ausgewogene Betrachtung dieses Themas und fordert Teams und Architekten geradezu heraus, gemeinsam den richtigen Trade-Off für ihr jeweiliges Umfeld zu finden. Auch in unseren Projekten haben wir bereits unzählige oft kontroverse Diskussionen über die richtige Modellierung der Daten geführt - eine Frage die es immer wieder neu zu klären gilt, während sich die Architektur kontinuierlich weiterentwickelt.

Themen:

Kommentare?

comSysto Logo