Microservices – Strategie vor Taktik

Keine Kommentare

Dynamisch, unvorhersehbar, schwer planbar, volatil, irrational – das sind nur einige Merkmale der heutigen Geschäftswelt. Und dann gibt es noch Software, die dabei helfen soll, diese Probleme zu vereinfachen. Doch sie bringt noch zusätzliche Effekte mit, wie die rasante Geschwindigkeit neuer technischer Entwicklungen, neue Endgeräte oder eine immer stärkere Vernetzung von Systemen.

Es funktioniert oftmals nicht mehr, ein bestimmtes Ziel mithilfe eines präzisen oder umfassenden Plans und Vorgehens erreichen zu wollen. Viel wichtiger ist die Eigenschaft, sehr schnell auf die sich ebenso sehr schnell ändernden Einflüsse reagieren zu können – die Organisation, die Methodik und die Softwaresysteme müssen wendig und wandelbar sein. Schnelles Feedback ist nötig, um einzelne Subsysteme bei bestehendem Bedarf optimieren oder ersetzen zu können.


“Deshalb sind wir ja agil!”

Eine Änderung einer für das Umfeld geeigneten Methodik kann helfen, doch in der Produktentwicklung führt sie fast zwangsläufig auch zu organisatorischen, architektonischen und technischen Änderungen. Dadurch bedingt ändern sich auch die Strategien der Umsetzung, denn viele bestehende Strukturen sind nicht mehr kompatibel.

Um maximale Anpassbarkeit erreichen zu können, strebt man mit strategischer Softwareentwicklung zu einem Design, das Charakteristiken und Vorteile von jederzeit wandelbaren Systemen zeigt. Ignoriert man dies, befindet man sich oft genug wieder im alten Sumpf der Abhängigkeiten und Seiteneffekte.

Agile Softwareentwicklung fördert die Entwicklung kleinerer Einheiten, weil sie die schnelle und kontinuierliche Auslieferung funktionsfähiger und wertvoller Produkte bevorzugt.
Doch das allein reicht nicht aus. Die Architektur und Implementierung muss diese Methodik auch sinnvoll und effektiv unterstützen. Der Schlüssel dazu ist, die Methodik und entstehende System so „geschmeidig“ wie möglich zu halten.

Strategie ohne Taktik ist der langsamste Weg zum Sieg. Taktik ohne Strategie ist das Geräusch vor der Niederlage. – Sun Tzu

Zu oft wird dabei die Strategie außer Acht gelassen oder vernachlässigt und man fokussiert sich auf die Taktik. Doch „wie“ und „warum“ sind entscheidender als das „Was“. Von daher ist es wichtig, sich frühzeitig über die Strategie und deren mögliche Auswirkungen Gedanken zu machen und diese so schnell wie möglich mit der Taktik zu verifizieren. Eine taktische Maßnahme mag Teilprobleme schnell lösen und ist leicht änderbar, aber die Änderung einer Strategie ist mit hohen Kosten und Schmerzen verbunden.


Am Anfang wissen wir am wenigsten über unser Produkt

In der agilen Softwareentwicklung versucht man die Ungewissheit in der Planung, die Vielzahl an “Anforderungen” und möglichen Lösungen mit anderen Ansätzen zu lösen als im traditionellen Projektmanagement. Beispielsweise ist man bemüht, Anforderungen in Epics und User Storys aufzuteilen, um die Komplexität jeder einzelnen Anforderung zu verkleinern, Prozesse zu verbessern und Abhängigkeiten zu minimieren.

Dazu gibt es auch einige sehr gute begleitende Ansätze, bspw. Impact Mapping, um die wichtigsten oder wertvollsten Anforderungen zu ermitteln und einen Hinweis auf einen möglichen Lösungsweg zu bekommen. Arbeitet man mit mehreren Teams an einem großen System, können Teams zudem nach Fachgebieten und Kontexten, fachlicher und organisatorischer Art, aufgeteilt werden.

Dabei geht man inkrementell und iterativ vor, d. h. man beginnt mit den Anforderungen, die den größten Nutzen (welcher Art auch immer) bringen, und baut sie schrittweise auf. Sollte sich die Richtung als nicht zweckdienlich erweisen, kann man sie aufgrund der geringen Größe leicht korrigieren. Oft entdeckt man auf dieser Tour auch, dass sich manche Annahmen als falsch erwiesen haben und daher bestimmte Anforderungen nicht mehr benötigt werden oder modifiziert werden müssen.

