Texte zu Büchern

Software Architecture: The Hard Parts – Zusammenfassung und Einordnung

Neal Ford, Mark Richards, Pramod Sadalage, Zhamak Dehghani, Software Architecture: The Hard Parts – Modern Trade-Off Analyses for Distributed Architectures (2021)

Lesestand

Einleitung

Fundamentals of Software Architecture gibt einen Überblick über Stile und die Rolle des Architekten. The Hard Parts geht dorthin, wo es wehtut: zu den Entscheidungen in verteilten Systemen, für die es keine bewährten Lösungen gibt, nur Abwägungen. Wie fein soll ein Dienst sein? Wem gehören welche Daten? Wie hält man Daten über Dienste hinweg konsistent? Wie eng darf ein Vertrag sein?

Autoren und Kontext

Neben Neal Ford und Mark Richards, den Autoren von Fundamentals, schrieben zwei Datenspezialisten mit: Pramod Sadalage, Mitautor von Refactoring Databases und NoSQL Distilled, und Zhamak Dehghani, die den Begriff Data Mesh geprägt hat. Das Buch erschien 2021, als viele Organisationen mitten in der Aufteilung ihrer Monolithen steckten und merkten, dass der Code der leichtere Teil war; die Daten waren der schwere.

Die Kernthese

Die Autoren beginnen mit einer ungewöhnlichen Feststellung: Viele Architekturentscheidungen sind so spezifisch für eine Organisation, dass es keine Best Practices gibt. Was es gibt, sind wiederkehrende Kräfte, die man erkennen und gegeneinander abwägen kann. Das Ziel des Buchs ist, Architekten zu befähigen, ihre eigene Abwägungsanalyse zu machen, statt nach fertigen Antworten zu suchen.

Daher der Untertitel: Modern Trade-Off Analyses for Distributed Architectures. Das erste Gesetz aus Fundamentals, alles ist eine Abwägung, ist hier das Programm.

Die Fallgeschichte

Durch das ganze Buch zieht sich eine erzählte Geschichte: Ein Elektronikhändler hat einen Kundendienst namens Sysops Squad, dessen Ticketsystem ein alter Monolith ist. Es ist langsam, fällt häufig aus, und Änderungen dauern lange. Ein kleines Team aus Architekten und Entwicklern soll es in eine verteilte Architektur überführen. Jedes Kapitel beginnt mit einer Szene, in der das Team vor einer schwierigen Entscheidung steht, und endet mit einem ADR, das die Entscheidung festhält.

Aufbau

TeilKapitelInhalt
Einführung1Entscheidungen ohne Best Practices, ADRs, Fitness Functions
I. Auseinandernehmen2–7Kopplung, Modularität, Zerlegung, Daten aufteilen, Granularität
II. Wieder zusammenfügen8–14Wiederverwendung, Datenbesitz, verteilter Datenzugriff, Abläufe, Sagas, Verträge, analytische Daten
Abschluss15die eigene Abwägungsanalyse

Mit rund 450 Seiten ist das Buch dicht; anders als Fundamentals geht es selten in die Breite, sondern fast immer in die Tiefe einer Entscheidung.

Kopplung erkennen

Das zweite Kapitel legt die begriffliche Grundlage für das ganze Buch. Es präzisiert das Architekturquant aus Fundamentals.

Statische und dynamische Kopplung

ArtBedeutungBeispiele
Statischwie Dienste verdrahtet sind: was ein Dienst braucht, um überhaupt zu starten und zu funktionierengemeinsame Datenbank, gemeinsame Bibliotheken, Verträge, Betriebssystem, Broker
Dynamischwie Dienste zur Laufzeit miteinander sprechensynchrone und asynchrone Aufrufe, Konsistenzanforderungen, Koordination

Das Architekturquant, präzisiert

Ein Architekturquant ist ein Teil des Systems, der

  • unabhängig auslieferbar ist,
  • hohe fachliche Kohäsion hat,
  • hohe statische Kopplung in sich trägt, also gemeinsam funktioniert,
  • und dessen Teile synchron dynamisch gekoppelt sind.

Der wichtigste Punkt: Zwei Microservices, die dieselbe Datenbank nutzen, bilden ein Quant, auch wenn sie getrennt ausgeliefert werden, weil beide ohne diese Datenbank nicht funktionieren. Und zwei Dienste, die sich synchron aufrufen, teilen während des Aufrufs ihre Verfügbarkeit und Leistung. Ein Monolith ist immer genau ein Quant; ein verteiltes System kann eines oder viele sein.

Drei Kräfte der dynamischen Kopplung

