Kapitel 1. Was sind Microservices?

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

In dem halben Jahrzehnt, seit ich die erste Ausgabe dieses Buches geschrieben habe, sind Microservices zu einer immer beliebteren Architektur geworden. Ich kann zwar nicht behaupten, dass ich für die explosionsartige Zunahme der Popularität verantwortlich bin, aber der Ansturm auf Microservice-Architekturen bedeutet, dass viele der Ideen, die ich zuvor beschrieben habe, inzwischen erprobt sind. Es ist also wieder einmal an der Zeit, die Essenz der Microservice-Architektur zu destillieren und die Kernkonzepte hervorzuheben, die Microservices funktionieren lassen.

Dieses Buch soll einen umfassenden Überblick über die Auswirkungen von Microservices auf verschiedene Aspekte der Softwarebereitstellung geben. In diesem Kapitel geht es zunächst um die Kernideen hinter Microservices, den Stand der Technik und einige Gründe, warum diese Architekturen so weit verbreitet sind.

Microservices auf einen Blick

Microservices sind unabhängig freigebbare Dienste, die um eine Geschäftsdomäne herum modelliert sind. Ein Dienst kapselt Funktionen und macht sie über Netzwerke für andere Dienste zugänglich - aus diesen Bausteinen lässt sich ein komplexeres System aufbauen. Ein Microservice kann das Inventar, ein anderer die Auftragsverwaltung und wieder ein anderer den Versand darstellen, aber zusammen können sie ein ganzes E-Commerce-System bilden. Microservices sind eine Architektur, die darauf ausgerichtet ist, dir viele Optionen zur Lösung deiner Probleme zu bieten.

Sie sind eine Art von serviceorientierter Architektur, auch wenn sie eine eigene Meinung darüber haben, wie die Grenzen zwischen den Diensten gezogen werden sollten, und bei denen die unabhängige Einsetzbarkeit der Schlüssel ist. Sie sind technologieunabhängig, was einer der Vorteile ist, die sie bieten.

Von außen betrachtet wird ein einzelner Microservice als Blackbox behandelt. Er hostet Geschäftsfunktionen auf einem oder mehreren Netzwerkendpunkten (z. B. einer Warteschlange oder einer REST-API, wie in Abbildung 1-1 dargestellt), und zwar über die jeweils am besten geeigneten Protokolle. Die Verbraucher, egal ob es sich um andere Microservices oder andere Programme handelt, greifen über diese vernetzten Endpunkte auf diese Funktionen zu. Interne Implementierungsdetails (z. B. die Technologie, in der der Dienst geschrieben ist, oder die Art und Weise, wie die Daten gespeichert werden) sind für die Außenwelt völlig verborgen. Das bedeutet, dass Microservice-Architekturen in den meisten Fällen die Verwendung gemeinsamer Datenbanken vermeiden; stattdessen kapselt jeder Microservice bei Bedarf seine eigene Datenbank.

bms2 0101
Abbildung 1-1. Ein Microservice, der seine Funktionalität über eine REST-API und ein Topic zur Verfügung stellt

Microservices basieren auf dem Konzept des Information Hiding.1 Information Hiding bedeutet, so viele Informationen wie möglich innerhalb einer Komponente zu verstecken und so wenig wie möglich über externe Schnittstellen preiszugeben. Dies ermöglicht eine klare Trennung zwischen dem, was leicht geändert werden kann, und dem, was schwieriger zu ändern ist. Die Implementierung, die vor externen Parteien verborgen ist, kann frei geändert werden, solange die vernetzten Schnittstellen, die der Microservice offenlegt, nicht rückwärtskompatibel geändert werden. Änderungen innerhalb einer Microservice-Grenze (wie in Abbildung 1-1 dargestellt) sollten sich nicht auf einen vorgelagerten Verbraucher auswirken, damit die Funktionalität unabhängig freigegeben werden kann. Dies ist wichtig, damit unsere Microservices isoliert bearbeitet und bei Bedarf freigegeben werden können. Klare, stabile Service-Grenzen, die sich nicht ändern, wenn sich die interne Implementierung ändert, führen zu Systemen mit geringerer Kopplung und stärkeremZusammenhalt.

Während wir auf über das Verstecken interner Implementierungsdetails sprechen, wäre es nachlässig von mir, das Muster der hexagonalen Architektur nicht zu erwähnen, das erstmals von Alistair Cockburn beschrieben wurde.2 Dieses Muster beschreibt, wie wichtig es ist, die interne Implementierung von den externen Schnittstellen zu trennen, denn es kann sein, dass du mit derselben Funktionalität über verschiedene Arten von Schnittstellen interagieren willst. Ich zeichne meine Microservices als Sechsecke, zum einen, um sie von "normalen" Diensten zu unterscheiden, zum anderen aber auch als Hommage an dieses Kunstwerk.

Schlüsselkonzepte von Microservices

Unter findest du einige Kernideen, die du verstehen musst, wenn du dich mit Microservices befasst. Da einige Aspekte oft übersehen werden, ist es wichtig, sich mit diesen Konzepten näher zu befassen, um sicherzustellen, dass du genau verstehst, was Microservices ausmacht.

Unabhängige Einsetzbarkeit

Independent Deployability ist die Idee, dass wir eine Änderung an einem Microservice vornehmen, sie bereitstellen und diese Änderung für unsere Nutzer freigeben können, ohne andere Microservices bereitstellen zu müssen. Noch wichtiger als die Tatsache, dass wir das tun können, ist die Tatsache, dass dies die Art und Weise ist, wie du Deployments in deinem System verwaltest. Es ist eine Disziplin, die du als deinen Standard-Release-Ansatz übernimmst. Das ist eine einfache Idee, die in der Umsetzung jedoch komplex ist.

Tipp

Wenn du nur eine Sache aus diesem Buch und dem Konzept der Microservices im Allgemeinen mitnimmst, sollte es diese sein: Sorge dafür, dass du das Konzept der unabhängigen Bereitstellungsfähigkeit deiner Microservices verinnerlichst. Gewöhne dir an, Änderungen an einem einzelnen Microservice in die Produktion zu übernehmen und freizugeben, ohne dass du dafür etwas anderes einsetzen musst. Daraus werden sich viele gute Dinge ergeben.

Um unabhängige Einsatzfähigkeit zu gewährleisten, müssen wir sicherstellen, dass unsere Microservices lose gekoppelt sind: Wir müssen in der Lage sein, einen Dienst zu ändern, ohne etwas anderes ändern zu müssen. Das bedeutet, dass wir explizite, gut definierte und stabile Verträge zwischen den Diensten brauchen. Einige Implementierungsentscheidungen erschweren dies - die gemeinsame Nutzung von Datenbanken ist zum Beispiel besonders problematisch.

Die unabhängige Einsatzfähigkeit an sich ist natürlich unglaublich wertvoll. Aber um eine unabhängige Einsatzfähigkeit zu erreichen, muss man noch viele andere Dinge richtig machen, die wiederum ihre eigenen Vorteile haben. Du siehst also, dass die unabhängige Einsatzfähigkeit eine wichtige Funktion ist - wenn du dich auf dieses Ziel konzentrierst, erreichst du eine Reihe zusätzlicher Vorteile.

Der Wunsch nach lose gekoppelten Diensten mit stabilen Schnittstellen leitet unser Denken darüber, wie wir unsere Microservice-Grenzen überhaupt erst finden.

Modelliert um ein Geschäftsfeld

Techniken wie das domänenorientierte Design ermöglichen es dir, deinen Code so zu strukturieren, dass er die reale Domäne, in der die Software arbeitet, besser abbildet.3 Bei Microservice-Architekturen nutzen wir dieselbe Idee, um unsere Dienste abzugrenzen. Durch die Modellierung von Diensten auf der Grundlage von Geschäftsdomänen können wir neue Funktionen leichter einführen und Microservices auf verschiedene Weise neu kombinieren, um unseren Nutzern neue Funktionen zu bieten.

Das Ausrollen einer Funktion, die Änderungen an mehr als einem Microservice erfordert, ist teuer. Du musst die Arbeit in jedem einzelnen Dienst (und möglicherweise in verschiedenen Teams) koordinieren und die Reihenfolge, in der die neuen Versionen dieser Dienste bereitgestellt werden, sorgfältig verwalten. Das ist viel aufwändiger, als die gleiche Änderung in einem einzelnen Dienst (oder in einem Monolithen) vorzunehmen. Daraus folgt, dass wir Wege finden wollen, um dienstübergreifende Änderungen so selten wie möglich vorzunehmen.

Ich sehe oft mehrschichtige Architekturen, wie zum Beispiel die dreischichtige Architektur in Abbildung 1-2. Hier steht jede Schicht in der Architektur für eine andere Dienstgrenze, wobei jede Dienstgrenze auf verwandten technischen Funktionen basiert. Wenn ich in diesem Beispiel nur die Präsentationsschicht ändern müsste, wäre das ziemlich effizient. Die Erfahrung hat jedoch gezeigt, dass sich Änderungen an der Funktionalität in solchen Architekturen in der Regel über mehrere Schichten erstrecken und Änderungen in der Präsentations-, Anwendungs- und Datenebene erforderlich machen. Dieses Problem wird noch verschärft, wenn die Architektur noch vielschichtiger ist als das einfache Beispiel in Abbildung 1-2; oft ist jede Schicht in weitere Schichten aufgeteilt.

Indem wir unsere Dienste zu End-to-End-Scheiben der Geschäftsfunktionalität machen, stellen wir sicher, dass unsere Architektur so gestaltet ist, dass Änderungen an der Geschäftsfunktionalität so effizient wie möglich sind. Mit Microservices haben wir uns entschieden, eine hohe Kohäsion der Geschäftsfunktionalität gegenüber einer hohen Kohäsion der technischen Funktionalität zu bevorzugen.

bms2 0102
Abbildung 1-2. Eine traditionelle dreistufige Architektur

Wir werden später in diesem Kapitel noch einmal auf das Zusammenspiel von domänenorientiertem Design und Organisationsdesign zurückkommen.

Den eigenen Staat besitzen