Monolithische Systeme und Planungen besitzen nicht die nötige Anpassungsfähigkeit, um schnelle und kontinuierliche Änderungen durchführen zu können, sie sind oftmals schlicht zu träge. Zumal sie meist auch in so genannten Silos gebaut werden und die organisatorische Struktur eine Anpassung nicht oder nur schwer zulässt.

Daraus lässt sich unschwer erkennen, dass schnell und leicht anpassbare Software nicht mehr mit herkömmlichen, allumfassenden Ansätzen designt werden kann, um diese zwingend notwendige Fähigkeit zu erreichen und mit den sich ständig ändernden Anforderungen mithalten zu können.


Am Anfang wissen wir am wenigsten über unsere Software

Wenn es in der Produktentwicklung vor allem darum geht, schnell auf Änderungen und neue Erkenntnisse reagieren zu können, dann in der Software erst recht. Das gelingt mit kleineren Einheiten wesentlich  einfacher und sicherer als mit einem großen System, das sehr viele Abhängigkeiten besitzt. Zudem können sie auch schneller in Produktion gehen und einen Mehrwert erzeugen.

Eric Evans stellte 2003 in seinem Opus Magnum “Domain-Driven Design [DDD]. Tackling Complexity in the Heart of Software” einen strategischen Ansatz dar, der darauf abzielt, Komplexität in Softwaresystemen besser in den Griff zu bekommen.

Neben den eher taktischen Patterns im ersten sind es vor allem die im zweiten Teil vorgestellten strategischen Ansätze für Architekturen und Systeme, die dabei helfen. Zusätzlich zur wichtigsten Vorbedingung, dass Software die Sprache und Struktur der Domäne spiegeln sollte (Ubiquitous Language), ist hier besonders das Prinzip Bounded Context hervorzuheben.

Analog zur agilen Produktentwicklung versucht man in DDD kleinere Einheiten aus den großen Blöcken herauszuschneiden, die spezialisiert sind und eine Einheit im Sinne der Datenintegrität darstellen, um damit die Komplexität der einzelnen Komponenten zu verringern.

Wie beim ständigen Verfeinern der Anforderungen im Produktmanagement werden Modelle und Kontexte in DDD ständig hinterfragt und verfeinert, umgebaut oder sogar entfernt. Dies ist ebenfalls ein kontinuierlicher Prozess. Evans nennt dies „Model Exploration Whirlpool“:

Model Exploration Whirlpool

Abb. 1: Model Exploration Whirlpool (PDF)

Anpassbarkeit aller Instrumente und Artefakte ist auch hier die Grundbedingung, um eine Produkt- und Softwareentwicklung erfolgreich zu gestalten.

 

Aufteilung und Verkleinerung

Je spezifischer Produkte und Anforderungen definiert werden, desto spezifischer muss die Software dies reflektieren. Agilität mit monolithischen Systemen wird zumindest stark erschwert, in vielen Fällen ad absurdum geführt. Das heißt nicht, dass nun Hunderte von eigenständigen Produkten entwickelt werden müssen. Sie können als Einheit nach außen durchaus monolithisch als ein Produkt erscheinen, doch im Kern bestehen sie aus kleinen, autonomen und anpassbaren Einheiten.

Für die Erstellung und Verwaltung von Anforderungen gibt es Instrumente (z. B. Hierarchical Backlog oder Story Maps), die eine ähnliche Zielsetzung auf fachlicher Ebene besitzen. Diese teilen ein System nach den fachlichen Bereichen auf und innerhalb dieser dann auf die entsprechenden Storys des jeweiligen Bereiches. So gewinnt man nicht nur einen Gesamtüberblick über das Produkt, sondern kann durch die Aufteilung auch einzelne Anforderungen und Fortschritte in Relation zum gesamten Produkt verfolgen. Dies können sehr mächtige Werkzeuge zur interdisziplinären Abstimmung sein, doch bei zu großen Produkten bzw. Kontexten kann es unübersichtlich werden. Von daher profitieren auch diese Werkzeuge von der Verkleinerung der Komponenten.

