Der fachliche Schnitt von Microservices: Was ist das Was?
In Teil 2 unserer Artikelserie haben wir die Frage nach der Größe eines Microservice gestellt und dabei u. a. dessen möglichst hohe fachliche Kohäsion betont. Damit stellt sich nun die Frage, was der Microservice konkret fachlich verkörpern soll und kann. Das ist ein schwer zu fassender Aspekt, der eine Reihe von Folgefragen aufwirft.
Greifen wir also verschiedene Leitlinien der Microservice-Veröffentlichungen auf und setzen diese insbesondere in Beziehung zu bewährten Gestaltungskonzepten für IT-Anwendungslandschaften [1].
Anwendung oder Anwendungslandschaft?
Mit Microservices verschwimmen die Grenzen zwischen einer Anwendung und einer Anwendungslandschaft. Jeder Microservice ist eine eigene betriebliche Einheit und damit im traditionellen Betrachtungswinkel eine Anwendung. Die aus diesen Microservices zusammengebauten Shops, Streaming-Portale und anderen Gesamtprodukte sind aus dem Blickwinkel Geschäftsprozess und UX immer noch fachlich motivierte Anwendungen.
Andererseits muss man wohl bei Anwendungen mit über 500 Microservices, die vermutlich mehrere Geschäftsbereiche und Geschäftsabläufe überspannen, von einer Anwendungslandschaft sprechen. Letztlich erheben Microservices eben den Anspruch darauf, eine Lösung für komplexe Aufgaben zu sein.
Wichtig ist, den Begriff Anwendungslandschaft nicht zu groß zu fassen: Insbesondere meinen wir damit die fachlich enger zusammenspielenden Services, die sich in einer Domäne befinden – und eben nicht eine unternehmensweite, "komplette" Service-Landschaft für alle Business-Domänen.
Übersicht und Unabhängigkeit durch starke fachliche Gliederung
Microservices haben sich insbesondere zwei Verbesserungen zur Autonomie-Maximierung auf die Fahne geschrieben: Strukturierung und Dezentralisierung. Sie unterstützen die Strukturierung mit Hilfe der starken fachlichen Bindung (Stichwort: Bounded Context) und die Dezentralisierung mit Hilfe der Ausrichtung auf vollständige Anwendungen (Stichwort: Fachliche Vertikale).
Folgende Aspekte geben Leitlinien für den fachlichen Schnitt von Microservices vor [2]:
- Bounded Context/ Domäne: Jeder Microservice implementiert einen Geschäftskontext mit zugeordneter Verantwortlichkeit – und zwar vollständig. Die Anwendungsarchitektur folgt somit der Geschäftsarchitektur. Das impliziert die lose Kopplung von Microservices. Lose Kopplung dient unter anderem der Minimierung gemeinsamen Wissens und bringt die Unabhängigkeit der Geschäftszwecke zum Ausdruck. Und lose Kopplung impliziert den ausschließlichen Zugriff über Schnittstellen.
- Vertikaler Schnitt: Jeder Microservice umfasst sämtliche benötigten technischen Schichten (Datenhaltung, Geschäftslogik, Prozess, API/Benutzeroberfläche). Da das Team autonom agieren soll, muss es auch alle technischen Kompetenzen zur Umsetzung des Microservice abdecken.
- Keine zyklischen Abhängigkeiten: Es soll keine ungewollten Service-Aggregate geben, um das Deadlock-Risiko zu minimieren und Release-Abhängigkeiten zu vermeiden.
- Shared Nothing: Keine gemeinsamen Daten wie z. B. Datenbank oder Session und auch kein gemeinsamer Code.
Domänenzugehörigkeit, Vertikaler Schnitt und Shared Nothing ergeben zusammen die fachlich eigenständige Anwendung.
Strukturierung durch fachliche Abgrenzung und Bindung (Bounded Context)
Der Begriff des Bounded Context stammt aus Domain-driven Design (DDD) und hilft in der Strukturierung und Abgrenzung fachlicher Strukturen.
In klassischen Anwendungslandschaften bestand die Tendenz, die Unternehmensbegriffswelt für alle einheitlich zu definieren und zu implementieren. In solchen Umgebungen ist alles mit allem verbunden, die IT-Arbeit wird dadurch mühselig und langsam. Eric Evans hat die gescheiterten Versuche zur Etablierung solcher "unternehmensweiten Datenmodelle" vor Augen, und stellt dem das Konzept von kleineren fachlichen Blöcken als Bounded Context entgegen [3]. Um genau zu sein, verwendet er mehrere, zum Teil geschachtelte Bounded Contexts, um eine umfangreiche Domäne fachlich in Subdomänen zu gliedern.
Leitlinien dazu finden sich bereits als Domänen- und Service-Schnitt in der Gestaltung idealer Anwendungslandschaften [1]. Diese Leitlinien sind nicht Microservice-spezifisch. Quasar Enterprise bettet sie zudem in eine Anleitung zur schrittweisen Verfeinerung ein. Wesentlich ist die Ableitung der IT-Landschaft aus den Geschäftszielen. Als treibende Kraft für Dekomposition dienen die Kohäsion einerseits sowie die lose Kopplung andererseits (s. Single Responsibility Principle [4]), um die Kontrolle über Änderungen zu erhalten und zu behalten.
Die Verbindung zur Geschäftssicht (Domänensicht) stellen auch Microservices her, wenn sie auf Domain-driven Design als methodischen Hintergrund verweisen [3]: Das fachliche Modell dient als Ubiquitous Language der Kommunikation zwischen Domänenexperten und Entwicklern. Die Idee des Bounded Context unterteilt das Gesamtmodell in eigenständige Teilmodelle. Manche fachlichen Begriffe kommen in mehreren dieser Teilmodelle vor, besitzen dann aber in der Regel kontextbedingt unterschiedliche Bedeutungen. Jeder Kontext hat die Pflegeverantwortung für sein Teilmodell – und eben nur für seines. Nur wo es notwendig ist, findet ein Austausch mit anderen Kontexten statt. Eine Context Map stellt die (logische) Verbindung zwischen Teilmodellen her.
Abb. 1 zeigt als Beispiel den "Sales Context" sowie den "Support Context" als verschiedene Sichten auf dieselben Fachbegriffe "Customer" und "Product", die jeweils eigene Ausgestaltungen haben [5].
Konkrete Modellierungsbeispiele aus der Praxis sind leider eher rar und allgemein gehalten, aber einiges findet man doch in der Literatur (ihre Darstellungen gleichen der Subdomänenbildung aus [1]):
- Kühne + Nagel nennt die Domänen Airfreight, Seafreight, Overland, Account, Addressbook, Quote List usw. [6]
- Der Otto-Online-Shop ist in elf Domänen unterteilt, darunter Backoffice, Product und Order [7].
- Stefan Toth zeigt für Netflix beispielhaft Services wie Playback, Review, Member und Payment [8].
Dieser fachliche Schnitt ist essentiell für einen erfolgreichen Microservice-Einsatz. Dies betont auch Sam Newman, wenn er die Eignung des Microservice-Ansatzes vom Domänenwissen abhängig macht: "My first piece of advice would be that the less well you understand a domain, the harder it will be for you to find proper bounded contexts for your services." [2]
Monolith First? Microservices First?
Sollte man gleich auf einer grünen Wiese versuchen, Microservices einzuführen? Oder doch erst einen Monolithen definieren, den man später sukzessive aufbricht? Martin Fowler und Stefan Tilkov sind sich hier nicht einig [9].
So schreibt Fowler: "Greenfield development is also quite challenging. It isn’t just that the domain is also likely to be new; it’s that it is much easier to chunk up something you have than something you don’t! So again, consider starting monolithic first and break things out when you’re stable". Bleibt die Frage, wann der richtige Zeitpunkt für das Aufbrechen in eine Microservice-Architektur ist. Tilkov plädiert dagegen dafür, "gleich richtig" mit Microservices anzufangen.
Letztendlich ist wohl eine Einzelfallentscheidung notwendig. Die Wahl beeinflusst signifikant den Entwicklungsaufwand: Wer gleich in der Breite mit Microservices startet, geht das Risiko ein, dass ein Refactoring über Service-Grenzen hinweg massive Aufwände verursacht: "Changes across service boundaries are expensive, so we want to avoid them" [10]. Dagegen sind Refactorings in einer Anwendung, in einem Team, in einem Monolithen mit heutigen Tools und IDEs gut und effizient umsetzbar.
Monolith = unstrukturiert? Microservices = strukturiert?
Mit einem Monolithen assoziiert man oft Spaghetti-Code ohne Struktur, weil die Strukturierung eines Monolithen durch mangelhafte Architekturarbeit (zunehmend) leidet. Das muss aber nicht stimmen, auch Monolithen können sauber strukturiert und aufgebaut sein.
Dagegen erhofft man sich von der Wahl von Microservices eine implizit strukturgebende Kraft, weil diese nur über ihre Schnittstellen interagieren können. Und auch diese Annahme muss nicht automatisch stimmen: Zieht man ihre geringe Größe und ihre potentiell hohe Anzahl in Betracht, besteht die Gefahr von Microservice-Spaghetti [11]. Dies veranschaulicht Abb. 2 mit Abhängigkeiten zwischen Netflix-Services, hier in Form eines "Todessterns" visualisiert [12].
Netflix legt daher Mindestanforderungen aus Nutzersicht an einen Microservice fest [13], identifiziert die dafür notwendigen "Critical Microservices" und stellt per umfänglichen Fault Injection-Tests sicher, dass ihr System wirklich mit dieser Minimalmenge auskommt. Will man diesen enormen Aufwand treiben? Aus Architektursicht sollte folglich eine Abwägung zwischen der inneren und der äußeren Strukturierung erfolgen:
- Welche alternativen Mittel, wie z. B. fachliche vertikale Schnitte, stehen in einem monolithisch geprägten Ansatz zur Verfügung, um ein solches Spaghetti-Design zu vermeiden?
- Wie groß ist die Verbesserung hinsichtlich Übersichtlichkeit und Handhabbarkeit, wenn wir die Knoten eines monolithischen Spaghetti-Designs mittels Microservices etwas vergrößern (mit weniger Spaghetti-Linien zwischen ihnen), dafür aber zusätzliche Probleme, wie z. B. Service-übergreifende Tests in der Integration und im Betrieb, erzeugen?
Dezentralisierung des Kontrollflusses (Vertikale)
In einer Microservice-Architektur stimmen die einzelnen Teams ihre Schnittstellen direkt und dezentral ab. Zwar mit der Gefahr von Microservice-Spaghetti, aber eben ohne schwerfälligen, zentralistischen Engpass.
Der Architekturspicker beschreibt als Faustformel "Microservices = SOA ohne ESB" [14].
- SOA ist hier als Ansatz für eine starke Ausrichtung auf Fachlichkeit und Geschäft zu verstehen,
- ESB meint hier eine technische Komponente für Integration und Geschäftsprozessautomatisierung (und nicht nur als einfache Kommunikationsplattform). Ein solcher ESB nimmt den Services ihre Autonomie, sie sind dann keine vollständigen Anwendungen mehr.
Die direkte Kommunikation zwischen Microservice-Teams vermeidet den zentralistischen ESB mit seiner Steuerung als orchestrierende Komponente [15]. Auch Newman wendet sich gegen "horizontale Services", da Änderungen dann teamübergreifend durch alle Schichten laufen [10].
Flohre stellt exemplarisch ein kontrollorientiertes System einem ereignisorientierten gegenüber und verdeutlicht damit sehr schön die jeweils berechtigten, aber unterschiedlichen Denkweisen eines BPM- und eines Microservices-Systems: "Wer Microservices richtig macht, braucht keine Workflow Engine und kein BPMN" [16].
Wo bleibt die Benutzeroberfläche?
Fachlicher Schnitt und Service-Schnittstellen erscheinen soweit nachvollziehbar. Bleibt die Gretchenfrage: Wie hältst Du’s mit dem User Interface? In der Gestaltung von Anwendungslandschaften mit grobgranularen Komponenten erzeugt die Forderung nach vollständigen Vertikalen (d. h. sie enthalten alle technischen Software-Schichten) keinen Widerspruch [1]. Eine Präsentationsschicht ist in all diesen Komponenten vorhanden – zumindest mehr oder weniger ausgeprägt, bei bestimmter Fachlichkeit ggf. auch gar nicht vorhanden. Dabei laufen häufig die Nutzersicht, in der z. B. ein Warenkorb keine eigenständige Anwendung ist, und der fachlich-architektonische Schnitt deutlich auseinander.
Letztlich bleibt die Frage nach der Integrationsschicht: Wie werden Microservices verbunden und integriert, ggf. insbesondere über die UI? Interessanterweise fällt erst beim zweiten Blick der uneinheitliche Umgang mit Microservices hinsichtlich der Präsentationsschicht auf. Die übergreifenden Gemeinsamkeiten hören an der API-Grenze auf, es bleiben der Domänenfokus und die weitestgehende Unabhängigkeit.
Ein möglichst dünnes verbindendes Element zur Fragment- oder Seitenintegration ist immer erforderlich und oft auch für einheitliches Layout und Styling zuständig. Das Team embarc stellt die Varianten von UI-Integration einander gegenüber (s. Abb. 3) [14]:
An der Präsentationsschicht scheiden sich die Geister: Kühne + Nagel [6], Otto [7] oder Zalando [17] belassen weite Teile der (Web-)Präsentationsschicht im Microservice. Die Integration ist weitestgehend Bestandteil des Microservice, ausgedrückt durch den Begriff des Self-Contained System[18]. Die einfachste Art der Integration ist somit eine Web-UI, deren einzelne Serviceteile der Browser zusammenbringt. Der Browser ist sozusagen die Integrationskomponente, die z. B. Suchdialog, Ergebnissichten, Warenkorb etc. eines Web-Shops zusammenbringt.
Netflix [13] nimmt dagegen die entgegengesetzte Position des Spektrums ein, ersichtlich an ihrem API-Gateway: Dieses API-Gateway trennt die diversen Frontends von den Backend-Services. Full-Clients sind spätestens dann erforderlich, wenn native Apps z. B. für mobile Plattformen eine PlugIn-artige UI-Aggregation im Client faktisch ausschließen. Doch Vorsicht: Sobald gemeinsame Zugriffsmuster oder sogar gemeinsamer Geschäftscode zum Einsatz kommt, droht die "Rückkehr des Monolithen" [14], denn alle Fäden laufen beim und um das API-Gateway herum zusammen (s. Abb. 4 & 5). Das Gateway ist der zentrale Zugriffspunkt, insbesondere für betriebliche Kontrolle wichtig.
Die Gefahr darin besteht nicht nur in "Frontend-Microservices", sondern auch im API-Gateway als umfassender serverseitiger Aggregations- und Verteiler-Schicht. Backend For Frontend (BFF) ist ein Ansatz, dem entgegenzuwirken. Diese BFFs sind also mehrere "zentrale" APIs, eines pro Frontend-Technologie: Zugeordnete Backend-Komponenten bündeln als Fassade die Client-Aufrufe und kapseln das Backend. Newman zeigt einen verbesserten Team-Schnitt des BFF gegenüber dem API-Gateway (Abb. 6 rechts im Vergleich zu links) [19].
Das sieht nach einer Lösung für das Problem "zu viel Konzentration im API-Gateway" aus. Bei genauerem Hinsehen werden die verschiedenen BFFs jedoch viel Redundanz aufweisen. Stefan Tilkov kommt daher zur Einschätzung: "I don’t think, that is something that you want. I think, that is something that happens to you. Because you fix something that is broken in the first place" [20]. Er gibt ein klares Votum für Self-Contained Systems ab und schließt: "Frontend monoliths are just as good, or bad, as backend monoliths".
Können Microservices also das Problem der UI-Integration nicht zufriedenstellend lösen? Könnte es sein, dass Microservices eine Idee von Backend-Architekten sind, die den für Kunden vielleicht wichtigsten Anwendungsteil – die UI mit Oberfläche und die Benutzerführung – aus ihren Bounded Contexts ausgeschlossen haben?
Ein weiterer Blick in Domain-driven Design als unsere methodische Basis zeigt bereits die Problematik [3]. Je ausgefeilter die Oberflächen werden – und wer wollte heutzutage schon auf moderne UIs verzichten? –, desto mehr Geschäftslogik enthalten sie notwendigerweise und desto stärker ziehen sie das Modell aus dem Domain Layer heraus in den gemeinsam genutzten Presentation Layer (s. Abb. 7).
Genau betrachtet erheben Microservices nicht per se den Anspruch, vollständige Vertikale zu sein (das tun nur die Verfechter von Self-Contained Systems). Die Dezentralisierung im Sinne von Prozess- und Datenhoheit verringert sich damit also auf den Aspekt "Datenhoheit". Und auch diese Datenhoheit stößt schnell an ihre Grenzen, wenn die Fachlichkeit einen übergreifenden transaktionalen Konsistenzbegriff erfordert.
Zusammenfassung
Domain-driven Design bietet einen hilfreichen Musterkatalog als methodische Grundlage zur Systemgestaltung [3]. Der DDD-Begriff des Bounded Context definiert eine fachlich selbständige Einheit mit zugehöriger fachlicher Nomenklatur. Der Bounded Context bildet eine Domäne bzw. Subdomäne und folglich auch die natürliche Begrenzung für einen fachlichen Microservice. Aber wie "micro" ist der Service dann noch?
Analog zu den Komponentenkategorien für Anwendungslandschaften (Bestand, Funktion, Prozess, Interaktion [1]) handelt es sich beim Microservice im besten Fall um eine vollständige Vertikale, der Microservice enthält also alle technischen Software-Schichten. In vielen Fällen ist jedoch implizit von API-Services die Rede, also von Services ohne UI, womit sich ein Anteil der Fachlichkeit eben doch in anderen Komponenten befindet. Hier zeigen sich die Grenzen der Fachorientierung von Microservices.
Microservices erheben den Anspruch, die "bessere SOA" zu sein, weil sie eine abgeschlossene Domäne inkl. Steuerung und Datenhoheit implementieren. Aber ist eine übergreifende Prozesssteuerung innerhalb eines oder mehrerer separater UI-Microservices besser als eine in einem ESB?
Dem Microservice-Anspruch werden Self-Contained Systems zwar gerecht, doch diese SCS scheinen wiederum für native Clients ungeeignet.
Zusammengenommen können Microservices beim fachlichen "Was" nicht komplett überzeugen. Es fehlt die nachvollziehbare Argumentation, dass die von kleinen Teams erstellten Anwendungen wirklich komplett eigenständige Einheiten sind und wie ein Microservices-Spaghetti vermeidbar bleibt. Denn umfassende fachliche Abhängigkeiten lassen sich durch architektonische Mittel wie Microservices nicht "weg-abstrahieren und weg-designen", höchstens strukturiert verlagern.
Lesen Sie jetzt Teil 4 der Artikelserie:
- G. Engels, A. Hess, B. Humm, O. Juwig, M. Lohmann, J.-P. Richter, M. Voß, J. Willkomm; 2008: Quasar Enterprise – Anwendungslandschaften serviceorientiert gestalten
- S. Newman; 2015: Building Microservices: Designing Fine-Grained Systems
G. Steinacker (Otto); 2016: Why Microservices? - A Completely Biased Summary of the Assets of Microservice Architectures - E. J. Evans; 2003: Domain-Driven Design: Tackling Complexity in the Heart of Software
- R. Martin; 2014: The Single Responsibility Principle
Informatik Aktuell – A. Krämer: SOLID [2] – Das Single Responsibility Principle - M. Fowler; 2014: Bounded Context
- B. Kimminich (Kühne + Nagel); 2016: From Monolith to Microservices
- G. Steinacker (Otto); 2015: On Monoliths and Microservices
- S. Toth; 2015: Netflix-Architektur Blogserie – Teil 2: Microservices
- M. Fowler; 2015: MonolithFirst
S. Tilkov; 2015: Don’t start with a monolith - S. Newman; 2015: Principles Of Microservices (Refactoring: 09:40; horizontale Services: 08:50)
- M. Fowler; 2015: Microservice Trade-Offs
- A. Cockcroft (Netflix); 2014: Migrating to Microservices (GOTO14)
- J. Evans (Netflix); 2016: Mastering Chaos - A Netflix Guide to Microservices (QCon 2016) (Mindestanforderungen: 18:25; Rückkehr des Monolithen: 20:00)
- Team embarc; 2016: Architektur-Spicker Nr. 3: Microservices
- M. Fowler; 2014: Microservices - a definition of this new architectural term
- T. Flohre; 2015: Wer Microservices richtig macht, braucht keine Workflow Engine und kein BPMN
- L. Mineiro, M. Grauel (Zalando); 2016: The Frontend Taboo: a Story of Full Stack Microservices
- E. Wolff; 2017: Microservices – der aktuelle Stand
- S. Newman; 2015: Pattern: Backends For Frontends
- S. Tilkov; 2016: Wait, what!? Our microservices have actual human users? (microXchg 2016) (Self-Contained Systems: 05:15)