Die Autoren beschreiben dynamische Kopplung durch drei Dimensionen, die später die acht Sagas ergeben:

DimensionAusprägungen
Kommunikationsynchron oder asynchron
Konsistenzatomar oder letztendlich konsistent
Koordinationorchestriert oder choreografiert

Gründe für Modularität

Bevor man ein System aufteilt, soll man wissen, warum. Die Autoren nennen fünf Treiber, die man den Fachleuten gegenüber begründen kann:

TreiberWirkung
WartbarkeitÄnderungen bleiben in einem Teil, weniger Seiteneffekte
Testbarkeitkleinere Teile lassen sich vollständiger testen
AuslieferbarkeitTeile können unabhängig und häufiger ausgeliefert werden
Skalierbarkeit und ElastizitätTeile mit hoher Last werden einzeln skaliert
Verfügbarkeit und FehlertoleranzAusfälle bleiben auf einen Teil begrenzt

Die Autoren betonen, dass Architekten diese Treiber in geschäftlicher Sprache begründen müssen, etwa als schnellere Markteinführung, geringere Ausfallkosten oder bessere Zufriedenheit der Kunden. Sonst bleibt eine Aufteilung ein technisches Projekt ohne Rückhalt.

Einordnung

Das präzisierte Quant ist die vielleicht nützlichste Begriffsbildung des Buchs. Es erklärt in einem Satz, warum Newmans verteilter Monolith kein Fortschritt ist: Er hat viele Dienste, aber nur ein Quant. Die drei Dimensionen der dynamischen Kopplung sind eine Erweiterung der Konnaszenz aus Fundamentals.

Ein System zerlegen

Wie zerlegt man einen bestehenden Monolithen? Die Autoren stellen zwei Wege vor und eine Folge von Mustern für den ersten.

Ist der Code überhaupt zerlegbar?

Vor der Zerlegung lohnt ein Blick auf die Struktur. Die Autoren greifen Martins Kennzahlen auf, Abstraktheit, Instabilität und Abstand zur Hauptreihe, sowie die Kopplung zwischen Komponenten. Code, der als großer Matschklumpen ohne erkennbare Komponenten vorliegt, lässt sich nicht sinnvoll Komponente für Komponente zerlegen.

Zwei Wege

AnsatzVorgehenPasst, wenn
Komponentenbasierte ZerlegungKomponenten im Monolithen erkennen, schrittweise ordnen, zu fachlichen Diensten zusammenfassen und dann herauslösender Code eine erkennbare Struktur hat
Taktisches Forkenden ganzen Monolithen mehrfach kopieren und in jeder Kopie alles löschen, was der jeweilige Dienst nicht brauchtder Code keine Struktur hat; schnell, aber jeder Dienst trägt anfangs viel totes Gewicht

Taktisches Forken klingt grob, hat aber einen Vorteil: Teams können sofort unabhängig arbeiten. Die Aufräumarbeit folgt danach.

Sechs Muster für die komponentenbasierte Zerlegung

Das fünfte Kapitel ist eines der praktischsten des Buchs: eine Folge von sechs Schritten, jeder mit einer Fitness Function, die den Fortschritt messbar macht.

SchrittMusterInhalt
1Komponenten erkennen und bemessenalle Komponenten, also Pakete oder Namensräume, auflisten und nach Größe vergleichen; zu große aufteilen
2Gemeinsame Fachkomponenten sammelndoppelte fachliche Logik, etwa Benachrichtigungen an verschiedenen Stellen, zusammenführen
3Komponenten abflachenverschachtelte Pakete so ordnen, dass Code nur in Blättern liegt und es keine verwaisten Klassen in Elternpaketen gibt
4Abhängigkeiten bestimmenprüfen, wie stark Komponenten voneinander abhängen und ob eine Zerlegung machbar ist
5Komponentendomänen bildenKomponenten zu fachlichen Gruppen zusammenfassen, sichtbar in der Paketstruktur
6Domänendienste bildenjede Domäne als eigenen, groben Dienst herauslösen, also eine service-basierte Architektur

Erst danach, und nur wo nötig, werden Domänendienste weiter in Microservices geteilt.

// Fitness Function zu Schritt 1: keine Komponente größer als 10 % der Codebasis
@ArchTest
static void noComponentDominates(JavaClasses classes) {
    Map<String, Long> sizeByComponent = classes.stream()
        .collect(groupingBy(c -> componentOf(c.getPackageName()), counting()));
    long total = classes.size();
    sizeByComponent.forEach((component, size) ->
        assertThat(size * 100 / total)
            .as("Komponente %s", component)
            .isLessThanOrEqualTo(10));
}

Einordnung