Eine der Dinge, mit denen sich die Leute am schwersten tun, ist die Idee, dass Microservices die Verwendung von gemeinsamen Datenbanken vermeiden sollten. Wenn ein Microservice auf die Daten eines anderen Microservices zugreifen möchte, sollte er den zweiten Microservice nach den Daten fragen. Auf diese Weise können die Microservices entscheiden, was gemeinsam genutzt wird und was nicht. So können wir die Funktionen, die sich frei ändern können (unsere interne Implementierung), klar von den Funktionen trennen, die wir nur selten ändern wollen (der externe Vertrag, den die Verbraucher nutzen).

Wenn wir die unabhängige Einsatzfähigkeit Wirklichkeit werden lassen wollen, müssen wir sicherstellen, dass wir die rückwärtskompatiblen Änderungen an unseren Microservices begrenzen. Wenn wir die Kompatibilität mit den vorgelagerten Verbrauchern unterbrechen, zwingen wir sie dazu, sich ebenfalls zu ändern. Eine klare Abgrenzung zwischen internen Implementierungsdetails und einem externen Vertrag für einen Microservice kann dazu beitragen, die Notwendigkeit rückwärtskompatibler Änderungen zu verringern.

Das Verstecken des internen Zustands von in einem Microservice ist vergleichbar mit der Praxis der Kapselung in der objektorientierten Programmierung (OO). Die Kapselung von Daten in OO-Systemen ist ein Beispiel für das Verstecken von Informationen in der Praxis.

Tipp

Gib keine Datenbanken weiter, es sei denn, du musst es wirklich tun. Und selbst dann tue alles, was du kannst, um es zu vermeiden. Meiner Meinung nach ist die gemeinsame Nutzung von Datenbanken eines der schlimmsten Dinge, die du tun kannst, wenn du eine unabhängige Einsatzfähigkeit erreichen willst.

Wie im vorigen Abschnitt erläutert, wollen wir unsere Dienste als durchgängige Scheiben von Geschäftsfunktionen betrachten, die gegebenenfalls die Benutzeroberfläche (UI), die Geschäftslogik und die Daten kapseln. Damit wollen wir den Aufwand für die Änderung geschäftsbezogener Funktionen reduzieren. Die Kapselung von Daten und Verhalten auf diese Weise sorgt für eine hohe Kohäsion der Geschäftsfunktionen. Indem wir die Datenbank, die unserem Dienst zugrunde liegt, verstecken, sorgen wir auch dafür, dass wir die Kopplung verringern. Auf Kopplung und Kohäsion kommen wir in Kapitel 2 zurück.

Größe

"Wie groß sollte ein Microservice sein?" ist eine der häufigsten Fragen, die ich höre. Wenn man bedenkt, dass das Wort "Mikro" schon im Namen steckt, ist das keine Überraschung. Wenn man sich jedoch mit der Frage beschäftigt, warum Microservices als Architektur funktionieren, ist das Konzept der Größe eigentlich einer der uninteressantesten Aspekte.

Wie misst du die Größe? Indem du Codezeilen zählst? Das ergibt für mich nicht viel Sinn. Etwas, das in Java vielleicht 25 Codezeilen erfordert, kann in Clojure in 10 Zeilen geschrieben werden. Das soll nicht heißen, dass Clojure besser oder schlechter als Java ist; manche Sprachen sind einfach ausdrucksstärker als andere.

James Lewis, technischer Direktor bei Thoughtworks, ist bekannt dafür, dass er sagt: "Ein Microservice sollte so groß sein wie mein Kopf." Auf den ersten Blick scheint das nicht sehr hilfreich zu sein. Denn wie groß ist James' Kopf genau? Der Grundgedanke hinter dieser Aussage ist, dass ein Microservice so groß sein sollte, dass er leicht verstanden werden kann. Die Herausforderung besteht natürlich darin, dass die Fähigkeit, etwas zu verstehen, bei verschiedenen Menschen nicht immer die gleiche ist, so dass du selbst entscheiden musst, welche Größe für dich geeignet ist. Ein erfahrenes Team ist vielleicht besser in der Lage, eine größere Codebasis zu verwalten als ein anderes Team. Vielleicht wäre es also besser, James' Zitat so zu lesen: "Ein Microservice sollte so groß wie dein Kopf sein."

Ich glaube, am ehesten kommt der Begriff "Größe" im Zusammenhang mit Microservices einer Bedeutung nahe, die Chris Richardson, der Autor von Microservice Patterns (Manning Publications), einmal formuliert hat: Das Ziel von Microservices ist es, "eine so kleine Schnittstelle wie möglich zu haben". Das entspricht wieder dem Konzept des Informationsversteckens, aber es ist ein Versuch, dem Begriff "Microservices" eine Bedeutung zu geben, dieursprünglich nicht vorhanden war. Als der Begriff zum ersten Mal verwendet wurde, um diese Architekturen zu definieren, lag der Fokus, zumindest anfangs, nicht speziell auf der Größe der Schnittstellen.

Letztlich ist das Konzept der Größe sehr kontextabhängig. Wenn du mit einer Person sprichst, die seit 15 Jahren an einem System arbeitet, wird sie denken, dass ihr System mit 100.000 Codezeilen wirklich leicht zu verstehen ist. Frag jemanden, der ganz neu in das Projekt eingestiegen ist, und er wird es für viel zu groß halten. Frag ein Unternehmen, das gerade erst mit der Umstellung auf Microservices begonnen hat und vielleicht 10 oder weniger Microservices hat, und du wirst eine andere Antwort bekommen als von einem Unternehmen ähnlicher Größe, für das Microservices seit vielen Jahren die Norm sind und das jetzt Hunderte hat.

Ich rate den Leuten, sich keine Gedanken über die Größe zu machen. Wenn du anfängst, ist es viel wichtiger, dass du dich auf zwei wichtige Dinge konzentrierst. Erstens: Wie viele Microservices kannst du bewältigen? Je mehr Dienste du anbietest, desto komplexer wird dein System, und du musst dir neue Fähigkeiten aneignen (und vielleicht auch neue Technologien einsetzen), um dies zu bewältigen. Die Umstellung auf Microservices bringt neue Quellen der Komplexität mit sich und damit auch neue Herausforderungen. Aus diesem Grund bin ich ein starker Befürworter einer schrittweisen Umstellung auf eine Microservice-Architektur. Zweitens: Wie definierst du die Grenzen von Microservices, um das Beste aus ihnen herauszuholen, ohne dass alles zu einem schrecklich gekoppelten Chaos wird? Diese Themen sind viel wichtiger, wenn du dich auf den Weg machst.

Flexibilität

Ein weiteres Zitat von James Lewis lautet: "Microservices kaufen dir Optionen". Lewis hat das mit Bedacht gesagt - sie verschaffen dir Optionen. Sie haben ihren Preis, und du musst entscheiden, ob die Kosten die Optionen wert sind, die du in Anspruch nehmen willst. Die daraus resultierende Flexibilität in vielerlei Hinsicht - organisatorisch, technisch, skalierbar, robust - kann unglaublich verlockend sein.

Wir wissen nicht, was die Zukunft bringt, also möchten wir eine Architektur, die uns theoretisch helfen kann, alle Probleme zu lösen, die in Zukunft auf uns zukommen könnten. Das Gleichgewicht zwischen dem Offenhalten deiner Optionen und dem Tragen der Kosten für solche Architekturen zu finden, kann eine echte Kunst sein.

Betrachte die Einführung von Microservices weniger als das Umlegen eines Schalters, sondern eher als das Drehen an einem Regler. Wenn du den Schalter weiter aufdrehst und mehr Microservices einführst, wird deine Flexibilität größer. Aber wahrscheinlich werden auch die Schmerzpunkte größer. Das ist ein weiterer Grund, warum ich für die schrittweise Einführung von Microservices plädiere. Wenn du den Regler schrittweise hochdrehst, kannst du die Auswirkungen besser einschätzen und bei Bedarf aufhören.

Angleichung von Architektur und Organisation

MusicCorp, ein E-Commerce-Unternehmen ( ), das CDs online verkauft, verwendet die in Abbildung 1-2 gezeigte einfache dreistufige Architektur. Wir haben uns entschlossen, MusicCorp mit aller Macht ins 21. Jahrhundert zu bringen, und in diesem Zusammenhang überprüfen wir die bestehende Systemarchitektur. Wir haben eine webbasierte Benutzeroberfläche, eine Geschäftslogikschicht in Form eines monolithischen Backends und eine Speicherung der Daten in einer herkömmlichen Datenbank. Diese Schichten gehören, wie üblich, verschiedenen Teams. Wir werden im Laufe des Buches immer wieder auf die Schwierigkeiten von MusicCorp zurückkommen.

Wir möchten eine einfache Aktualisierung unserer Funktionalität vornehmen: Wir möchten unseren Kunden die Möglichkeit geben, ihr Lieblingsmusikgenre anzugeben. Für diese Aktualisierung müssen wir die Benutzeroberfläche so ändern, dass sie die Genreauswahl anzeigt, den Backend-Service so, dass das Genre in der Benutzeroberfläche angezeigt und der Wert geändert werden kann, und die Datenbank so, dass sie diese Änderung akzeptiert. Diese Änderungen müssen von jedem Team verwaltet und in der richtigen Reihenfolge implementiert werden, wie in Abbildung 1-3 dargestellt.

bms2 0103
Abbildung 1-3. Eine Änderung über alle drei Ebenen hinweg ist aufwändiger

Diese Architektur ist nicht schlecht. Jede Architektur wird letztendlich auf eine Reihe von Zielen hin optimiert. Die dreistufige Architektur ist auch deshalb so verbreitet, weil sie universell ist - jeder hat schon davon gehört. Die Tendenz, eine gängige Architektur zu wählen, die du vielleicht schon woanders gesehen hast, ist also ein Grund dafür, dass wir dieses Muster immer wieder sehen. Aber ich glaube, der Hauptgrund, warum wir diese Architektur immer wieder sehen, ist, dass sie darauf beruht, wie wir unsere Teams organisieren.

Das berühmt gewordene Conway'sche Gesetz besagt Folgendes:

Organisationen, die Systeme entwerfen, sind gezwungen, Entwürfe zu erstellen, die die Kommunikationsstrukturen dieser Organisationen widerspiegeln.

Melvin Conway, "Wie machen Ausschüsse Erfindungen?"

