💡
Microservices sind ein Design-Pattern in der Softwarearchitektur, das zum Ziel hat, durch kleinere Teams und konzentriertere Verantwortlichkeiten Kommunikationswege zu verkürzen und so zu einer geringeren Time-to-market zu gelangen. Die grundlegenden Ideen von Microservices gehen auf die Unix-Prinzipien zurück, die bereits 1978 von Doug McIlroy¹ formuliert wurden. Laut ihm sollten Programme so gestaltet werden, dass sie ...

... genau eine Aufgabe haben und diese dafür gut erfüllen,
... hintereinander geschachtelt werden können, sodass ein Programm die Eingabe für ein anderes Programm produziert,
... früh getestet und gegebenenfalls wieder aufgegeben werden können,
... Tools für wiederkehrende Aufgaben bei der Entwicklung nutzen.
Eigenschaften von Microservices

Microservices² werden durch die folgenden Eigenschaften charakterisiert:

  • Single Purpose: Wie bei den Unix-Prinzipien sollte ein Microservice genau eine Aufgabe gut erfüllen.
  • Encapsulation: Microservices haben das alleinige Eigentum an ihren Daten. Sie interagieren mit der Außenwelt über wohldefinierte Schnittstellen.
  • Ownership: Ein einzelnes Team (bestenfalls bestehend aus 5-9 Personen) ist verantwortlich für einen Microservice über seine gesamte Lebenszeit.
  • Autonomy: Das für den Microservice zuständige Team darf ohne Absprache zu jeder Zeit den Microservice bauen und deployen. Das Team ist frei in Implementationsentscheidungen.
  • Multiple Versions: Es ist möglich, dass zur gleichen Zeit verschiedene Versionen eines Microservices existieren.
  • Choreography: Es gibt kein zentralisiertes System, das einen Workflow orchestriert. Stattdessen ist jeder Microservice in der Lage sich selbstständig mit den Informationen zu versorgen, die er für seine Funktionalität braucht.
  • Eventual consistency: Eine kurzzeitige Inkonsistenz von Daten zwischen Microservices ist akzeptiert, solange die Daten schließlich wieder konsistent werden.

Herausforderungen von Microservices

Microservices sind durch ihre innere Einfachheit generell besser skalierbar und leichter und schneller zu ändern als Monolithen, in denen die gesamte Logik in einem einzelnen Programm enthalten ist. Durch die kleinen Teams fühlen sich (und sind) Microservice-Teams deutlich stärker für den Erfolg ihrer Microservices verantwortlich, was häufig zu besseren Ergebnissen und Entscheidungen führt. Microservices erleichtern Omnichannel-Lösungen durch geteilte Backend-Funktionalität, die von verschiedenen Benutzerschnittstellen konsumiert werden können.

Erhöhte Komplexität

Auf der Ebene einzelner Microservices schaffen wir uns durch den begrenzten Aufgabenbereich und die Kapselung eine einfache und schöne Welt, doch TANSTAAFL!³ Die innere Einfachheit von Microservices wird erkauft durch die erhöhte Komplexität in Bezug auf die Kommunikation zwischen Microservices sowie eine gewisse Redundanz: Da die Microservice-Teams vollständig für ihre Services verantwortlich sind (Ownership) und sie auch selbstständig deployen können (Autonomy), müssen sie technisch dazu in der Lage sein, dieses Deployment tatsächlich durchzuführen. Das schließt sowohl Kompetenzen in Bezug auf Cloud-Infrastruktur ein als auch die Ressourcen, die für den Betrieb der Infrastruktur notwendig sind.

Konkreter können wir uns einen Mailing-Service vorstellen, der dafür zuständig ist, Informationsmails über ein Produkt an Kunden zu versenden. Der Service muss im Laufe der Entwicklung gebaut und dann auch ausgeführt werden. Die dafür verwendeten Computer müssen die notwendige Konnektivität haben, um diese Aufgaben durchzuführen. Sobald der Service läuft, müssen andere Services dazu in der Lage sein, den E-Mail-Prozess in Gang zu setzen. Mögliche Wege hierfür sind beispielsweise ein Event-System, aus dem der Mailing-Service selbstständig (im Sinne der Eigenschaft Choreography) die für ihn relevanten Events herausfiltert und zu E-Mails weiterverarbeitet.

So ein Event-System oder Messaging-System muss aber zum einen bereitgestellt werden und zum anderen von verschiedenen Teams mit ihren jeweiligen Microservices genutzt werden können. Ein anderes denkbares Szenario wäre die Ansprache des Mailing-Services via REST-Schnittstelle. In diesem Fall muss der Client dazu in der Lage sein, über ein Netzwerk mit unserem Mailing-Service zu kommunizieren, und diese Verbindung muss so abgesichert sein, dass nur autorisierte Services Zugriff auf die Schnittstelle bekommen.

Authentifizierung & Autorisierung

Authentifizierung und Autorisierung ist typischerweise ein relativ komplexes Problem und sollte innerhalb des Unternehmens auch konsistent gehandhabt werden, um die Nachvollziehbarkeit zu erhöhen. Daher ist es empfehlenswert, wenn Microservice-Teams sich nicht eigenständig für dieses Thema verantwortlich sind.

Da zum einen mehrere Instanzen des Mailing-Service in gegebenenfalls verschiedenen Versionen koexistieren können (Multiple Versions) und zum anderen Microservice-Instanzen jederzeit neu deployed und insbesondere auch gestoppt werden können (Autonomy), ist es essenziell, dass relevante Daten (wie beispielsweise fertig zusammengestellte Mails, die noch nicht versendet werden konnten) instanzübergreifend und permanent gespeichert werden. Dafür sind Datenbanken und persistente Volumes nötig, die bereitgestellt und gewartet werden müssen. Auch Backups und Snapshots der Daten sollten existieren und regelmäßig getestet werden.

Für besondere Herausforderungen sorgt der Fehlerfall: Wenn irgendein Prozess im Gesamtsystem plötzlich nicht mehr funktioniert, ist es wichtig, über mehrere Microservices hinweg Fehleranalyse betreiben zu können. Hierfür ist eine konsistente Logging-Infrastruktur sowie Request Tracing und Alerting hilfreich. Natürlich muss auch für diese Lösungen Infrastruktur bereitgestellt werden.

Zusammenfassung

Die beschriebenen Herausforderungen zeigen, dass es einige Punkte gibt, an denen wir serviceübergreifend für Einheitlichkeit sorgen sollten. Manche dieser Punkte werden wir in diesem Artikel genauer unter die Lupe nehmen. Elemente der Softwarearchitektur, die (potenziell) für mehrere Bausteine der Architektur (in unserem konkreten Fall also Microservices) relevant sind, nennt man Querschnittskonzepte. Um Redundanz zu vermeiden, sollten solche Querschnittskonzepte in der übergreifenden Architekturdokumentation behandelt werden, unabhängig von den einzelnen Komponenten. Es ist wichtig, Querschnittskonzepte frühzeitig festzulegen, da eine Migration zu einem späteren Zeit viel Kapazität über alle Teams hinweg in Anspruch nimmt.

💡
Coming up
Im zweiten Teil gehen wir auf die Basis-Bausteine von Microservices ein, die sich nur wenig unterscheiden und daher zentral von einem Infrastrukturteam verwalten werden können.
Quellen:
¹ Doug McIlroy - https://archive.org/details/bstj57-6-1899/mode/2up
² Kelly Goetsch - https://commercetools.com/resources/booklet/microservices