Der Weg über Domänendienste als Zwischenschritt ist die Empfehlung aus Fundamentals, die service-basierte Architektur als pragmatischen Mittelweg zu nutzen, und Newmans Rat, grob anzufangen. Schritt 5 entspricht dem modularen Monolithen: Die fachliche Struktur wird erst im Code sichtbar, bevor sie verteilt wird.

Operative Daten aufteilen

Das sechste Kapitel, maßgeblich von Sadalage geprägt, behandelt den schwierigsten Teil jeder Zerlegung: die Datenbank.

Kräfte für und gegen die Aufteilung

Die Autoren führen hier ein Denkmuster ein, das sich durch das ganze Buch zieht: Zerlegungstreiber gegen Zusammenhaltstreiber.

Für die AufteilungInhalt
Änderungskontrolleeine Schemaänderung betrifft bei gemeinsamer Datenbank alle Dienste; getrennte Datenbanken begrenzen den Schaden
Verbindungsverwaltungviele Dienste mit eigenen Verbindungspools erschöpfen eine gemeinsame Datenbank
Skalierbarkeiteine Datenbank kann zum Engpass für alle Dienste werden
Fehlertoleranzfällt die gemeinsame Datenbank aus, fallen alle Dienste aus
Architekturquanteneine gemeinsame Datenbank verschmilzt alle Dienste zu einem Quant
Optimierung des Datenbanktypsverschiedene Daten passen zu verschiedenen Datenbanktypen
Für den ZusammenhaltInhalt
DatenbeziehungenFremdschlüssel, Trigger, Sichten und gespeicherte Prozeduren verbinden Tabellen
Datenbanktransaktionenwas atomar sein muss, gehört in eine Datenbank

Die Entscheidung ergibt sich aus der Abwägung dieser Kräfte für die konkreten Tabellen, nicht aus einem Grundsatz wie „jeder Dienst hat seine eigene Datenbank“.

Fünf Schritte

Für die Aufteilung beschreiben die Autoren einen schrittweisen Weg, angelehnt an Sadalages Refactoring Databases:

SchrittInhalt
1Datenbank analysieren und Datendomänen bilden, also Gruppen zusammengehöriger Tabellen
2Tabellen den Domänen zuordnen, etwa als getrennte Schemas in derselben Datenbank
3Datenbankverbindungen der Dienste auf ihre jeweiligen Schemas beschränken; Zugriffe auf fremde Schemas sichtbar machen und beseitigen
4Schemas auf getrennte Datenbankserver verschieben, zunächst mit Replikation
5auf die unabhängigen Server umschalten

Schritt 3 ist der entscheidende: Solange ein Dienst noch Tabellen einer anderen Domäne direkt liest, ist die Aufteilung nicht fertig. Diese Zugriffe müssen zuerst durch Aufrufe, Replikation oder Ereignisse ersetzt werden, wie die späteren Kapitel beschreiben.

Datenbanktypen

Das Kapitel bewertet außerdem acht Arten von Datenbanken, relational, Schlüssel-Wert, Dokument, Spaltenfamilie, Graph, NewSQL, Cloud-nativ und Zeitreihen, nach Kriterien wie Lernkurve, Modellierung, Skalierbarkeit, Konsistenz und Verfügbarkeit. Das ist eine knappe Fassung dessen, was Kleppmann ausführlich herleitet.

Einordnung

Die Unterscheidung von Kräften für und gegen die Aufteilung ist das zentrale Werkzeug des Buchs. Sie macht aus „Datenbank pro Dienst“, einer Regel aus Newmans Buch, eine Entscheidung, die man für jeden Fall abwägen kann.

Dienstgranularität

Wie groß soll ein Dienst sein? Statt einer Regel geben die Autoren zwei Gruppen von Kräften, die gegeneinander abgewogen werden.

die Kräfte der Dienstgranularität nach Ford, Richards, Sadalage und Dehghani

die Kräfte der Dienstgranularität nach Ford, Richards, Sadalage und Dehghani

Zerlegungstreiber

TreiberFrage
Umfang und FunktionTut der Dienst zu viele, nicht zusammenhängende Dinge?
ÄnderungshäufigkeitÄndert sich ein Teil viel häufiger als der Rest?
Skalierbarkeit und DurchsatzBraucht ein Teil eine ganz andere Skalierung?
FehlertoleranzSoll ein Teil weiterlaufen, wenn ein anderer ausfällt?
SicherheitBraucht ein Teil strengeren Schutz, etwa Zahlungsdaten?
ErweiterbarkeitKommen in einem Teil regelmäßig neue Varianten hinzu, etwa neue Zahlungsarten?

