Zum Hauptinhalt springen

Microservices

Womit alles anfängt: der Monolith

Ein Monolith ist eine Anwendung, die als ein Stück gebaut, ausgeliefert und gestartet wird. Eine .jar-Datei, eine Datenbank, ein Prozess.

Genau so sieht dein Gästebuch aus. Und dein Webshop. Und die allermeisten Anwendungen, die je geschrieben wurden.

„Monolith" ist kein Schimpfwort

Der Begriff klingt nach Altlast, beschreibt aber nur eine Bauform — und meistens die richtige. Ein Monolith ist einfach zu bauen, einfach zu testen, einfach zu betreiben. Eine Datenbanktransaktion umspannt das ganze System. Ein Methodenaufruf kann nicht im Netzwerk verlorengehen.

Wer mit Microservices anfängt, weil sie moderner klingen, zahlt einen hohen Preis für ein Problem, das er noch gar nicht hat.

Wo der Monolith eng wird

Solange er klein ist, gar nicht. Die Probleme kommen mit der Größe und mit der Anzahl der Menschen, die daran arbeiten.

Was passiertWarum es beim Monolithen weh tut
Ein Team wartet auf das andereAlle liefern gemeinsam aus. Wer fertig ist, wartet auf den, der es nicht ist
Eine Änderung, alles neu testenEin Fehler im Rechnungsdruck kann den Warenkorb betreffen — man weiß es nicht sicher
Ein Absturz, alles wegEin Speicherleck im Bildimport reißt die ganze Anwendung mit
Eine Technologie für allesDie Bildverarbeitung wäre in einer anderen Sprache besser aufgehoben — geht aber nicht
Skalieren nur im GanzenNur die Suche ist überlastet? Trotzdem wird alles vervielfacht

Der letzte Punkt ist der greifbarste.

Monolith skalierennur die Suche ist überlastetSucheKatalogVersandSucheKatalogVersandSucheKatalogVersand3 × allesKatalog und Versand laufen zweimalumsonst mitMicroservices skalierendieselbe ÜberlastSucheSucheSucheKatalogVersand3 × Suche, 1 × der Restvervielfacht wird nur, waswirklich unter Last stehtDas ist der Kern des Skalierbarkeits-Arguments — und der einzige Vorteil, der sich sofort ausrechnen lässt.

Der Schnitt: waagerecht oder senkrecht?

Bevor es um Microservices geht, lohnt eine Zwischenstufe — und sie erklärt, warum manche Monolithen altern und andere nicht.

Ein Monolith ist meist in Schichten geteilt: Web-Schicht, Fachlichkeit, Datenzugriff. Das ist ein waagerechter Schnitt, und du kennst ihn aus Tutorial 04. Innerhalb einer Anwendung ist das genau richtig.

Der andere Schnitt geht senkrecht: nicht nach technischer Aufgabe, sondern nach Fachlichkeit. Alles, was zu „Bestellung" gehört — Oberfläche, Regeln, Tabellen —, liegt beieinander.

waagerecht: nach TechnikWeb-SchichtFachlichkeitDatenzugriffEine fachliche Änderung fasstalle drei Schichten an.senkrecht: nach FachlichkeitKatalogWebFachlichkeitDatenBestellungWebFachlichkeitDatenVersandWebFachlichkeitDatenEine fachliche Änderung bleibtin einem Streifen.Beide Schnitte schließen sich nicht aus: Jeder senkrechte Streifen ist innen waagerecht geschichtet.

Diese senkrechten Streifen tragen einen Namen: Self-Contained Systems, im deutschsprachigen Raum oft schlicht Vertikalen. Jede Vertikale ist für sich lauffähig, hat ihre eigenen Daten und ihre eigene Oberfläche.

Die wichtigste Einsicht dieses Blatts

Der Unterschied zwischen einem alternden Monolithen und einem, mit dem man in zehn Jahren noch arbeiten kann, ist nicht die Anzahl der Prozesse. Es ist der Schnitt.

Ein senkrecht geschnittener Monolith lässt sich später in Dienste zerlegen, weil die Grenzen schon da sind. Ein waagerecht geschnittener nicht — dort verläuft jede fachliche Grenze quer durch alle Schichten.