Story Map

Abb. 2: Beispielhafte Story Map eines E-Commerce Systems

Betrachtet man nun den eigentlichen Code und die technische Implementierung, geht es in den meisten Fällen um Modularisierung, Trennung von Belangen und Minimierung von Abhängigkeiten. Im Zuge von TDD, hoher Automatisierung, Continuous Delivery und heterogenen und verteilten Systemen und Teams ist man dazu fast schon gezwungen.

Doch dieser “Zwang” führt in den meisten Fällen zu langfristig besser wartbarer Software, da die Komponenten kleiner, autonomer und dadurch weniger anfällig sind. Sie können zudem iterativ und inkrementell erstellt werden, ganz im Sinne von Agilität.

Selbstredend hat das auch seinen Preis, wie z. B. ein erhöhter Test-, Monitoring- und Koordinationsaufwand, aber der Nutzen überwiegt diese Kosten in den meisten Systemen bei Weitem. Zumal in der heutigen Zeit Systeme nicht mehr nur auf einem Device oder einer Plattform laufen und häufig verteilt sind oder in der Cloud bereitgestellt werden.

 

Context Is King

Bestimmte Fachkonzepte und -Begriffe ergeben nur in bestimmten Fachgebieten einen Sinn und können in anderen Kontexten eine völlig andere Bedeutung haben. Ohne einen klar abgegrenzten Kontext sind Begriffe und Konzepte eines Geschäftsmodells missverständlich und führen langfristig zu Problemen. Deswegen ist ebenfalls keine vernünftige Modellierung, z. B. eines Domain Models, möglich.

Im Zusammenhang mit den Diskussionen um Modularisierung von Software (z. B. mit Microservices) ist DDD (und vor allem eines seiner zentralen Konzepte Bounded Context) aktueller denn je.

Bounded Context Map

Abb. 3: “Karte” eines E-Commerce-Systems mit Domänen und Kontexten

Innerhalb dieser Domänen des Problemraums existieren ein oder mehrere Kontexte, die ebenfalls klar abgegrenzt sind und ein Domain Model kapseln – hier allerdings im Lösungsraum, d.h. vor allem der technischen Implementierung (hin und wieder auch der organisatorischen Umsetzung). Das Zusammenspiel und die Abhängigkeiten der Komponenten werden in einer Context Map visualisiert. Hierbei werden nicht nur technische, sondern auch organisatorische Abhängigkeiten transparent gemacht.

Context Map

Abb. 4: Context Map eines E-Commerce-Systems mit den Beziehungen zwischen Kontexten und möglichen Schnittstellen

Das Prinzip des Bounded Context ist sehr geeignet, um die Korrelation eines Services und seiner Kontextgrenzen darzustellen. Eine autonome Komponente (bzw. ein Microservice) ist dann die konkrete Implementierung eines Bounded Context.

Arbeitet nur ein Team an allen Kontexten, ist dies wenig problematisch. Sobald allerdings die Kontexte auf einzelne Teams verteilt werden, kommt man nicht umhin, die Beziehungen und Abhängigkeiten untereinander möglichst frühzeitig klar zu definieren.
Wichtig dabei ist: Die Context Map bildet in erster Linie die organisatorischen Abhängigkeiten ab. Denn die entscheidet, wie die Arbeit an einzelnen Kontexten strukturiert sein muss, damit es langfristig nicht zu Problemen kommt. Zusätzlich kann sie visualisieren, wie die Art der Zusammenarbeit geregelt ist und welche (technischen) Schnittstellen dafür bereit gestellt werden, wenn nötig.

Zwei Beispiele für Beziehungen und Abhängigkeiten:

BeziehungAbhängigkeiten (organisatorisch)
RelationshipBeide Teams arbeiten eng zusammen und stimmen Änderungen und deren Auswirkungen stets gemeinsam ab.
ConformistDer Kontext im Downstream ist passiv und muss auf Änderungen des Modells im Upstream-Kontext reagieren. Das kann z. B. auch bedeuten, dass der Downstream-Kontext auch Daten empfängt, die er nicht benötigt. Eine Steigerung davon ist, wenn der Kontext im Upstream oft seine Schnittstelen oder Strukturen ändert. Dann benötigt man in der Regel einen Layer, der dafür sorgt, dass der eigene (Downstream-)Kontext dadurch nicht kompromittiert wird. Oft wird das mit einem sog. Anti-corruption Layer (ACL) gelöst.