Zusammenhaltstreiber

TreiberFrage
DatenbanktransaktionenMüssen Änderungen in mehreren Teilen atomar sein?
Abläufe und ChoreografieRufen sich die Teile so häufig gegenseitig auf, dass Latenz, Fehleranfälligkeit und Komplexität steigen?
Gemeinsamer CodeTeilen die Teile so viel Logik, dass eine Trennung ständige Abstimmung erzwingt?
DatenbeziehungenSind die Daten so verflochten, dass eine Trennung viele Abfragen über Dienstgrenzen erfordert?

Das Vorgehen

Für jeden Kandidaten listet man die zutreffenden Kräfte auf beiden Seiten und gewichtet sie mit den Fachleuten. Ein Beispiel in der Art des Buchs, das solche Fälle mit der Fallgeschichte durchspielt: Ein Benachrichtigungsdienst für SMS, E-Mail und Brief soll aufgeteilt werden, weil neue Kanäle hinzukommen könnten, also Erweiterbarkeit. Dagegen spricht, dass die drei Kanäle dieselbe Logik zur Auswahl und Formatierung teilen, also gemeinsamer Code. Je nachdem, welche Kraft schwerer wiegt, bleibt es ein Dienst mit internen Erweiterungspunkten oder wird aufgeteilt.

Die Autoren betonen, dass dieselbe Analyse in einem anderen Unternehmen zum entgegengesetzten Ergebnis führen kann. Das ist der Kern des Buchs.

Einordnung

Die Zerlegungstreiber entsprechen den Kriterien, die Newman neben fachlichen Grenzen nennt, etwa Änderungshäufigkeit und Daten. Die Zusammenhaltstreiber sind Kohäsion im Sinne von Martins Common Closure Principle und Becks „Was sich gemeinsam ändert, gehört zusammen“.

Wiederverwendung

Teil II fragt, wie man Teile wieder zusammenbringt, nachdem man sie getrennt hat. Das erste Thema ist Code, den mehrere Dienste brauchen.

MusterStärkeSchwächePasst für
Code duplizierenvolle UnabhängigkeitÄnderungen müssen überall nachgezogen werdenstabilen, einfachen Code
Gemeinsame Bibliothekeine Stelle für Änderungen, keine LaufzeitkopplungVersionen laufen auseinander; Änderungen erzwingen Neuauslieferungentechnische Belange, eher kleine, feingranulare Bibliotheken
Gemeinsamer DienstÄnderungen sofort für alle wirksamLaufzeitkopplung: Latenz, Ausfallrisiko, Versionierung der Schnittstellefachliche Logik, die sich oft ändert
Sidecar und Service Meshtechnische Querschnittsbelange außerhalb des Codeszusätzliche InfrastrukturLogging, Sicherheit, Metriken

Die Autoren unterscheiden zwei Achsen: fachliche Wiederverwendung, die meist besser vermieden oder in einen Dienst ausgelagert wird, und technische, quer zu allen Diensten, für die Sidecars und Plattformen gedacht sind. Ihr Rat zu Bibliotheken: lieber mehrere kleine als eine große „Commons“-Bibliothek, die bei jeder Änderung alle Dienste betrifft.

Datenbesitz

Nach der Aufteilung muss jede Tabelle einem Dienst gehören. Die Autoren unterscheiden drei Situationen:

SituationBeschreibungLösung
Alleiniger Besitznur ein Dienst schreibt in die Tabelleeinfach: die Tabelle gehört diesem Dienst
Gemeinsamer Besitzviele Dienste schreiben, etwa eine Audit-Tabelleein eigener Dienst übernimmt die Tabelle; die anderen senden ihm Nachrichten
Geteilter Besitzwenige, meist zwei Dienste schreiben in dieselbe Tabelleeine von vier Techniken

Vier Techniken für geteilten Besitz

TechnikInhaltAbwägung
Tabelle aufteilenjeder Dienst erhält den Teil der Tabelle, den er schreibtklare Zuständigkeit, aber Synchronisation zwischen den Teilen
Datendomänebeide Dienste teilen ein Schemaeinfach, aber Kopplung bleibt
Delegierenein Dienst besitzt die Tabelle, der andere ruft ihn aufklare Zuständigkeit, aber Laufzeitkopplung
Dienste zusammenlegenbeide Dienste werden zu einemKonsistenz einfach, Dienst wird größer

Beim Delegieren gibt es zwei Varianten: Der Dienst mit der fachlichen Priorität besitzt die Daten, etwa der Katalog die Produktdaten, oder der Dienst mit dem höheren Schreibvolumen.

Verteilte Transaktionen und letztendliche Konsistenz

