Auf dieser Seite

Eine Plattform-Bibliothek für zehn Microservices: was hineingehört

Zehn Microservices bedeuten ohne gemeinsame Grundlage zehnmal Fehlerbehandlung, zehnmal Logging und zehnmal dasselbe Upgrade. Jede Lösung sieht ein wenig anders aus, und im Betrieb merkt man das spätestens, wenn eine Anfrage durch vier Services läuft und in jedem anders protokolliert wird.

Ich war anderthalb Jahre Hauptentwickler einer solchen gemeinsamen Bibliothek samt Parent-POM für Maven, auf der zehn fachliche Services aufsetzen. Hier steht, was sich bewährt hat.

Was hineingehört

In die Bibliothek gehört, was jeder Service braucht und was in jedem Service gleich sein muss:

Der Maßstab ist einfach: Ein neuer Service soll Beobachtbarkeit, Resilienz und Security geschenkt bekommen, ohne eigenen Code dafür.

Eine Regel, die viel Diskussion spart

Manche Dinge lassen sich nur zentral durchsetzen. Zum Beispiel diese:

@ExceptionHandler(ServerError.class)
ResponseEntity<ApiError> handle(ServerError error) {
    log.error("Unerwarteter Serverfehler", error); // jeder 500er wird zum Alarm
    return ResponseEntity.status(error.status()).body(ApiError.withoutInternals(error));
}

Jeder Fehler mit Status 500 erzeugt einen Log-Eintrag auf Fehlerstufe und damit einen Alarm. Niemand muss daran denken, es steht an einer Stelle.

Was nicht hineingehört

Die Versuchung ist groß, alles Gemeinsame in die Bibliothek zu legen. Zwei Dinge habe ich bewusst herausgehalten:

Fachlogik. Sobald die Bibliothek weiß, was eine Bestellung ist, hängen alle Services an einer fachlichen Entscheidung.

Die Anbindung eines bestimmten Systems. Die Anbindung des zentralen Backends lag zunächst in der Bibliothek. Ich habe sie in eine eigene Bibliothek ausgelagert, die heute der Großteil der Services nutzt. Services ohne diese Abhängigkeit bekommen sie so nicht mehr ungefragt mitgeliefert.

Releases ernst nehmen

Eine Bibliothek, von der zehn Services abhängen, ist ein Produkt mit Stakeholdern im eigenen Haus:

Der Hebel: ein Upgrade statt zehn

Der Nutzen zeigt sich beim großen Upgrade – damals zum Beispiel bei der Migration auf Spring Boot 3. Der Sprung von Spring Boot 2.7 auf 3.0 betrifft Namensräume, Security-Konfiguration und zahlreiche Abhängigkeiten. Er lief einmal in Bibliothek und Parent-POM. Die Services haben die neue Version übernommen und nur noch angepasst, was bei ihnen wirklich eigen war. Fünf weitere Upgrades bis 3.3 liefen genauso.

Ein Diagramm, das nicht veraltet

Ein Nebenprodukt, das ich nicht mehr missen möchte: Die Bibliothek erzeugt aus den konfigurierten Aufrufen automatisch ein Diagramm, welcher Service welchen anderen aufruft. Es stimmt immer, weil es aus derselben Quelle kommt wie das Verhalten: aus dem Code.

Mehr zum Hintergrund steht im Fallbeispiel.