Die dreistufige Architektur ist ein gutes Beispiel für dieses Gesetz in Aktion. In der Vergangenheit gruppierten IT-Organisationen ihre Mitarbeiter in erster Linie nach ihrer Kernkompetenz: Datenbankadministratoren waren in einem Team mit anderen Datenbankadministratoren; Java-Entwickler waren in einem Team mit anderen Java-Entwicklern; und Frontend-Entwickler (die heutzutage exotische Dinge wie JavaScript und native mobile Anwendungsentwicklung beherrschen) waren in einem weiteren Team. Wir gruppieren die Mitarbeiter nach ihren Kernkompetenzen und schaffen so IT-Assets, die auf diese Teams abgestimmt werden können.

Das erklärt, warum diese Architektur so weit verbreitet ist. Sie ist nicht schlecht, sondern nur auf eine bestimmte Art von Kräften optimiert - wie wir die Menschen traditionell gruppiert haben, auf Vertrautheit. Aber die Kräfte haben sich geändert. Unsere Ansprüche an unsere Software haben sich geändert. Wir gruppieren unsere Mitarbeiter jetzt in Teams mit mehreren Qualifikationen, um Übergaben und Silos zu vermeiden. Wir wollen Software viel schneller als je zuvor bereitstellen. Das treibt uns dazu, unsere Teams anders zu organisieren, und zwar so, dass wir sie so organisieren, wie wir unsere Systeme aufteilen.

Die meisten Änderungen, die wir an unserem System vornehmen sollen, beziehen sich auf Änderungen der Geschäftsfunktionen. In Abbildung 1-3 ist unsere Geschäftsfunktionalität jedoch über alle drei Ebenen verteilt, was die Wahrscheinlichkeit erhöht, dass eine Änderung der Funktionalität schichtenübergreifend ist. Diese Architektur weist eine hohe Kohäsion der zugehörigen Technologien, aber eine geringe Kohäsion der Geschäftsfunktionen auf. Wenn wir es einfacher machen wollen, Änderungen vorzunehmen, müssen wir stattdessen die Art und Weise ändern, wie wir den Code gruppieren, und uns für den Zusammenhalt der Geschäftsfunktionen statt der Technologie entscheiden. Jeder Dienst kann eine Mischung aus diesen drei Schichten enthalten oder auch nicht, aber das ist eine Frage der lokalen Dienstimplementierung.

Vergleichen wir mit einer möglichen alternativen Architektur, die in Abbildung 1-4 dargestellt ist. Anstelle einer horizontalen Architektur und Organisation gliedern wir unsere Organisation und Architektur entlang vertikaler Geschäftslinien. Hier sehen wir ein spezielles Team, das die volle Verantwortung für Änderungen an Aspekten des Kundenprofils trägt, wodurch sichergestellt wird, dass der Umfang der Änderungen in diesem Beispiel auf ein Team beschränkt ist.

Die Umsetzung könnte über einen einzigen Microservice erfolgen, der dem Profilteam gehört und eine Benutzeroberfläche bereitstellt, über die die Kunden ihre Informationen aktualisieren können, wobei der Status des Kunden ebenfalls in diesem Microservice gespeichert wird. Die Wahl eines Lieblingsgenres ist mit einem bestimmten Kunden verbunden, so dass diese Änderung viel lokaler ist. In Abbildung 1-5 sehen wir auch die Liste der verfügbaren Genres, die von einem Microservice Catalog abgerufen wird, der wahrscheinlich schon vorhanden ist. Wir sehen auch einen neuen Microservice Recommendation, der auf die Informationen zu unseren Lieblingsgenres zugreift.

bms2 0104
Abbildung 1-4. Die Benutzeroberfläche ist aufgeteilt und wird von einem Team verwaltet, das auch die serverseitigen Funktionen verwaltet, die die Benutzeroberfläche unterstützen
bms2 0105
Abbildung 1-5. Ein spezieller Microservice Customer kann es viel einfacher machen, die Lieblingsmusikrichtung eines Kunden aufzunehmen

In einer solchen Situation kapselt unser Microservice Customer einen kleinen Teil jeder der drei Ebenen - ein bisschen Benutzeroberfläche, ein bisschen Anwendungslogik und ein bisschen Datenspeicherung. Unsere Geschäftsdomäne wird zur Hauptantriebskraft unserer Systemarchitektur, was es hoffentlich einfacher macht, Änderungen vorzunehmen und unsere Teams auf die Geschäftsbereiche innerhalb des Unternehmens auszurichten.

Oft wird die Benutzeroberfläche nicht direkt vom Microservice bereitgestellt, aber selbst wenn dies der Fall ist, würden wir erwarten, dass der Teil der Benutzeroberfläche, der mit dieser Funktionalität zusammenhängt, immer noch dem Kundenprofil-Team gehört, wie in Abbildung 1-4 dargestellt. Das Konzept, dass ein Team für einen Teil der Benutzeroberfläche verantwortlich ist, gewinnt zunehmend an Bedeutung. Das Buch Team Topologies4 wird die Idee des Stream-Aligned Teams vorgestellt, das dieses Konzept verkörpert:

Ein Stream-aligned Team ist ein Team, das auf einen einzigen, wertvollen Arbeitsstrom ausgerichtet ist...[D]as Team ist in der Lage, so schnell, sicher und unabhängig wie möglich einen Mehrwert für den Kunden oder Benutzer zu schaffen und zu liefern, ohne dass Teile der Arbeit an andere Teams weitergegeben werden müssen.

Die in Abbildung 1-4 gezeigten Teams wären auf Streams ausgerichtete Teams, ein Konzept, das wir in den Kapiteln 14 und 15 genauer untersuchen werden, einschließlich der Frage, wie diese Arten von Organisationsstrukturen in der Praxis funktionieren und wie sie mit Microservices zusammenpassen.

Der Monolith

Wir haben auf über Microservices gesprochen, aber am häufigsten werden Microservices als Architekturansatz diskutiert, der eine Alternative zur monolithischen Architektur darstellt. Um die Microservice-Architektur deutlicher abzugrenzen und dir zu helfen, besser zu verstehen, ob Microservices eine Überlegung wert sind, sollte ich auch darauf eingehen, was genau ich mit Monolithen meine.

Wenn ich in diesem Buch von Monolithen spreche, beziehe ich mich in erster Linie auf eine Einheit für die Bereitstellung. Wenn alle Funktionen eines Systems zusammen eingesetzt werden müssen, ist es ein Monolith. Es gibt mehrere Architekturen, auf die diese Definition zutrifft, aber ich werde die am häufigsten vorkommenden besprechen: den Monolithen mit nur einem Prozess, den modularen Monolithen und den verteilten Monolithen.

Der Einzelprozess-Monolith

Das gängigste Beispiel, das einem in den Sinn kommt, wenn man über Monolithen spricht, ist ein System, in dem der gesamte Code als ein einziger Prozess bereitgestellt wird, wie in Abbildung 1-6. Aus Gründen der Robustheit oder Skalierung kann es mehrere Instanzen dieses Prozesses geben, aber im Grunde ist der gesamte Code in einem einzigen Prozess zusammengefasst. In Wirklichkeit können diese Einzelprozesssysteme selbst einfache verteilte Systeme sein, weil sie fast immer Daten aus einer Datenbank lesen oder in einer Datenbank speichern oder Informationen für Web- oder mobile Anwendungen bereitstellen.

bms2 0106
Abbildung 1-6. In einem Monolithen mit einem Prozess wird der gesamte Code in einen einzigen Prozess verpackt

Obwohl dies dem Verständnis der meisten Menschen von einem klassischen Monolithen entspricht, sind die meisten Systeme, denen ich begegne, etwas komplexer als das. Es kann sein, dass du zwei oder mehr Monolithen hast, die eng miteinander verbunden sind, möglicherweise mit Software von anderen Anbietern.

Ein klassischer monolithischer Einsatz mit nur einem Prozess kann für viele Unternehmen sinnvoll sein. David Heinemeier Hansson, der Erfinder von Ruby on Rails, hat überzeugend dargelegt, dass eine solche Architektur für kleinere Organisationen sinnvoll ist.5 Wenn die Organisation jedoch wächst, kann der Monolith möglicherweise mitwachsen, was uns zum modularen Monolithen bringt.

Der modulare Monolith

Der modulare Monolith ( ) ist eine Untermenge des Einzelprozess-Monolithen und eine Variante, bei der der Einzelprozess aus einzelnen Modulen besteht. Jedes Modul kann unabhängig von den anderen bearbeitet werden, muss aber für den Einsatz zusammengefügt werden, wie in Abbildung 1-7 dargestellt. Das Konzept, Software in Module aufzuteilen, ist nicht neu; modulare Software hat ihre Wurzeln in der strukturierten Programmierung in den 1970er Jahren und sogar noch weiter zurück. Dennoch ist dies ein Ansatz, den ich noch immer nicht bei genügend Unternehmen sehe.

bms2 0107
Abbildung 1-7. In einem modularen Monolithen ist der Code innerhalb des Prozesses in Module unterteilt

Für viele Unternehmen kann der modulare Monolith eine ausgezeichnete Wahl sein. Wenn die Modulgrenzen gut definiert sind, kann er ein hohes Maß an paralleler Arbeit ermöglichen und gleichzeitig die Herausforderungen einer verteilten Microservice-Architektur vermeiden, da er eine viel einfachere Bereitstellungstopologie hat. Shopify ist ein großartiges Beispiel für ein Unternehmen, das diese Technik als Alternative zur Microservice-Dekomposition verwendet hat, und sie scheint für dieses Unternehmen wirklich gut zu funktionieren.6

Eine der Herausforderungen eines modularen Monolithen besteht darin, dass die Datenbank in der Regel nicht so zerlegt ist wie die Code-Ebene, was zu erheblichen Problemen führt, wenn du den Monolithen in Zukunft auseinandernehmen willst. Ich habe gesehen, wie einige Teams versucht haben, die Idee des modularen Monolithen weiter voranzutreiben, indem sie die Datenbank nach dem gleichen Prinzip wie die Module zerlegt haben (siehe Abbildung 1-8).

bms2 0108
Abbildung 1-8. Ein modularer Monolith mit einer dekomponierten Datenbank

Der verteilte Monolith

Ein verteiltes System ist ein System, in dem der Ausfall eines Computers, von dem du noch nicht einmal wusstest, dass er existiert, deinen eigenen Computer unbrauchbar machen kann.7

Leslie Lamport