Ohne gemeinsame Transaktion sind Daten über Dienste hinweg nur letztendlich konsistent. Die Autoren beschreiben drei Wege dorthin:

MusterFunktionsweiseAbwägung
Hintergrund-Synchronisationein separater Prozess gleicht Daten periodisch abeinfach, aber verzögert; koppelt den Prozess an alle Datenbanken
Orchestriert, anfragebasiertein Koordinator ruft alle Beteiligten nacheinander synchron aufschnell konsistent, aber Fehlerbehandlung komplex
Ereignisbasiertein Dienst veröffentlicht ein Ereignis, andere aktualisieren ihre Datenentkoppelt, schnell; Fehlerbehandlung über Dead Letter Queues
// Ereignisbasierte Konsistenz mit Transactional Outbox
@Transactional
public void closeCustomerAccount(CustomerId id) {
    customers.markClosed(id);                                   // eigene Daten
    outbox.add(new CustomerClosed(id, Instant.now()));          // gleiche Transaktion
}
// Ein separater Prozess veröffentlicht die Outbox auf Kafka;
// Ticket- und Vertragsdienst entfernen den Kunden aus ihren eigenen Daten.

Einordnung

Die Wiederverwendungsmuster sind fast wörtlich das Kapitel über Architekturmuster aus der zweiten Auflage von Fundamentals. Die Techniken für geteilten Besitz sind eine Ausarbeitung von Evans‘ Context Map auf Datenebene, und die Outbox ist EIPs Transactional Client in heutiger Form.

Verteilter Datenzugriff

Wenn jede Tabelle einem Dienst gehört, wie liest ein Dienst dann Daten, die einem anderen gehören? Etwa der Wunschlistendienst den Produktnamen aus dem Katalog? Die Autoren vergleichen vier Muster:

MusterFunktionsweiseStärkeSchwäche
Dienstaufrufder Dienst fragt den Besitzer bei BedarfDaten immer aktuell, keine KopieLatenz, Laufzeitkopplung, Ausfallrisiko
Spaltenreplikationder Dienst hält eine Kopie der benötigten Spalten in eigener Tabelle, aktualisiert über Ereignisseschnell, unabhängigDaten können veraltet sein; Synchronisation nötig
Replizierter Cacheder Besitzer stellt seine Daten in einem verteilten In-Memory-Cache bereit, den andere nur lesenschnell, Besitz bleibt klarSpeicherbedarf, Startreihenfolge, Volumenbegrenzung
Datendomänebeide Dienste teilen ein Schema für diese Dateneinfach, konsistent, schnellKopplung über das Schema, gemeinsames Quant

Welches Muster passt, hängt von Datenmenge, Änderungsrate, Konsistenzanforderungen und Verfügbarkeitsansprüchen ab. Spaltenreplikation entspricht dem, was Kleppmann als abgeleitete Daten beschreibt.

Abläufe über Dienste

Das elfte Kapitel vertieft die Frage aus Fundamentals: Orchestrierung oder Choreografie?

OrchestrierungChoreografie
Fehlerbehandlungan einer Stelleverteilt
WiederherstellungKoordinator kennt den Zustandschwierig
Zustandsverwaltungzentralverteilt oder in der Nachricht
Reaktionsfähigkeit und SkalierbarkeitKoordinator wird Engpassbesser
KopplungKoordinator kennt alleDienste kennen nur Ereignisse

Die Faustregel der Autoren: Je komplexer ein Ablauf und je wichtiger Fehlerbehandlung, desto eher Orchestrierung. Je einfacher und je wichtiger Skalierbarkeit, desto eher Choreografie.

Zustand in choreografierten Abläufen

Ohne Koordinator weiß niemand, wo ein Ablauf gerade steht. Die Autoren beschreiben drei Lösungen:

MusterInhalt
Front Controllerder erste Dienst im Ablauf verfolgt den Zustand und beantwortet Statusanfragen
Zustandslose Choreografieder Zustand wird bei Bedarf durch Abfragen aller Beteiligten ermittelt
Stamp Couplingder Zustand reist in der Nachricht mit; jeder Dienst ergänzt seinen Teil

Semantische und Implementierungskopplung

Ein wichtiger Gedanke des Kapitels: Ein Ablauf hat eine semantische Kopplung, die aus der Fachlichkeit kommt und sich nicht wegarchitektieren lässt. Eine Bestellung braucht Zahlung, Lager und Versand, egal wie man die Dienste schneidet. Die Implementierungskopplung, also wie die Dienste diese Schritte konkret verbinden, kann man wählen. Gute Architektur bildet die semantische Kopplung ab, ohne zusätzliche Implementierungskopplung hinzuzufügen. Das ist Brooks‘ Unterscheidung von wesentlicher und zufälliger Komplexität, angewandt auf Abläufe.