Prominentes Beispiel für Conformist aus vielen IT-Abteilungen sind DBAdmins alter Schule, die das Schema einer Datenbank ohne Vorwarnung ändern können. Oder Fremdsysteme, aus denen man Daten abgreift, bei denen man aber nicht mal weiß, wer dafür zuständig ist und – selbst wenn man es wüsste – keinerlei Einfluss darauf hat. Beispiele für Abteilungen, die ohne Absprache Schnittstellen und Systeme ändern, gibt es in der Praxis leider zuhauf.

Diese Beispiele zeigen, wie wichtig es ist, sich auf strategischer Ebene über die Beziehungen von Kontexten im Klaren zu sein. Ansonsten zahlt man einen hohen Preis.
Dies gilt umso mehr in Systemen, die relativ autonom sind und von verschiedenen Teams gebaut werden.
In einem Folgeartikel werde ich detaillierter auf den Nutzen einer Context Map eingehen.

 

Skalierung – in alle Richtungen

Ein in den letzten Jahren häufig diskutiertes Thema ist die Skalierung agiler Methoden und insbesondere Scrum, vor allem in größeren Unternehmen. Es gibt eine Reihe von mehr oder weniger bekannten und auch erfolgreichen Lösungsansätzen.

Conway’s Law besagt, dass ein Softwaresystem die Kommunikationsstruktur seiner Organisation kopiert. Das führt bei vielen Softwaresystemen großer Organisationen zu “interessanten” Strukturen. Die Fortsetzung dieser Aussage, in der Conway vorschlägt, wie man dies vermeiden kann, wird oft nicht mitzitiert: “[…] Therefore, flexibility of organization is important to effective design.

Man kann das Ganze auch umkehren: die Organisation passt sich einem flexiblen, modularen und aus vielen kleinen, autonomen und spezialisierten Komponenten bestehenden System an. Die Beziehungen untereinander, technischer und organisatorischer Art, können in einer Art Karte (z. B. einer Context Map) visualisiert und transparent gemacht werden.

Die Karte aller Kontexte und Komponenten und ihrer Beziehungen untereinander ist die Visualisierung der Kommunikation der Organisation.

Von daher ist in vielen Fällen nicht die Skalierung nach oben oder zu mehr die beste Lösung, sondern hin zur Verkleinerung der Anforderungen und Komponenten des Problem- und Lösungsraums. Also der Aufteilung eines nicht in voller Gänze überblickbaren Systems in kleinere und damit besser einschätzbare und beherrschbare Anforderungen, Einheiten, Belange oder Subsysteme. Selbst das bekannte Beispiel von Spotify kann man eher als Aufteilung in kleinere, spezialisierte Einheiten denn als Skalierung nach oben sehen.

Es ist effizienter, eine Komponente genau einem Team zuzuordnen, statt eine große Anwendung auf mehrere Teams aufzuteilen. Allein der Aufwand der Kommunikation und der Abstimmung bezüglich der Anforderungen und Abhängigkeiten ist enorm im Gegensatz zu den wesentlich unabhängigeren, kleineren Komponenten. Natürlich kann ein Team für mehr als eine Komponente verantwortlich sein, aber eine Komponente sollte nur von einem Team gebaut (und betrieben) werden.

 

Also Microservices?

Modularität von Softwaresystemen ist nicht revolutionär neu – viele Konzepte von Microservices und anderen Ansätzen, die die Entwicklung (in jede Richtung!) skalierbarer Software fördern, sind schon sehr alt. Das macht nichts, es ist trotz des Wirbels um den Begriff eine “natürliche” und fast logische Evolution der agilen Produkt- und der strategischen Softwareentwicklung.
Vielleicht unter neuem Namen, aber mit den gleichen Prinzipien.