Ein verteilter Monolith ist ein System, das aus mehreren Diensten besteht, aber aus welchen Gründen auch immer, muss das gesamte System gemeinsam eingesetzt werden. Ein verteilter Monolith mag zwar die Definition einer SOA erfüllen, aber allzu oft schlägt er die Versprechen einer SOA nicht ein. Meiner Erfahrung nach hat ein verteilter Monolith alle Nachteile eines verteilten Systems und die Nachteile eines Monolithen mit nur einem Prozess, ohne die Vorteile eines der beiden zu bieten. Die Begegnung mit einer Reihe von verteilten Monolithen in meiner Arbeit hat mein Interesse an der Microservice-Architektur stark beeinflusst.

Verteilte Monolithen entstehen in der Regel in einer Umgebung, in der nicht genug Wert auf Konzepte wie das Verstecken von Informationen und die Kohäsion von Geschäftsfunktionen gelegt wurde. Stattdessen führen hochgradig gekoppelte Architekturen dazu, dass Änderungen über die Grenzen von Diensten hinausgehen und scheinbar unschuldige Änderungen, die lokal begrenzt zu sein scheinen, andere Teile des Systems zerstören.

Monolithen und Lieferstreitigkeiten

Da immer mehr Menschen am selben Ort arbeiten, kommen sie sich gegenseitig in die Quere - zum Beispiel, wenn verschiedene Entwickler dasselbe Stück Code ändern wollen, wenn verschiedene Teams Funktionen zu unterschiedlichen Zeitpunkten in Betrieb nehmen wollen (oder die Bereitstellung verzögern) und wenn nicht klar ist, wem was gehört und wer Entscheidungen trifft. Eine Vielzahl von Studien hat gezeigt, wie schwierig es ist, die Verantwortlichkeiten zu klären.8 Ich bezeichne dieses Problem als " Delivery Contention".

Wenn du einen Monolithen hast, bedeutet das nicht, dass du auf jeden Fall mit dem Problem der Lieferkonflikte konfrontiert wirst, genauso wenig wie eine Microservice-Architektur bedeutet, dass du nie mit diesem Problem konfrontiert wirst. Aber eine Microservice-Architektur gibt dir konkretere Grenzen, innerhalb derer die Eigentumsverhältnisse in einem System geklärt werden können, was dir viel mehr Flexibilität bei der Reduzierung dieses Problems gibt.

Vorteile von Monolithen

Einige Monolithen, wie z. B. die Single-Process- oder modularen Monolithen, haben ebenfalls eine ganze Reihe von Vorteilen. Ihre viel einfachere Bereitstellungstopologie kann viele der Fallstricke vermeiden, die mit verteilten Systemen verbunden sind. Dies kann zu wesentlich einfacheren Entwicklungsworkflows führen, und auch die Überwachung, Fehlerbehebung und Aktivitäten wie End-to-End-Tests können stark vereinfacht werden.

Monolithen können auch die Wiederverwendung von Code innerhalb des Monolithen selbst vereinfachen. Wenn wir Code in einem verteilten System wiederverwenden wollen, müssen wir uns entscheiden, ob wir Code kopieren, Bibliotheken auslagern oder die gemeinsam genutzten Funktionen in einen Dienst einbringen wollen. Mit einem Monolithen sind unsere Entscheidungen viel einfacher, und viele Leute mögen diese Einfachheit - der ganze Code ist da; benutze ihn einfach!

Leider betrachten die Menschen den Monolithen als etwas, das man vermeiden sollte - als etwas, das von Natur aus problematisch ist. Ich habe mehrere Menschen getroffen, für die der Begriff Monolith gleichbedeutend mit Legacy ist. Das ist ein Problem. Eine monolithische Architektur ist eine Entscheidung, und zwar eine richtige Entscheidung. Ich würde sogar noch weiter gehen und sagen, dass sie meiner Meinung nach die vernünftige Standardwahl für einen Architekturstil ist. Mit anderen Worten: Ich suche eher nach einem Grund, der mich davon überzeugt, Microservices zu nutzen, als nach einem Grund, sie nicht zu nutzen.

Wenn wir in die Falle tappen und den Monolithen als praktikable Option für die Bereitstellung unserer Software systematisch untergraben, laufen wir Gefahr, weder uns noch den Nutzern unserer Software gerecht zu werden.

Ermöglichende Technologie

Wie ich bereits erwähnt habe ( ), glaube ich nicht, dass du viele neue Technologien einführen musst, wenn du anfängst, Microservices zu nutzen. Das kann sogar kontraproduktiv sein. Stattdessen solltest du beim Ausbau deiner Microservice-Architektur ständig nach Problemen Ausschau halten, die durch dein zunehmend verteiltes System verursacht werden, und dann nach Technologien suchen, die dir dabei helfen könnten.

Dennoch hat die Technologie eine große Rolle bei der Einführung von Microservices als Konzept gespielt. Die Werkzeuge zu verstehen, die dir helfen, das Beste aus dieser Architektur herauszuholen, ist ein entscheidender Faktor für den Erfolg einer Microservice-Implementierung. Ich würde sogar so weit gehen zu sagen, dass Microservices ein so umfassendes Verständnis der unterstützenden Technologie erfordern, dass die bisherige Unterscheidung zwischen logischer und physischer Architektur problematisch sein kann - wenn du an der Gestaltung einer Microservice-Architektur beteiligt bist, brauchst du ein umfassendes Verständnis dieser beiden Welten.

In den folgenden Kapiteln werden wir viele dieser Technologien im Detail erforschen, aber vorher wollen wir dir kurz einige der Technologien vorstellen, die dir helfen können, wenn du dich für Microservices entscheidest.

Log Aggregation und verteiltes Tracing

Mit der zunehmenden Anzahl von Prozessen, die du verwaltest, kann es schwierig sein zu verstehen, wie sich dein System in einer Produktionsumgebung verhält. Das wiederum kann die Fehlerbehebung erheblich erschweren. Wir werden uns in Kapitel 10 eingehender mit diesen Ideen befassen, aber ich empfehle zumindest die Implementierung eines Log-Aggregationssystems als Voraussetzung für die Einführung einer Microservice-Architektur.

Tipp

Wenn du mit Microservices anfängst, solltest du vorsichtig sein, zu viele neue Technologien zu übernehmen. Dennoch ist ein Tool zur Log-Aggregation so wichtig, dass du es als Voraussetzung für die Einführung von Microservices betrachten solltest.

Diese Systeme ermöglichen es dir, Logs von all deinen Diensten zu sammeln und zu aggregieren. So hast du eine zentrale Stelle, von der aus die Logs analysiert und sogar in einen aktiven Alarmierungsmechanismus integriert werden können. Es gibt viele Optionen in diesem Bereich, die für zahlreiche Situationen geeignet sind. Ich bin aus verschiedenen Gründen ein großer Fan von Humio, aber auch die einfachen Logging-Dienste der großen Public Cloud-Anbieter sind für den Anfang gut genug.

Du kannst diese Tools zur Log-Aggregation noch nützlicher machen, indem du Korrelations-IDs einführst, bei denen eine einzige ID für einen zusammenhängenden Satz von Serviceaufrufen verwendet wird - z. B. für die Kette von Aufrufen, die durch eine Benutzerinteraktion ausgelöst werden könnten. Indem du diese ID als Teil jedes Protokolleintrags protokollierst, wird es viel einfacher, die Protokolle zu isolieren, die mit einem bestimmten Fluss von Aufrufen verbunden sind, was wiederum die Fehlersuche erleichtert.

Je komplexer wird, desto wichtiger werden Tools, mit denen du besser erforschen kannst, was dein System tut. Sie bieten die Möglichkeit, Traces über mehrere Dienste hinweg zu analysieren, Engpässe zu erkennen und deinem System Fragen zu stellen, von denen du gar nicht wusstest, dass du sie stellen wolltest. Open-Source-Tools können einige dieser Funktionen bieten. Ein Beispiel dafür ist Jaeger, das sich auf die verteilte Ablaufverfolgung konzentriert.

Aber Produkte wie Lightstep und Honeycomb (siehe Abbildung 1-9) gehen noch weiter. Sie stellen eine neue Generation von Tools dar, die über die traditionellen Überwachungsansätze hinausgehen und es viel einfacher machen, den Zustand deines laufenden Systems zu untersuchen. Vielleicht hast du bereits konventionellere Tools im Einsatz, aber du solltest dir die Möglichkeiten dieser Produkte unbedingt ansehen. Sie wurden von Grund auf entwickelt, um die Probleme zu lösen, mit denen die Betreiber von Microservice-Architekturen zu kämpfen haben.

bms2 0109
Abbildung 1-9. Ein verteilter Trace in Honeycomb, der es dir ermöglicht, zu erkennen, wo die Zeit für Operationen, die sich über mehrere Microservices erstrecken können, verbraucht wird

Container und Kubernetes

Idealerweise möchtest du jede Microservice-Instanz isoliert betreiben. So wird sichergestellt, dass sich Probleme in einem Microservice nicht auf einen anderen Microservice auswirken können, indem sie z. B. die gesamte CPU beanspruchen. Virtualisierung ist eine Möglichkeit, isolierte Ausführungsumgebungen auf vorhandener Hardware zu schaffen, aber normale Virtualisierungstechniken können ziemlich schwerfällig sein, wenn wir die Größe unserer Microservices berücksichtigen. Container hingegen bieten eine viel schlankere Methode, um isolierte Ausführungsumgebungen für Service-Instanzen bereitzustellen, was zu schnelleren Startzeiten für neue Container-Instanzen führt und für viele Architekturen viel kostengünstiger ist.

Wenn du anfängst, mit Containern herumzuspielen, wirst du auch feststellen, dass du etwas brauchst, mit dem du diese Container auf vielen zugrunde liegenden Maschinen verwalten kannst. Container-Orchestrierungsplattformen wie Kubernetes tun genau das: Sie ermöglichen es dir, Container-Instanzen so zu verteilen, dass sie die Robustheit und den Durchsatz bieten, die dein Dienst braucht, und gleichzeitig die zugrunde liegenden Maschinen effizient zu nutzen. In Kapitel 8 werden wir die Konzepte der betrieblichen Isolierung, der Container und Kubernetes untersuchen.