Die acht transaktionalen Sagas

Das zwölfte Kapitel ist das bekannteste des Buchs. Es kombiniert die drei Dimensionen der dynamischen Kopplung, Kommunikation, Konsistenz und Koordination, zu acht Saga-Mustern und gibt jedem einen einprägsamen Namen.

NameKommunikationKonsistenzKoordinationCharakter
Epic Sagasynchronatomarorchestriertverhält sich wie ein Monolith; stark gekoppelt, einfach zu verstehen, schlecht skalierbar
Phone Tagsynchronatomarchoreografiertohne Koordinator, aber mit atomarem Anspruch; Fehlerbehandlung schwierig
Fairy Talesynchronletztendlichorchestriertübersichtlich, gut handhabbar; ein pragmatischer Standard
Time Travelsynchronletztendlichchoreografierteinfache Kette, gut für lineare Abläufe mit hohem Durchsatz
Fantasy Fictionasynchronatomarorchestriertatomar und asynchron widerspricht sich; komplex
Horror Storyasynchronatomarchoreografiertdie schlechteste Kombination: atomarer Anspruch ohne Koordinator und ohne Synchronität
Parallelasynchronletztendlichorchestriertgut skalierbar, Koordinator behält den Überblick
Anthologyasynchronletztendlichchoreografiertam lockersten gekoppelt, am besten skalierbar, am schwersten nachzuvollziehen

Die Lehren

  • Atomare Konsistenz in verteilten Systemen ist teuer und wird mit jeder weiteren Entkopplung schwieriger. Die Kombinationen mit atomarer Konsistenz und asynchroner Kommunikation sind fast immer schlecht.
  • Fairy Tale und Parallel sind für viele Geschäftsabläufe die vernünftigen Mittelwege: ein Koordinator für Übersicht und Fehlerbehandlung, letztendliche Konsistenz für Entkopplung.
  • Anthology passt für einfache Abläufe mit sehr hohem Durchsatz, bei denen Überblick weniger wichtig ist.
  • Teams landen oft unbeabsichtigt bei einer Horror Story: Sie bauen choreografierte, asynchrone Dienste und erwarten dann, dass alles atomar zusammenpasst.

Zustand statt Ausgleich

Die Autoren beschreiben eine Alternative zu Ausgleichsaktionen: Saga-Zustandsmaschinen. Statt bei einem Fehler alle Schritte zurückzunehmen, bleibt der Ablauf in einem definierten Zwischenzustand, etwa „Ticket angelegt, Zuweisung ausstehend“, und wird später fortgesetzt oder von Menschen geklärt. Das ist oft fachlich ehrlicher als ein Ausgleich, der so tut, als sei nichts passiert.

enum TicketSagaState { CREATED, ASSIGNED, ROUTED, COMPLETED, ASSIGNMENT_FAILED, ROUTING_FAILED }

record TicketSaga(TicketId id, TicketSagaState state, Instant updatedAt) {
    TicketSaga on(TicketEvent e) {
        return switch (state) {
            case CREATED  -> e instanceof Assigned ? next(ASSIGNED) : next(ASSIGNMENT_FAILED);
            case ASSIGNED -> e instanceof Routed   ? next(ROUTED)   : next(ROUTING_FAILED);
            case ROUTED   -> e instanceof Done     ? next(COMPLETED) : this;
            default       -> this;          // Fehlerzustände warten auf Klärung
        };
    }
    private TicketSaga next(TicketSagaState s) { return new TicketSaga(id, s, Instant.now()); }
}

Einordnung

Die acht Sagas sind der vollständigste Rahmen für verteilte Abläufe in dieser Reihe. Newman unterscheidet nur zwei Varianten, Hohpe und Woolf beschreiben die Bausteine, Kleppmann die Theorie der Konsistenz. Hier werden sie zu einem Entscheidungsraum zusammengeführt.

Verträge

Verträge sind die Form, in der Dienste Informationen austauschen. Die Autoren beschreiben sie als Spektrum von streng bis locker.

StrengLocker
Formexplizite Schemas: Protobuf, Avro, JSON Schema, typisierte KlassenSchlüssel-Wert-Paare, freie JSON-Strukturen
StärkeVerlässlichkeit, Typprüfung, Dokumentation, Werkzeugunterstützungleicht weiterzuentwickeln, geringe Kopplung
Schwächejede Änderung erfordert Versionierung und AbstimmungFehler fallen erst zur Laufzeit auf, Bedeutung ist implizit