Erst richtig schneiden, dann verteilen. Wer verteilt, ohne geschnitten zu haben, bekommt einen Monolithen mit Netzwerk dazwischen — alle Nachteile beider Bauformen.

Was Microservices sind

Ein Microservice ist eine Vertikale, die als eigener Prozess läuft und über das Netzwerk angesprochen wird.

Die Merkmale, die zusammengehören:

MerkmalWas es bedeutet
Fachlich geschnittenEin Dienst gehört zu einem Fachgebiet, nicht zu einer technischen Schicht
Unabhängig auslieferbarDer Katalog-Dienst geht live, ohne dass Versand angefasst wird
Eigene DatenhaltungKein anderer Dienst greift auf seine Tabellen zu — nur über seine Schnittstelle
Über das Netz erreichbarMeist per HTTP und JSON — also mit genau dem, was du in diesem Lernfeld baust
Ein Team pro DienstEin Dienst ist klein genug, dass ein Team ihn ganz versteht
Die eigene Datenhaltung ist der harte Teil

Es klingt nach einer Nebensache, ist aber die Regel, an der die meisten Umbauten scheitern: Zwei Dienste greifen nie auf dieselben Tabellen zu.

Sobald der Versand-Dienst direkt in die Katalog-Tabellen schreibt, sind beide wieder aneinandergekettet — die Datenbank ist dann die gemeinsame Kupplung, und keiner der beiden lässt sich noch allein ändern. Man hat die Nachteile des Monolithen behalten und die Nachteile des Netzwerks dazugekauft.

Der Preis dieser Regel: Daten liegen mehrfach vor, und sie sind nicht in derselben Sekunde überall gleich.

Was es kostet

Ein Methodenaufruf im Monolithen kann nicht fehlschlagen. Ein Netzwerkaufruf kann immer fehlschlagen. Das ist der Kern aller Nachteile.

Was im Monolithen selbstverständlich istWas daraus bei Microservices wird
Methodenaufruf, NanosekundenNetzwerkaufruf, Millisekunden — und er kann ausbleiben
Eine Transaktion über allesJeder Dienst hat seine eigene; über Dienstgrenzen gibt es keine gemeinsame
Ein Log, eine FehlermeldungEine Anfrage hinterlässt Spuren in fünf Logs
Beim Kompilieren fällt auf, wenn ein Aufruf nicht mehr passtFällt erst zur Laufzeit auf — die Schnittstelle ist ein Versprechen, kein Vertrag, den der Compiler prüft
Eine Anwendung startenZehn Dienste starten, in der richtigen Reihenfolge

Deshalb der oft zitierte Rat: Fang mit einem Monolithen an. Zerlege ihn, wenn du weißt, wo die Grenzen wirklich verlaufen — und nicht vorher, denn vorher weiß es niemand.

Die Fehlvorstellung, um die es hier geht

„Microservices sind moderner, also besser."

Sie sind eine Antwort auf ein Organisationsproblem: viele Menschen, die gleichzeitig an einem System arbeiten und sich nicht gegenseitig blockieren sollen. Wer dieses Problem nicht hat, hat auch den Nutzen nicht — zahlt aber den vollen Preis.

Es gibt gute Gründe für Microservices und gute Gründe dagegen. Was es nicht gibt, ist ein Grund, sie zu nehmen, weil sie neuer klingen.

Was ein Anwendungsentwickler darüber wissen muss

Du wirst als Auszubildende oder Auszubildender keine Systemarchitektur entwerfen. Du wirst aber sehr wahrscheinlich an einem Dienst arbeiten, der einer von vielen ist. Dafür ändert sich einiges an der täglichen Arbeit:

1. Deine Schnittstelle ist ein Versprechen. Andere Teams verlassen sich darauf. Ein Feld umbenennen ist kein Refactoring mehr, sondern ein Bruch. Deshalb: neue Felder hinzufügen ja, vorhandene entfernen oder umdeuten nein — jedenfalls nicht ohne neue Version. Das ist auch der Grund, warum die Adresse /api/**v1**/… heißt.