Du brauchst dich nicht zu beeilen, um Kubernetes oder gar Container einzuführen. Sie bieten zweifellos erhebliche Vorteile gegenüber herkömmlichen Bereitstellungsmethoden, aber ihre Einführung ist schwer zu rechtfertigen, wenn du nur ein paar Microservices hast. Wenn der Aufwand für die Verwaltung der Bereitstellung zu einem großen Problem wird, solltest du die Containerisierung deines Dienstes und den Einsatz von Kubernetes in Betracht ziehen. Wenn du dich dafür entscheidest, solltest du dafür sorgen, dass jemand anderes den Kubernetes-Cluster für dich betreibt, z. B. indem du einen verwalteten Dienst bei einem öffentlichen Cloud-Provider in Anspruch nimmst. Einen eigenen Kubernetes-Cluster zu betreiben, kann eine Menge Arbeit bedeuten!

Streaming

Obwohl wir uns mit Microservices von monolithischen Datenbanken wegbewegen, müssen wir immer noch Wege finden, um Daten zwischen Microservices auszutauschen. Dies geschieht zur gleichen Zeit, in der Unternehmen von der Batch-Berichterstattung weg und hin zu mehr Echtzeit-Feedback kommen wollen, damit sie schneller reagieren können. Produkte, die ein einfaches Streaming und die Verarbeitung von oft großen Datenmengen ermöglichen, sind daher bei den Nutzern von Microservice-Architekturen beliebt.

Apache Kafka ist für viele Menschen die de-facto-Wahl für das Streaming von Daten in einer Microservice-Umgebung geworden, und das aus gutem Grund. Funktionen wie Nachrichtenpermanenz, Verdichtung und Skalierbarkeit zur Verarbeitung großer Nachrichtenmengen sind unglaublich nützlich. Kafka hat in Form von KSQLDB begonnen, Stream-Processing-Funktionen hinzuzufügen, aber du kannst es auch mit speziellen Stream-Processing-Lösungen wie Apache Flink verwenden. Debezium ist ein Open-Source-Tool, das entwickelt wurde, um Daten aus bestehenden Datenquellen über Kafka zu streamen und so sicherzustellen, dass traditionelle Datenquellen Teil einer Stream-basierten Architektur werden können. In Kapitel 4 werden wir uns ansehen, wie Streaming-Technologie bei der Integration von Microservices eine Rolle spielen kann.

Öffentliche Cloud und Serverless

Öffentliche Cloud-Provider, genauer gesagt die drei wichtigsten Anbieter - Google Cloud, Microsoft Azure und Amazon Web Services (AWS) - bieten eine riesige Auswahl an Managed Services und Bereitstellungsoptionen für die Verwaltung deiner Anwendung. Wenn deine Microservice-Architektur wächst, wird immer mehr Arbeit in den operativen Bereich verlagert. Öffentliche Cloud-Provider bieten eine Vielzahl von Managed Services an, von verwalteten Datenbankinstanzen oder Kubernetes-Clustern bis hin zu Message Brokern oder verteilten Dateisystemen. Wenn du diese verwalteten Dienste in Anspruch nimmst, verlagerst du einen großen Teil dieser Arbeit auf einen Dritten, der diese Aufgaben wohl besser bewältigen kann.

Besonders interessant unter den Public-Cloud-Angeboten sind die Produkte, die unter dem Begriff " Serverless" zusammengefasst werden. Diese Produkte verbergen die zugrundeliegenden Maschinen und ermöglichen es dir, auf einer höheren Abstraktionsebene zu arbeiten. Beispiele für serverlose Produkte sind Message-Broker, Speicherlösungen und Datenbanken. Die Funktion as a Service (FaaS)-Plattformen sind von besonderem Interesse, weil sie eine schöne Abstraktion rund um die Bereitstellung von Code bieten. Du brauchst dir keine Gedanken darüber zu machen, wie viele Server du für deinen Dienst brauchst, sondern du stellst einfach deinen Code bereit und überlässt es der Plattform, bei Bedarf Instanzen deines Codes zu erstellen. Wir werden uns Serverless in Kapitel 8 genauer ansehen.

Vorteile von Microservices

Die Vorteile von Microservices sind zahlreich und vielfältig. Viele dieser Vorteile lassen sich auf jedes verteilte System übertragen. Microservices erreichen diese Vorteile jedoch vor allem deshalb in einem höheren Maße, weil sie bei der Definition der Dienstgrenzen eine eigenwillige Haltung einnehmen. Durch die Kombination der Konzepte des Information Hiding und des domain-driven Designs mit der Leistungsfähigkeit verteilter Systeme können Microservices gegenüber anderen Formen verteilterArchitekturen erhebliche Vorteile bieten.

Heterogenität der Technologie

Mit einem System, das aus mehreren zusammenarbeitenden Microservices besteht, können wir entscheiden, ob wir innerhalb jedes einzelnen Services unterschiedliche Technologien einsetzen wollen. So können wir für jede Aufgabe das richtige Werkzeug auswählen, anstatt einen standardisierten, einheitlichen Ansatz zu wählen, der oft als kleinster gemeinsamer Nenner endet.

Wenn ein Teil unseres Systems leistungsfähiger werden muss, können wir beschließen, einen anderen Technologie-Stack zu verwenden, der besser in der Lage ist, das erforderliche Leistungsniveau zu erreichen. Wir könnten auch entscheiden, dass wir die Art der Datenspeicherung für verschiedene Teile unseres Systems ändern müssen. In einem sozialen Netzwerk könnten wir zum Beispiel die Interaktionen unserer Nutzer/innen in einer graphenorientierten Datenbank speichern, um die stark vernetzte Natur eines sozialen Graphen widerzuspiegeln, aber die Beiträge der Nutzer/innen könnten in einem dokumentenorientierten Datenspeicher gespeichert werden.

bms2 0110
Abbildung 1-10. Mit Microservices kannst du verschiedene Technologien leichter einbinden

Mit Microservices können wir Technologien auch schneller übernehmen und verstehen, wie neue Entwicklungen uns helfen können. Eines der größten Hindernisse beim Ausprobieren und Übernehmen einer neuen Technologie sind die damit verbundenen Risiken. Wenn ich bei einer monolithischen Anwendung eine neue Programmiersprache, Datenbank oder ein neues Framework ausprobieren möchte, wirkt sich jede Änderung auf einen Großteil meines Systems aus. Bei einem System, das aus mehreren Diensten besteht, habe ich mehrere Möglichkeiten, eine neue Technologie auszuprobieren. Ich kann einen Microservice mit dem vielleicht geringsten Risiko auswählen und die Technologie dort einsetzen, weil ich weiß, dass ich mögliche negative Auswirkungen begrenzen kann. Für viele Unternehmen ist diese Fähigkeit, neue Technologien schneller zu übernehmen, ein echter Vorteil.

Der Einsatz mehrerer Technologien ist natürlich nicht ohne Aufwand verbunden. Manche Unternehmen schränken die Wahl der Sprache ein. Netflix und Twitter zum Beispiel nutzen meist die Java Virtual Machine (JVM) als Plattform, weil diese Unternehmen die Zuverlässigkeit und Leistung dieses Systems sehr gut kennen. Sie entwickeln auch Bibliotheken und Werkzeuge für die JVM, die den Betrieb in großem Umfang erleichtern. Die Abhängigkeit von JVM-spezifischen Bibliotheken erschwert jedoch die Arbeit für nicht Java-basierte Dienste oder Kunden. Aber weder Twitter noch Netflix verwenden nur einen Technologie-Stack für alle Aufträge.

Die Tatsache, dass die interne Technologieimplementierung vor den Kunden verborgen ist, kann auch die Aktualisierung von Technologien erleichtern. Deine gesamte Microservice-Architektur könnte z. B. auf Spring Boot basieren, aber du könntest die JVM-Version oder die Framework-Versionen für nur einen Microservice ändern, was es einfacher macht, das Risiko von Upgrades zu verwalten.

Robustheit

Ein Schlüsselkonzept zur Verbesserung der Robustheit deiner Anwendung ist die Trennwand. Eine Komponente eines Systems kann fehlschlagen, aber solange dieser Fehler nicht kaskadenartig auftritt, kannst du das Problem isolieren und der Rest des Systems kann weiterarbeiten. Dienstgrenzen werden zu deinen offensichtlichen Schotten. Wenn bei einem monolithischen Dienst der Dienst fehlschlägt, funktioniert alles nicht mehr. Bei einem monolithischen System können wir auf mehreren Rechnern arbeiten, um das Risiko eines Ausfalls zu verringern, aber mit Microservices können wir Systeme bauen, die den Totalausfall einiger der einzelnen Dienste verkraften und die Funktionalität entsprechend reduzieren.

Wir müssen jedoch vorsichtig sein. Um sicherzustellen, dass unsere Microservice-Systeme diese verbesserte Robustheit auch wirklich nutzen können, müssen wir die neuen Fehlerquellen verstehen, mit denen verteilte Systeme umgehen müssen. Netzwerke können und werden fehlschlagen, ebenso wie Maschinen. Wir müssen wissen, wie wir mit solchen Ausfällen umgehen und welche Auswirkungen diese Ausfälle auf die Endnutzer unserer Software haben werden. Ich habe schon mit Teams zusammengearbeitet, die nach der Umstellung auf Microservices mit einem weniger robusten System endeten, weil sie diese Bedenken nicht ernst genug genommen haben.

Skalierung

Bei einem großen, monolithischen Dienst müssen wir alles zusammen skalieren. Vielleicht ist ein kleiner Teil unseres Gesamtsystems in der Leistung eingeschränkt, aber wenn dieses Verhalten in einer riesigen monolithischen Anwendung eingeschlossen ist, müssen wir alles zusammen skalieren. Mit kleineren Diensten können wir nur die Dienste skalieren, die skaliert werden müssen, so dass wir andere Teile des Systems auf kleinerer, weniger leistungsfähiger Hardware betreiben können, wie in Abbildung 1-11 dargestellt.

bms2 0111
Abbildung 1-11. Du kannst die Skalierung nur auf die Microservices ausrichten, die sie benötigen

Gilt, ein Online-Modehändler, hat genau aus diesem Grund Microservices eingeführt. Das System von Gilt, das 2007 mit einer monolithischen Rails-Anwendung begann, war 2009 nicht mehr in der Lage, die Belastung zu bewältigen. Durch die Aufteilung von Kernbereichen des Systems konnte Gilt besser mit den Verkehrsspitzen umgehen und verfügt heute über mehr als 450 Microservices, von denen jeder auf mehreren separaten Maschinen läuft.