Zwischen beiden Polen liegen verbraucherorientierte Verträge, wie Newman sie beschreibt: Der Anbieter darf sich frei ändern, solange er die Erwartungen seiner Verbraucher erfüllt.

Stamp Coupling

Ein verbreitetes Problem: Ein Dienst übergibt eine große Datenstruktur, etwa ein vollständiges Kundenprofil, obwohl der Empfänger nur zwei Felder braucht. Die Folgen: unnötige Bandbreite, und der Empfänger ist an Änderungen der ganzen Struktur gekoppelt. Die Autoren sehen Stamp Coupling aber nicht nur negativ: Zur Zustandsverwaltung in choreografierten Abläufen ist es nützlich, weil die Nachricht den Zustand des Ablaufs trägt.

Analytische Daten

Das vierzehnte Kapitel, maßgeblich von Dehghani geprägt, wechselt von operativen zu analytischen Daten: Berichte, Auswertungen, maschinelles Lernen. Es vergleicht drei Generationen.

AnsatzIdeeProblem
Data WarehouseDaten aus allen Systemen werden in ein zentrales, sorgfältig modelliertes Schema geladenzentrales Team als Engpass; enge Kopplung an die Quellsysteme; langsame Änderungen
Data LakeRohdaten werden zentral gesammelt und erst beim Lesen interpretiertohne Struktur und Verantwortung wird der See zum Sumpf
Data MeshFachdomänen stellen ihre analytischen Daten selbst als Produkte bereitorganisatorisch anspruchsvoll; erfordert Plattform und gemeinsame Regeln

Die vier Prinzipien von Data Mesh

PrinzipInhalt
Fachliche Verantwortungdas Team, das die Daten erzeugt, verantwortet auch ihre analytische Bereitstellung
Daten als Produktanalytische Daten haben Nutzer, Qualitätsziele, Dokumentation und Versionen wie ein Produkt
Selbstbedienungsplattformeine Plattform macht es Teams einfach, Datenprodukte zu bauen und zu betreiben
Föderierte, rechnergestützte Steuerunggemeinsame Regeln zu Sicherheit, Qualität und Interoperabilität werden automatisiert durchgesetzt

Ein Datenprodukt-Quant ist das analytische Gegenstück zum Architekturquant: ein unabhängig betriebenes Datenprodukt neben dem operativen Dienst einer Domäne.

Einordnung

Data Mesh ist Team Topologies, angewandt auf Daten: Stream-aligned Teams besitzen ihre Datenprodukte, ein Plattform-Team stellt die Selbstbedienungsplattform. Es ist auch die analytische Seite von Evans‘ Bounded Contexts. Kleppmann ist vorsichtiger: Er beschreibt eher die technischen Bausteine, auf denen solche Plattformen aufbauen.

Die eigene Abwägungsanalyse

Das letzte Kapitel fasst die Methode zusammen, mit der das ganze Buch arbeitet, damit Leser sie auf eigene Probleme anwenden können.

Drei Schritte

  1. Verflochtene Dimensionen finden: Welche Teile hängen zusammen und wie, statisch und dynamisch?
  2. Kopplungspunkte analysieren: Wo genau entsteht die Kopplung, und welche Kräfte wirken darauf?
  3. Abwägungen bewerten: Wie wirkt sich jede Option auf die betroffenen Architekturmerkmale aus?

Ratschläge

  • Qualitativ statt quantitativ: Für viele Architekturentscheidungen gibt es keine verlässlichen Zahlen. Eine ehrliche qualitative Analyse ist besser als scheinbar exakte Zahlen.
  • Vollständige, sich nicht überschneidende Listen: Optionen und Kriterien so formulieren, dass nichts fehlt und nichts doppelt zählt.
  • Die Kontextfalle vermeiden: Eine Abwägung, die anderswo richtig war, muss es hier nicht sein. Wer Optionen ohne den eigenen Kontext bewertet, kommt zu falschen Schlüssen.
  • Konkrete Szenarien durchspielen: Statt abstrakt zu argumentieren, typische fachliche Fälle durch jede Option schicken.
  • Das Ergebnis vor die Belege stellen: Fachleute wollen eine klare Empfehlung mit Begründung, nicht eine Wand aus Tabellen.
  • Vorsicht vor Evangelisten: Wer eine Technologie oder einen Stil ohne Nachteile darstellt, hat die Abwägung nicht verstanden oder verschweigt sie. Das ist die Folgerung zum ersten Gesetz aus Fundamentals.

Kritik und Einordnung