2. Fremde Dienste fallen aus. Immer. Nicht „falls", sondern „wann". Was dein Code deshalb braucht: einen Timeout bei jedem Aufruf (sonst wartest du ewig), eine Entscheidung, was ohne die fremde Antwort passieren soll, und einen Statuscode, der ehrlich ist — 503, wenn du gerade nicht liefern kannst.

3. Derselbe Aufruf kommt manchmal zweimal. Wenn eine Antwort ausbleibt, versucht der Aufrufer es erneut — obwohl deine Arbeit vielleicht schon getan war. Ein POST, der zweimal ankommt, darf nicht zwei Bestellungen anlegen. Das nennt man Idempotenz, und im Infoblatt Das REST-Paradigma steht, welche HTTP-Methoden sie von sich aus mitbringen und welche nicht.

4. Dein Dienst merkt sich nichts zwischen zwei Anfragen. Wenn drei Kopien deines Dienstes laufen, landet die zweite Anfrage vielleicht bei einer anderen als die erste. Alles, was zwischen zwei Anfragen erhalten bleiben soll, gehört in die Datenbank — nicht in ein Feld der Klasse.

5. Einstellungen kommen von außen. Datenbankadresse, Zugangsdaten, die Adresse des Nachbardienstes — nichts davon steht im Quelltext. Es kommt aus Umgebungsvariablen, damit dasselbe Image in Entwicklung, Test und Betrieb laufen kann.

6. Schreib Logs, an denen man eine Anfrage verfolgen kann. Wenn eine Bestellung durch vier Dienste läuft, will jemand später wissen, wo sie steckengeblieben ist. Dafür bekommt jede Anfrage eine Kennung, die mitgereicht und in jede Logzeile geschrieben wird.

Die Verbindung zu diesem Lernfeld

Alles, was du in den Tutorials baust, ist genau das Handwerkszeug, aus dem Microservices bestehen:

Was du gelernt hastWozu es im Verbund dient
REST-Endpunkte mit sauberen AdressenSo sprechen Dienste miteinander
Statuscodes mit BedeutungSo erfährt der Aufrufer, ob es geklappt hat — ohne den Rumpf zu lesen
DTOs statt EntitätenSo bleibt die Schnittstelle stabil, wenn sich die Datenbank ändert
OpenAPISo weiß das andere Team, was dein Dienst kann
Automatisierte TestsSo merkst du, dass du dein Versprechen brichst, bevor der andere es merkt
DockerSo wird dein Dienst vervielfacht, ohne dass jemand etwas installiert

Ein einzelner Spring-Boot-Dienst mit eingebautem Webserver, in einen Container gepackt: Das ist die übliche Bauform eines Microservice. Du baust sie bereits — nur eben bisher allein und nicht im Verbund.

Filme

Alle auf Deutsch:

FilmWofür
Was ist ein Microservice? (predic8)Der Einstieg: Nachteile des Monolithen, Aufteilung in Dienste
Monolithen vs. Microservices (the native web)Der direkte Vergleich beider Bauformen
Warum ein Monolith oft besser ist als Microservices (predic8)Die Gegenrede — sieh dir diesen als zweiten an
Warum der dritte Film wichtig ist

Nach zwei begeisterten Filmen bleibt leicht hängen: „Monolith alt, Microservices neu." Der dritte räumt das wieder ab — und erst mit beiden Seiten kann man die Frage beurteilen, statt sie zu glauben.

Das Wichtigste in Kürze

  • Ein Monolith ist eine Anwendung aus einem Stück. Das ist meistens die richtige Wahl.
  • Er wird eng, wenn viele Menschen gleichzeitig daran arbeiten und wenn nur ein Teil unter Last steht.
  • Entscheidend ist der Schnitt: waagerecht nach Technik, senkrecht nach Fachlichkeit. Senkrechte Streifen heißen Vertikalen oder Self-Contained Systems.
  • Ein Microservice ist eine Vertikale als eigener Prozess, mit eigener Datenhaltung, unabhängig auslieferbar, über das Netz erreichbar.
  • Der Preis: Netzwerk statt Methodenaufruf, keine gemeinsame Transaktion, viele Logs, mehr Betrieb.
  • Für dich heißt das: stabile Schnittstelle, Timeouts, Idempotenz, kein Zustand im Dienst, Einstellungen von außen.

Weiterlesen