Wenn On-Demand-Bereitstellungssysteme wie die von AWS einsetzt, können wir diese Skalierung sogar bei Bedarf für die Teile anwenden, die sie benötigen. So können wir unsere Kosten besser kontrollieren. Es kommt nicht oft vor, dass ein architektonischer Ansatz so eng mit einer fast unmittelbaren Kosteneinsparung verbunden ist.

Letztlich können wir unsere Anwendungen auf vielfältige Weise skalieren, und Microservices können ein effektiver Teil davon sein. Wir werden die Skalierung von Microservices in Kapitel 13 genauer betrachten.

Einfacher Einsatz

Eine einzeilige Änderung an einer monolithischen Anwendung mit einer Million Zeilen erfordert, dass die gesamte Anwendung bereitgestellt wird, um die Änderung zu veröffentlichen. Das könnte ein Einsatz mit großen Auswirkungen und hohem Risiko sein. In der Praxis werden solche Einsätze aus verständlicher Angst nur selten durchgeführt. Leider bedeutet das, dass sich unsere Änderungen zwischen den Releases immer weiter anhäufen, bis die neue Version unserer Anwendung, die in Produktion geht, massenhaft Änderungen enthält. Und je größer das Delta zwischen den Versionen ist, desto höher ist das Risiko, dass wir etwas falsch machen!

Mit Microservices können wir eine Änderung an einem einzelnen Dienst vornehmen und ihn unabhängig vom Rest des Systems einsetzen. So können wir unseren Code schneller bereitstellen. Wenn ein Problem auftritt, kann es schnell auf einen einzelnen Dienst eingegrenzt werden, so dass ein schnelles Rollback möglich ist. Das bedeutet auch, dass wir unsere neuen Funktionen schneller an die Kunden weitergeben können. Das ist einer der Hauptgründe, warum Unternehmen wie Amazon und Netflix diese Architekturen verwenden - um sicherzustellen, dass sie so viele Hindernisse wie möglich aus dem Weg räumen, um Software auf den Markt zu bringen.

Organisatorische Ausrichtung

Viele von uns haben die Probleme erlebt, die mit großen Teams und großen Codebases verbunden sind. Diese Probleme können sich noch verschärfen, wenn das Team verteilt ist. Wir wissen auch, dass kleinere Teams, die an kleineren Codebases arbeiten, tendenziell produktiver sind.

Microservices ermöglichen es uns, unsere Architektur besser auf unsere Organisation abzustimmen. So können wir die Anzahl der Mitarbeiter, die an einer Codebasis arbeiten, minimieren und den Sweet Spot aus Teamgröße und Produktivität erreichen. Microservices ermöglichen es uns auch, die Eigentumsverhältnisse an den Diensten zu ändern, wenn sich die Organisation ändert - so können wir die Ausrichtung zwischen Architektur und Organisation auch in Zukunft beibehalten.

Zusammensetzbarkeit

Eines der wichtigsten Versprechen von verteilten Systemen und serviceorientierten Architekturen ist, dass wir Möglichkeiten zur Wiederverwendung von Funktionen eröffnen. Mit Microservices können unsere Funktionen auf unterschiedliche Weise für verschiedene Zwecke genutzt werden. Das kann besonders wichtig sein, wenn wir darüber nachdenken, wie unsere Kunden unsere Software nutzen.

Vorbei sind die Zeiten, in denen wir nur an unsere Desktop-Website oder unsere mobile Anwendung denken konnten. Jetzt müssen wir an die unzähligen Möglichkeiten denken, wie wir Funktionen für das Web, die native Anwendung, das mobile Web, die Tablet-App oder das tragbare Gerät miteinander verknüpfen können. Wenn Unternehmen nicht mehr nur in engen Kanälen denken, sondern ganzheitlichere Konzepte der Kundenansprache verfolgen, brauchen wir Architekturen, die damit Schritt halten können.

Mit Microservices öffnen wir Nahtstellen in unserem System, die von Außenstehenden angesprochen werden können. Je nachdem, wie sich die Umstände ändern, können wir die Anwendungen auf unterschiedliche Weise aufbauen. Bei einer monolithischen Anwendung habe ich oft eine grobkörnige Naht, die von außen genutzt werden kann. Wenn ich diese aufbrechen will, um etwas Nützlicheres zu erhalten, brauche ich einen Hammer!

Microservice Schmerzpunkte

Wie wir bereits gesehen haben, bringen Microservice -Architekturen eine Menge Vorteile mit sich. Aber sie bringen auch eine Menge Komplexität mit sich. Wenn du die Einführung einer Microservice-Architektur in Erwägung ziehst, ist es wichtig, dass du in der Lage bist, das Gute mit dem Schlechten zu vergleichen. In Wirklichkeit lassen sich die meisten Microservice-Punkte auf verteilte Systeme zurückführen und wären daher in einem verteilten Monolithen genauso zu finden wie in einer Microservice-Architektur.

Wir werden viele dieser Themen im weiteren Verlauf des Buches ausführlich behandeln - ich würde sogar behaupten, dass der Großteil dieses Buches dem Umgang mit den Schmerzen, dem Leid und dem Schrecken einer Microservice-Architektur gewidmet ist.

Erfahrung als Entwickler

Wenn du immer mehr Dienste hast, kann die Erfahrung der Entwickler darunter leiden. Ressourcenintensivere Laufzeiten wie die JVM können die Anzahl der Microservices begrenzen, die auf einem einzigen Entwicklerrechner ausgeführt werden können. Ich könnte wahrscheinlich vier oder fünf JVM-basierte Microservices als separate Prozesse auf meinem Laptop ausführen, aber könnte ich auch 10 oder 20 ausführen? Höchstwahrscheinlich nicht. Selbst bei weniger anstrengenden Laufzeiten gibt es eine Grenze für die Anzahl der Dinge, die du lokal ausführen kannst, was unweigerlich zu Diskussionen darüber führt, was zu tun ist, wenn du nicht das gesamte System auf einem Rechner ausführen kannst. Das kann noch komplizierter werden, wenn du Cloud-Dienste nutzt, die du nicht lokal ausführen kannst.

Extreme Lösungen können die "Entwicklung in der Cloud" sein, bei der die Entwickler nicht mehr lokal entwickeln können. Ich bin kein Fan davon, weil die Feedback-Zyklen darunter stark leiden können. Stattdessen denke ich, dass die Beschränkung des Umfangs der Teile eines Systems, an denen ein Entwickler arbeiten muss, ein viel geradlinigerer Ansatz ist. Dies könnte jedoch problematisch sein, wenn du ein Modell der "kollektiven Verantwortung" anstrebst, bei dem jeder Entwickler an jedem Teil des Systems arbeiten kann.

Technologie-Überlastung

Die schiere Masse an neuen Technologien, die aufgetaucht sind, um die Einführung von Microservice-Architekturen zu ermöglichen, kann überwältigend sein. Ich will ehrlich sein und sagen, dass viele dieser Technologien einfach nur als "Microservice-freundlich" umgetauft wurden, aber einige Fortschritte haben wirklich geholfen, die Komplexität dieser Art von Architekturen zu bewältigen. Es besteht jedoch die Gefahr, dass diese Fülle an neuen Spielzeugen zu einer Art Technologiefetischismus führen kann. Ich habe so viele Unternehmen erlebt, die eine Microservice-Architektur eingeführt haben, dass sie beschlossen haben, dass dies auch der beste Zeitpunkt ist, um eine Vielzahl neuer und oft fremder Technologien einzuführen.

Microservices bieten dir vielleicht die Möglichkeit, jeden Microservice in einer anderen Programmiersprache zu schreiben, auf einer anderen Laufzeitumgebung laufen zu lassen oder eine andere Datenbank zu verwenden - aber das sind Optionen, keine Anforderungen. Du musst sorgfältig abwägen zwischen dem Umfang und der Komplexität der von dir verwendeten Technologie und den Kosten, die eine Vielzahl von Technologien mit sich bringen kann.

Wenn du mit der Einführung von Microservices beginnst, sind einige grundsätzliche Herausforderungen unausweichlich: Du wirst viel Zeit damit verbringen müssen, Fragen zu Datenkonsistenz, Latenz, Servicemodellierung und Ähnlichem zu verstehen. Wenn du versuchst zu verstehen, wie diese Ideen die Art und Weise, wie du über Softwareentwicklung denkst, verändern, während du gleichzeitig eine riesige Menge an neuer Technologie einführst, wirst du es schwer haben. Außerdem wird die Zeit, die du brauchst, um all diese neuen Technologien zu verstehen, dazu führen, dass du weniger Zeit hast, um deine Nutzer/innen mit neuen Funktionen zu versorgen.

Wenn du die Komplexität deiner Microservice-Architektur (allmählich) erhöhst, solltest du neue Technologien einführen, wenn du sie brauchst. Du brauchst keinen Kubernetes-Cluster, wenn du drei Dienste hast! Diese schrittweise Steigerung hat nicht nur den Vorteil, dass du mit der Komplexität dieser neuen Tools nicht überfordert bist, sondern auch, dass du neue und bessere Methoden kennenlernst, die sich im Laufe der Zeit zweifellos herausbilden werden.

Kosten

Es ist sehr wahrscheinlich, dass zumindest kurzfristig die Kosten aus mehreren Gründen steigen werden. Erstens musst du wahrscheinlich mehr Dinge betreiben - mehr Prozesse, mehr Computer, mehr Netzwerk, mehr Speicherung und mehr unterstützende Software (für die zusätzliche Lizenzgebühren anfallen).

Zweitens: Jede Veränderung, die du in einem Team oder einer Organisation einführst, wird dich kurzfristig ausbremsen. Es braucht Zeit, um neue Ideen zu lernen und herauszufinden, wie man sie effektiv einsetzt. Während dieser Zeit werden andere Aktivitäten beeinträchtigt. Dies führt entweder zu einer direkten Verlangsamung der Bereitstellung neuer Funktionen oder zu der Notwendigkeit, mehr Personal einzustellen, um diese Kosten auszugleichen.