Stärken

  • Der Mut, keine Antworten zu geben: Statt Best Practices liefert das Buch Kräfte und Methoden. Das ist ehrlicher als die meisten Architekturbücher.
  • Daten im Zentrum: Die Kapitel zu Datenaufteilung, Besitz, Zugriff und Konsistenz füllen eine Lücke, die Newman und Fundamentals offenlassen.
  • Präzise Begriffe: Architekturquant, statische und dynamische Kopplung, semantische und Implementierungskopplung, die acht Sagas.
  • Die Fallgeschichte: Sie macht abstrakte Abwägungen anschaulich und zeigt, wie Teams Entscheidungen gemeinsam treffen und mit ADRs festhalten.
  • Data Mesh aus erster Hand.

Kritikpunkte

1. Anstrengend. Abwägungen ohne Auflösung sind anspruchsvoll zu lesen. Wer klare Empfehlungen sucht, wird unzufrieden sein.

2. Die Fallgeschichte trägt nicht immer. Die erzählten Szenen sind teils künstlich; manche Leser empfinden sie als Füllstoff.

3. Die Saga-Namen. Die einprägsamen Namen sind beliebt, aber nicht in der Branche etabliert. Außerhalb von Lesern des Buchs versteht man unter „Fairy Tale Saga“ nichts.

4. Wenig Code und Werkzeuge. Wie man Sagas, Outboxes oder Datenprodukte konkret baut, bleibt anderen Quellen überlassen.

5. Verteilte Systeme als Ausgangspunkt. Das Buch setzt voraus, dass man verteilt; die Frage, ob ein modularer Monolith reicht, kommt im Zerlegungsteil vor, danach kaum noch.

Gesamturteil

The Hard Parts ist das Buch für den Moment, in dem ein Team merkt, dass Microservices vor allem ein Datenproblem sind. Es baut direkt auf Fundamentals auf und ergänzt Newman und Kleppmann. Wer es gelesen hat, stellt bessere Fragen, auch wenn er die Antworten weiterhin selbst finden muss.

Bezüge zur Reihe

Thema in The Hard PartsBezug in der Reihe
Keine Best Practices, nur AbwägungenRichards und Ford: erstes Gesetz; McConnell; Ousterhout
Architekturquant präzisiertRichards und Ford; Accelerate: unabhängig auslieferbar; Newman
Statische und dynamische KopplungRichards und Ford: Konnaszenz; Beck: Kopplung relativ zur Änderung
Semantische und ImplementierungskopplungBrooks: wesentliche und zufällige Komplexität
Komponentenbasierte ZerlegungMartin: Komponentenprinzipien; Feathers; Newman: Strangler Fig
Domänendienste als ZwischenschrittRichards und Ford: service-basiert; Newman: grob anfangen
Daten aufteilen, DatendomänenSadalage: Refactoring Databases; Evans: Bounded Contexts
Zerlegungs- und ZusammenhaltstreiberMartin: Common Closure; Newman: andere Kriterien
WiederverwendungsmusterRichards und Ford, 2. Aufl.: Architekturmuster; Newman: gemeinsamer Code
Datenbesitz und DelegierenEvans: Context Map; Fowler PoEAA
SpaltenreplikationKleppmann: abgeleitete Daten
Outbox, EreignisseHohpe und Woolf: Transactional Client; Kleppmann
Acht SagasNewman: zwei Varianten; Hohpe: Starbucks; Kleppmann: Konsistenz
Strenge und lockere VerträgeNewman: Tolerant Reader, Pact; Hohpe und Woolf: Format Indicator
Data MeshTeam Topologies; Evans
ADRs und Fitness FunctionsNygard; Richards und Ford

Diskussionsfragen

  1. Quanten: Wie viele Architekturquanten hat euer System nach der präzisierten Definition, und wie viele Dienste?
  2. Daten: Teilen sich bei euch Dienste ein Schema oder Tabellen? Welche der vier Techniken für geteilten Besitz passt?
  3. Granularität: Welcher eurer Dienste ist zu groß oder zu klein? Welche Kräfte sprechen jeweils dafür und dagegen?
  4. Sagas: Welcher der acht Sagas entsprechen eure verteilten Abläufe? Gibt es eine Horror Story?
  5. Zustand oder Ausgleich: Behandelt ihr Fehler in Abläufen eher durch Ausgleichsaktionen oder durch Zwischenzustände, die Menschen klären?
  6. Verträge: Eher streng oder locker? Wie geht ihr mit Änderungen um?
  7. Analytische Daten: Data Warehouse, Data Lake oder Data Mesh: was trifft auf euer Unternehmen zu?
  8. Weiter: Als Nächstes Building Evolutionary Architectures, das Fitness Functions ausführlich behandelt.