Warum überhaupt ein neuer Name her musste und es nicht einfach (Autonomous) Component heißt, ist fragwürdig. Schon 1996 definierte die European Conference on Object-Oriented Programming (ECOOP) eine Komponente als: „A software component is a unit of composition with contractually specified interfaces and explicit context dependencies only. A software component can be deployed independently and is subject to composition by third parties.“
Das passt ziemlich genau auf die gängigen Definitionen von Microservices …

Zumindest ist es sehr empfehlenswert für Systeme, die häufigen Änderungen ausgesetzt sind, also maximale Anpassbarkeit in jegliche Richtung haben müssen. Systemen, die sich nur langsam oder selten ändern, schadet es in den meisten Fällen dennoch nicht, im Gegenteil.

Vor allem besitzen wir heutzutage endlich viele neue technische Möglichkeiten, die das Ganze stark vereinfachen oder vieles erst möglich machen, z. B. sind wir in der Lage, unveränderliche Infrastruktur einzusetzen, Server und Plattformen nach Bedarf zu skalieren und ein- und ausschalten zu können, ja ganze Schichten von Technologien in kürzester Zeit zu ersetzen.

Micro-, Nano-, Pico- oder Macroservices?
Die zahlreichen Diskussionen um den Namen und vor allem die ideale Größe von Microservices (ich benutze hier den gebräuchlichsten Namen) sind Zeitverschwendung. Die Größe des Kontexts (bzw. Bounded Context) gibt die Größe eines Microservices vor, und die kann sich auch ändern (ein anderer interessanter und lesenswerter Vorschlag ist: Es muss in deinen Kopf passen…). Dabei sollte möglichst die Regel 1 Bounded Context = 1 Team = mindestens 1 Model/Aggregat gelten. Ein empfehlenswerter Ansatz ist, dass man (zu) groß anfängt und dann weiter aufteilt und verkleinert.

Dass dieser Ansatz fast zwangsläufig zu einer Änderung der Organisation führt, liegt nahe. Dass er eben aus diesem Grund häufig nicht oder nur bis zu einem bestimmten Punkt gegangen wird, leider auch.

Zu guter Letzt noch ein Vergleich der Charakteristiken von Microservices mit Konzepten und Ideen der agilen Produktentwicklung (angereichert mit Konzepten aus DDD und anderen Bereichen). Es soll beispielhaft darstellen, welche Konzepte korrelieren und/oder hilfreich für die Umsetzung sein können und ist keinesfalls von wissenschaftlicher Präzision, sondern ein reiner Ideenpool zur weiteren Beschäftigung mit diesem Thema. Hierbei liegen die Kriterien aus dem bekannten Artikel des Chefwissenschaftlers von Thoughtworks, Martin Fowler, über Microservices als Grundlage vor:

MicroservicesAgile Produktentwicklung (+ DDD et al.)
Componentization via servicesBounded Context
Autonomous Component
1 Context = 1 Team = 1 Component
Domain Eventing
Organized around business capabilitiesDomain Knowledge
Ubiquituous Language
Backlog & Model Grooming
Product Owner / Stakeholder
Story Map / Product Vision Board
Products not projectsProduct Backlog
Product Roadmap
Product Team
Domain Language
Feature Iterations
Smart endpoints and dumb pipesClean Architecture
Ports & Adapters
Context Map
(RESTful) APIs
Decentralized governanceContext Mapping
Use Cases + Coordination Context
1 Transaction = 1 Aggregate
Cloud, Docker, Virtualization
Event-driven architecture, CQRS
Infrastructure automationSeparate deployment
Continuous Delivery
DevOps
“You build it, you run it”
Phoenix Server & Immutable Environment
Design for failure
Evolutionary design
Adaptability
Fail fast
Inspect & Adapt
Chaos Monkeys
Adaptable (Clean, Hexagonal) Architecture
“Fake it ‚til you make it”

 

Nino Martincevic

Nino Martincevic ist Agiler Consultant & Evangelist bei codecentric. Er treibt alle Themen der ganzheitlichen agilen SW-Entwicklung an und forscht nach neuen Wegen, um die Kluft zwischen Anforderung und Umsetzung zu verkleinern.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Artikel von Nino Martincevic

Agile Management

legacy.org => agile.org (Teil 3)

Agile Management

legacy.org => agile.org (Teil 2)

Weitere Inhalte zu Agile Management

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.