Meiner Erfahrung nach sind Microservices eine schlechte Wahl für Unternehmen, denen es in erster Linie um die Senkung von Kosten geht, denn eine Mentalität der Kostensenkung - bei der die IT als Kostenstelle und nicht als Profitcenter betrachtet wird - wird immer ein Hindernis sein, das Beste aus dieser Architektur herauszuholen. Andererseits können Microservices dir helfen, mehr Geld zu verdienen, wenn du diese Architekturen nutzen kannst, um mehr Kunden zu erreichen oder mehr Funktionen parallel zu entwickeln. Sind Microservices also ein Weg, um mehr Gewinn zu machen? Vielleicht. Sind Microservices ein Weg, um Kosten zu senken? Nicht unbedingt.

Melden

Mit, einem monolithischen System, hast du normalerweise auch eine monolithische Datenbank. Das bedeutet, dass die Beteiligten, die alle Daten zusammen analysieren wollen, was oft umfangreiche Join-Operationen zwischen den Daten erfordert, ein fertiges Schema haben, mit dem sie ihre Berichte erstellen können. Sie können sie einfach direkt gegen die monolithische Datenbank laufen lassen, vielleicht gegen ein Replikat, wie in Abbildung 1-12 gezeigt.

bms2 0112
Abbildung 1-12. Direkt auf der Datenbank eines Monolithen durchgeführte Berichterstattung

Mit einer Microservice-Architektur haben wir dieses monolithische Schema aufgebrochen. Das bedeutet nicht, dass die Notwendigkeit von Berichten über alle unsere Daten weggefallen ist; wir haben es nur viel schwieriger gemacht, weil unsere Daten jetzt über mehrere logisch isolierte Schemata verstreut sind.

Modernere Ansätze für das Berichtswesen, wie z.B. die Nutzung von Streaming für Echtzeitberichte über große Datenmengen, können gut mit einer Microservice-Architektur funktionieren, erfordern aber in der Regel die Einführung neuer Ideen und damit verbundener Technologien. Alternativ kannst du auch einfach Daten aus deinen Microservices in zentralen Berichtsdatenbanken (oder vielleicht weniger strukturierten Data Lakes) veröffentlichen, um Berichtsfälle zu ermöglichen.

Überwachung und Fehlerbehebung

Mit, einer monolithischen Standardanwendung, können wir einen ziemlich einfachen Ansatz zur Überwachung verfolgen. Wir müssen uns nur um eine kleine Anzahl von Rechnern kümmern, und der Ausfallmodus der Anwendung ist eher binär - die Anwendung ist oft entweder komplett in Betrieb oder komplett ausgefallen. Verstehen wir bei einer Microservice-Architektur, welche Auswirkungen es hat, wenn nur eine einzige Instanz eines Dienstes ausfällt?

Wenn unsere CPU bei einem monolithischen System für längere Zeit bei 100 % hängen bleibt, wissen wir, dass das ein großes Problem ist. Können wir bei einer Microservice-Architektur mit Dutzenden oder Hunderten von Prozessen dasselbe sagen? Müssen wir jemanden um 3 Uhr morgens wecken, wenn nur ein Prozess bei 100 % CPU festhängt?

Glücklicherweise gibt es in diesem Bereich eine ganze Reihe von Ideen, die dir helfen können. Wenn du dich eingehender mit diesem Konzept befassen möchtest, empfehle ich dir Distributed Systems Observability von Cindy Sridharan (O'Reilly) als hervorragenden Ausgangspunkt, aber wir werden uns in Kapitel 10 auch selbst mit Monitoring und Observability befassen.

Sicherheit

Als noch ein monolithisches System mit nur einem Prozess war, flossen viele unserer Informationen innerhalb dieses Prozesses. Jetzt fließen mehr Informationen über Netzwerke zwischen unseren Diensten. Das macht unsere Daten anfälliger dafür, dass sie bei der Übertragung beobachtet und möglicherweise im Rahmen von Man-in-the-Middle-Angriffen manipuliert werden. Das bedeutet, dass du unter Umständen mehr darauf achten musst, die Daten während der Übertragung zu schützen und sicherzustellen, dass deine Microservice-Endpunkte so geschützt sind, dass sie nur von autorisierten Parteien genutzt werden können. Kapitel 11 widmet sich ganz den Herausforderungen in diesem Bereich.

Testen

Mit jeder Art von automatisiertem Funktionstest musst du einen Balanceakt vollziehen. Je mehr Funktionen ein Test ausführt, d. h. je breiter der Testumfang ist, desto mehr Vertrauen hast du in deine Anwendung. Andererseits ist es umso schwieriger, Testdaten und unterstützende Fixtures einzurichten, je größer der Testumfang ist, je länger die Ausführung des Tests dauert und je schwieriger es ist, herauszufinden, was nicht funktioniert, wenn er fehlschlägt. In Kapitel 9 stelle ich dir eine Reihe von Techniken vor, mit denen das Testen in dieser schwierigen Umgebung funktioniert.

End-to-End-Tests für jede Art von System stehen am äußersten Ende der Skala, was die abgedeckten Funktionen angeht, und wir sind daran gewöhnt, dass sie schwieriger zu schreiben und zu warten sind als Unit-Tests mit kleinerem Umfang. Das ist es aber oft wert, denn wir wollen das Vertrauen haben, dass ein End-to-End-Test unsere Systeme so nutzt, wie es ein Benutzer tun würde.

Aber mit einer Microservice-Architektur wird der Umfang unserer End-to-End-Tests sehr groß. Wir müssen nun Tests über mehrere Prozesse hinweg durchführen, die alle für die Testszenarien bereitgestellt und entsprechend konfiguriert werden müssen. Außerdem müssen wir auf falsch negative Ergebnisse vorbereitet sein, die auftreten, wenn unsere Tests aufgrund von Umweltproblemen fehlschlagen, z. B. wenn Service-Instanzen sterben oder Netzwerk-Time-outs bei fehlgeschlagenen Implementierungen auftreten.

Diese Kräfte bedeuten, dass du mit zunehmender Größe deiner Microservice-Architektur eine abnehmende Investitionsrendite bei den End-to-End-Tests erzielst. Die Tests werden mehr kosten, aber sie werden dir nicht mehr das gleiche Maß an Vertrauen geben wie in der Vergangenheit. Das treibt dich zu neuen Formen des Testens, wie z. B. vertragsgesteuertes Testen oder Testen in der Produktion, sowie zur Erkundung progressiver Auslieferungstechniken wie parallele Läufe oder Kanarienvogel-Releases, die wir uns in Kapitel 8 ansehen werden.

Latenz

Mit, einer Microservice-Architektur, kann die Verarbeitung, die zuvor lokal auf einem Prozessor stattfand, nun auf mehrere separate Microservices aufgeteilt werden. Informationen, die vorher nur innerhalb eines einzigen Prozesses geflossen sind, müssen nun über Netzwerke, die du vielleicht mehr als je zuvor nutzt, serialisiert, übertragen und deserialisiert werden. All das kann dazu führen, dass sich die Latenz deinesSystems verschlechtert.

Obwohl es schwierig sein kann, die genaue Auswirkung auf die Latenz von Abläufen in der Entwurfs- oder Programmierphase zu messen, ist dies ein weiterer Grund, warum es wichtig ist, eine Microservice-Migration schrittweise vorzunehmen. Nimm eine kleine Änderung vor und miss dann die Auswirkungen. Das setzt voraus, dass du eine Möglichkeit hast, die End-to-End-Latenz für die Operationen, die dich interessieren, zu messen - verteilte Tracing-Tools wieJaeger können dabei helfen. Aber du musst auch wissen, welche Latenzzeit für diese Vorgängeakzeptabel ist. Manchmal ist es durchaus akzeptabel, eine Operation langsamer zu machen, solange sie noch schnell genug ist!

Datenkonsistenz

Die Umstellung von von einem monolithischen System, in dem die Daten in einer einzigen Datenbank gespeichert und verwaltet werden, auf ein viel verteilteres System, in dem mehrere Prozesse den Zustand in verschiedenen Datenbanken verwalten, bringt potenzielle Herausforderungen in Bezug auf die Konsistenz der Daten mit sich. Während du dich in der Vergangenheit vielleicht auf Datenbanktransaktionen verlassen hast, um Zustandsänderungen zu verwalten, musst du dir darüber im Klaren sein, dass eine ähnliche Sicherheit in einem verteilten System nicht ohne weiteres gewährleistet werden kann. Die Verwendung verteilter Transaktionen erweist sich in den meisten Fällen als äußerst problematisch bei der Koordinierung von Zustandsänderungen.

Stattdessen musst du vielleicht anfangen, Konzepte wie Sagas (auf die ich in Kapitel 6 ausführlich eingehen werde) und eventuelle Konsistenz zu verwenden, um den Zustand deines Systems zu verwalten und darüber nachzudenken. Diese Ideen können grundlegende Änderungen in der Art und Weise erfordern, wie du über die Daten in deinen Systemen denkst, was bei der Migration bestehender Systeme ziemlich entmutigend sein kann. Das ist ein weiterer Grund, warum du vorsichtig sein solltest, wie schnell du deine Anwendung zerlegst. Es ist wichtig, dass du bei der Zerlegung schrittweise vorgehst, damit du die Auswirkungen von Änderungen an deiner Architektur in der Produktion beurteilen kannst.

Sollte ich Microservices nutzen?

Trotz der Bestrebungen in einigen Kreisen, Microservice-Architekturen zum Standardansatz für Software zu machen, bin ich der Meinung, dass ihre Einführung aufgrund der zahlreichen Herausforderungen, die ich skizziert habe, immer noch gut überlegt sein muss. Du musst deinen eigenen Problembereich, deine Fähigkeiten und deine Technologielandschaft bewerten und verstehen, was du erreichen willst, bevor du entscheidest, ob Microservices das Richtige für dich sind. Sie sind ein architektonischer Ansatz, nicht der architektonische Ansatz. Dein eigener Kontext sollte eine große Rolle bei deiner Entscheidung spielen, ob du diesen Weg gehen willst.

Dennoch möchte ich einige Situationen beschreiben, die mich davon abhalten würden, Microservices zu wählen - oder dafür plädieren.

Für wen sie nicht arbeiten sollten

Da es wichtig ist, stabile Service-Grenzen zu definieren, halte ich Microservice-Architekturen für brandneue Produkte oder Start-ups oft für eine schlechte Wahl. In beiden Fällen ist die Domäne, mit der du arbeitest, in der Regel erheblichen Veränderungen unterworfen, während du die Grundlagen dessen, was du aufzubauen versuchst, iterierst. Diese Verschiebung der Domänenmodelle führt wiederum dazu, dass mehr Änderungen an den Servicegrenzen vorgenommen werden, und die Koordination von Änderungen überServicegrenzen hinweg ist ein teuresUnterfangen. Im Allgemeinen halte ich es für sinnvoller zu warten, bis sich das Domänenmodell ausreichend stabilisiert hat, bevor man sich an die Definition von Dienstgrenzen macht.

Ich erlebe immer wieder, dass Start-ups zuerst auf Microservices setzen, mit der Begründung: "Wenn wir wirklich erfolgreich sind, müssen wir skalieren!" Das Problem ist, dass du nicht unbedingt weißt, ob überhaupt jemand dein neues Produkt nutzen will. Und selbst wenn du so erfolgreich wirst, dass du eine hoch skalierbare Architektur brauchst, kann es sein, dass das, was du am Ende deinen Nutzern anbietest, ganz anders ist als das, was du ursprünglich entwickelt hast. Uber konzentrierte sich zunächst auf Limousinen, und Flickr entstand aus dem Versuch, ein Multiplayer-Online-Spiel zu entwickeln. Die Suche nach dem passenden Produkt für den Markt kann dazu führen, dass du am Ende ein ganz anderes Produkt hast als das, das du zu Beginn zu bauen gedenktest.

Startups haben in der Regel auch weniger Mitarbeiter, um das System aufzubauen, was zu weiteren Herausforderungen im Hinblick auf Microservices führt. Microservices bringen neue Arbeit und Komplexität mit sich, was wertvolle Bandbreite binden kann. Je kleiner das Team ist, desto ausgeprägter sind diese Kosten. Bei der Arbeit mit kleineren Teams mit nur einer Handvoll Entwickler/innen bin ich aus diesem Grund sehr zögerlich, Microservices vorzuschlagen.

Die Herausforderung von Microservices für Startups wird durch die Tatsache verschärft, dass das größte Hindernis normalerweise die Mitarbeiter sind. Für ein kleines Team kann es schwierig sein, eine Microservice-Architektur zu rechtfertigen, weil allein für die Bereitstellung und Verwaltung der Microservices Arbeit anfällt. Manche Leute bezeichnen dies als "Microservice-Steuer". Wenn diese Investition vielen Menschen zugute kommt, ist sie leichter zu rechtfertigen. Aber wenn nur eine Person deines fünfköpfigen Teams ihre Zeit mit diesen Aufgaben verbringt, ist das eine Menge wertvoller Zeit, die nicht für die Entwicklung deines Produkts genutzt wird. Es ist viel einfacher, zu einem späteren Zeitpunkt auf Microservices umzusteigen, wenn du weißt, wo die Grenzen deiner Architektur liegen und was deine Probleme sind - dann kannst du deine Energie darauf konzentrieren, Microservices an den sinnvollsten Stellen einzusetzen.

Unternehmen, die Software entwickeln, die von ihren Kunden eingesetzt und verwaltet werden soll, haben möglicherweise Schwierigkeiten mit Microservices. Wie wir bereits erwähnt haben, können Microservice-Architekturen eine Menge Komplexität in den Bereich der Bereitstellung und des Betriebs bringen. Wenn du die Software selbst betreibst, kannst du diese neue Komplexität ausgleichen, indem du neue Technologien einführst, neue Fähigkeiten entwickelst und deine Arbeitsmethoden änderst. Das kannst du von deinen Kunden nicht erwarten. Wenn sie daran gewöhnt sind, deine Software als Windows-Installationsprogramm zu erhalten, wird es für sie ein Schock sein, wenn du die nächste Version deiner Software verschickst und sagst: "Setze einfach diese 20 Pods auf deinen Kubernetes-Cluster!" Höchstwahrscheinlich haben sie keine Ahnung, was ein Pod, Kubernetes oder ein Cluster überhaupt ist.

Wo sie gut funktionieren

Meiner Erfahrung nach ist der wahrscheinlich wichtigste Grund für die Einführung von Microservices, dass mehr Entwickler am selben System arbeiten können, ohne sich gegenseitig in die Quere zu kommen. Wenn die Architektur und die organisatorischen Grenzen stimmen, können mehr Leute unabhängig voneinander arbeiten und es gibt weniger Konflikte bei der Lieferung. Ein Startup mit fünf Mitarbeitern wird eine Microservice-Architektur wahrscheinlich als lästig empfinden. Ein hundertköpfiges Unternehmen, das schnell wächst, wird feststellen, dass sein Wachstum mit einer Microservice-Architektur, die auf die Produktentwicklung abgestimmt ist, viel einfacher zu bewältigen ist.

Software-as-a-Service (SaaS)-Anwendungen eignen sich im Allgemeinen auch gut für eine Microservice-Architektur. Von diesen Produkten wird in der Regel erwartet, dass sie rund um die Uhr in Betrieb sind, was eine Herausforderung darstellt, wenn es darum geht, Änderungen einzuführen. Die unabhängige Releasefähigkeit von Microservice-Architekturen ist in diesem Bereich ein großer Vorteil. Außerdem können die Microservices je nach Bedarf nach oben oder unten skaliert werden. Das bedeutet, dass du, sobald du eine vernünftige Basislinie für die Belastungsmerkmale deines Systems festgelegt hast, mehr Kontrolle darüber hast, wie du dein System auf die kosteneffizienteste Weise skalieren kannst.

Die technologieunabhängige Natur von Microservices stellt sicher, dass du das Beste aus den Cloud-Plattformen herausholen kannst. Public Cloud-Provider bieten eine breite Palette von Diensten und Bereitstellungsmechanismen für deinen Code. Du kannst die Anforderungen bestimmter Dienste viel einfacher mit den Cloud-Diensten abgleichen, mit denen du sie am besten umsetzen kannst. Du könntest dich zum Beispiel dafür entscheiden, einen Dienst als Funktionspaket, einen anderen als verwaltete virtuelle Maschine (VM) und einen weiteren auf einer verwalteten Platform as a Service (PaaS)-Plattform bereitzustellen.

Obwohl die Einführung einer breiten Palette von Technologien oft ein Problem darstellt, ist die Möglichkeit, neue Technologien einfach auszuprobieren, eine gute Möglichkeit, schnell neue Ansätze zu erkennen, die Vorteile bringen könnten. Die wachsende Beliebtheit von FaaS-Plattformen ist ein solches Beispiel. Für die entsprechenden Arbeitslasten kann eine FaaS-Plattform den betrieblichen Aufwand drastisch reduzieren, aber derzeit ist sie nicht in allen Fällen ein geeigneter Einsatzmechanismus.

Microservices bieten auch klare Vorteile für Unternehmen, die ihren Kunden Dienstleistungen über eine Vielzahl neuer Kanäle anbieten wollen. Bei vielen Bemühungen um die digitale Transformation geht es darum, Funktionen freizuschalten, die in bestehenden Systemen verborgen sind. Der Wunsch ist es, neue Kundenerfahrungen zu schaffen, die die Bedürfnisse der Nutzer/innen über die jeweils sinnvollsten Interaktionsmechanismen erfüllen können.

Eine Microservice-Architektur ist vor allem eine Architektur, die dir bei der Weiterentwicklung deines Systems viel Flexibilität bietet. Diese Flexibilität hat natürlich ihren Preis, aber wenn du dir deine Optionen für künftige Änderungen offen halten willst, kann sich dieser Preis lohnen.

Zusammenfassung

Microservice-Architekturen bieten dir ein hohes Maß an Flexibilität bei der Auswahl von Technologien, der Handhabung von Robustheit und Skalierung, der Organisation von Teams und vielem mehr. Diese Flexibilität ist einer der Gründe, warum sich viele Menschen für Microservice-Architekturen entscheiden. Aber Microservices bringen auch ein hohes Maß an Komplexität mit sich, und du musst sicherstellen, dass diese Komplexität auch gerechtfertigt ist. Für viele sind sie zu einer Standard-Systemarchitektur geworden, die in praktisch allen Situationen eingesetzt werden soll. Ich bin jedoch nach wie vor der Meinung, dass sie eine architektonische Entscheidung sind, deren Einsatz durch die zu lösenden Probleme gerechtfertigt sein muss; oft lassen sich einfachere Ansätze viel leichter umsetzen.

Nichtsdestotrotz haben viele Unternehmen, vor allem größere, gezeigt, wie effektiv Microservices sein können. Wenn die Kernkonzepte von Microservices richtig verstanden und umgesetzt werden, können sie helfen, leistungsfähige, produktive Architekturen zu schaffen, die Systeme zu mehr als der Summe ihrer Teile machen.

Ich hoffe, dieses Kapitel hat dir eine gute Einführung in diese Themen gegeben. Als Nächstes werden wir uns ansehen, wie wir die Grenzen von Microservices definieren und dabei die Themen strukturierte Programmierung und domänenorientiertes Design erkunden.

1 Dieses Konzept wurde zum ersten Mal von David Parnas in "Information Distribution Aspects of Design Methodology", Information Processing, umrissen : Proceedings of the IFIP Congress 1971 (Amsterdam: North-Holland, 1972), 1:339-44.

2 Alistair Cockburn, "Hexagonal Architecture", 4. Januar 2005, https://oreil.ly/NfvTP.

3 Eine ausführliche Einführung in das Domain-Driven Design findest du in Domain-Driven Design von Eric Evans (Addison-Wesley), einen kompakteren Überblick findest du in Domain-Driven Design Distilled von Vaughn Vernon (Addison-Wesley).

4 Matthew Skelton und Manuel Pais, Team Topologies (Portland, OR: IT Revolution, 2019).

5 David Heinemeier Hansson, "The Majestic Monolith", Signal v. Noise, 29. Februar 2016, https://oreil.ly/WwG1C.

6 Einige nützliche Einblicke in die Überlegungen, die hinter Shopifys Verwendung eines modularen Monolithen anstelle von Microservices stehen, findest du in "Deconstructing the Monolith" von Kirsten Westeinde.

7 Leslie Lamport, E-Mail-Nachricht an ein DEC SRC-Bulletin Board um 12:23:29 PDT am 28. Mai 1987.

8 Microsoft Research hat in diesem Bereich Studien durchgeführt, die ich alle empfehle, aber als Ausgangspunkt empfehle ich "Don't Touch My Code! Examining the Effects of Ownership on Software Quality" von Christian Bird et al.

Get Aufbau von Microservices, 2. Auflage now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.