Suche
Software schnell und nachhaltig entwickeln

Microservices: Agilität mit Architektur skalieren

Eberhard Wolff

© iStockphoto.com / marigold_88

Softwarearchitektur beschreibt die Aufteilung von Softwareprojekten in Module – aber die Architektur hat nicht nur Auswirkungen auf die Struktur der Software, sondern auch auf die Organisation der Projekte. Microservices machen sich das zu Nutze und sind so eine neue Hoffnung für produktive und nachhaltige Softwareentwicklung – gerade auch bei großen Teams.

Kleinere Teams können bekanntermaßen sehr effizient und effektiv agil arbeiten. Bei großen Teams ist das oftmals nicht mehr so einfach. Größere Projekte haben allerdings auch einen viel größeren Kommunikationsbedarf und eine komplexere Organisation. Das scheint unvermeidlich, denn mit der Größe des Projekts muss auch die Organisation wachsen, und damit steigt auch der Kommunikationsbedarf.

 

Conways Gesetz

Aber es gibt Möglichkeiten, diese Herausforderungen anders anzugehen. Basis ist das Gesetz von Conway von 1968. Es stellt einen interessanten Zusammenhang zwischen der Organisation und der Softwarearchitektur her und besagt, dass Organisationen, die Systeme modellieren, auf solche Modelle festgelegt sind, die der Kommunikationsstruktur dieser Organisationen entsprechen. Software besteht letztendlich aus Modellen und daher gilt das Gesetz gerade auch für die Softwareentwicklung. Es konnte bereits in verschiedenen Kontexten empirisch nachgewiesen werden.

Ein Beispiel für das Gesetz von Conway: Nehmen wir an, dass ein Softwareprojekt in ein Team für das GUI-Design, ein Team für die Geschäftslogik und ein Team für die Datenbank aufgeteilt wird (Abb. 1). Conways Gesetz besagt nun, dass dieses System der Organisationsform dieser drei Teams entsprechen wird – also wird es eine GUI-Komponente, eine Komponente für die Geschäftslogik und eine Komponente für die Datenbank geben. Natürlich kann es in diesen Komponenten noch weitere, feinere Aufteilungen geben. Aber eine Aufteilung ohne diese drei fundamentalen Komponenten ist nicht möglich.

Abb. 1: Technische Aufteilung von Teams und Komponenten

Abb. 1: Technische Aufteilung von Teams und Komponenten

In letzter Zeit rückt ein anderes Verständnis von Conways Gesetz in den Mittelpunkt: Die Organisation wird so gewählt, dass sie der Softwarearchitektur entspricht. Die Organisation des Projekts ist also ein Ergebnis der Softwarearchitektur. So wird die Umsetzung der Architektur erleichtert. Statt also Conways Gesetz als eine Begrenzung zu begreifen, wird es innerhalb dieses Ansatzes konstruktiv zur Umsetzung der Architektur genutzt.

Letztendlich ergibt sich ein Dualismus: Die Architektur und die Teamstruktur sind nur zwei Seiten derselben Medaille. Beide Komponenten beeinflussen sich so stark, dass sie eigentlich untrennbar sind.

Das wirft folgende Frage auf: Wenn die Organisation und Kommunikation großer Projekte so komplex ist, kann die Softwarearchitektur dieses Problem gegebenenfalls lösen?

Typisch ist eine Aufteilung von Organisation und Architektur in technische Schichten wie oben beschrieben – also GUI, Geschäftslogik und Datenbank. Damit geht aber ein Nachteil einher: Ein typisches fachliches Feature erfordert Änderungen in allen drei Schichten und damit auch Kommunikation zwischen den drei Teams. Schließlich wird jedes Feature an der Oberfläche sichtbar sein, Logik enthalten und auch Daten speichern. Dazu müssen nicht nur in der Software Schnittstellen geschaffen und erweitert werden – vor allem müssen sich die Teams koordinieren und abstimmen, um das Feature in den drei Schichten zu implementieren. Auch die zeitliche Koordination der Teams ist wichtig, damit die Änderungen gemeinsam getestet und ausgerollt werden können.

Für die schnelle Entwicklung der Software ist diese Struktur offensichtlich keine gute Wahl. Besser wäre eine Aufteilung nach fachlichen Belangen – idealerweise so, dass jedes Team Features unabhängig von den anderen Teams implementieren kann. In einem E-Commerce-Team könnte es beispielsweise Teams für die Bestellung, die Suche und die Registrierung geben (Abb. 2). Wenn nun jedes Team einen Product Owner hat, der fachliche Features definiert, können die Teams unabhängig voneinander neue Features implementieren und Produkte selbstständig weiterentwickeln.

Abb. 2: Fachliche Aufteilung von Teams und Komponenten

Abb. 2: Fachliche Aufteilung von Teams und Komponenten

Abhängigkeit durch Architektur

Ganz so einfach ist es aber nicht. Die Komponenten haben natürlich noch immer Schnittstellen. Eine Suche sollte idealerweise in einer Bestellung münden. Die Daten müssen dann entsprechend von der Suchkomponente zur Bestellkomponente weitergegeben werden. Um diese Übergabe umzusetzen, müssen sich die Teams erneut koordinieren und kommunizieren. In diesen Szenarien steigen also die Anforderungen an die funktionale Architektur: Wenn zwei Komponenten eng gekoppelt sind und von zwei unterschiedlichen Teams weiterentwickelt werden, sind Absprachen über die Schnittstellen notwendig, worunter die Geschwindigkeit der Entwicklung leidet. Die Teams müssen dementsprechend möglichst an unabhängigen Komponenten arbeiten, in denen sie ihre Funktionalitäten implementieren können. Wenn das in einem bestimmten Fall nicht möglich ist, müssen sich die Teams erneut abstimmen – oder die Änderung in der fremden Komponente selbst vornehmen. Beides führt zu einer Verlangsamung der Entwicklung.

Neben der funktionalen Unabhängigkeit spielt auch die Technik eine Rolle. Typischerweise müssen in einem System technische Absprachen getroffen und Basistechnologien ausgewählt werden. Eine einheitliche technische Basis vereinfacht die Implementierung und auch den Betrieb. Oftmals kommt es zudem vor, dass eine bestimmte Technologie nur in einer bestimmten Version genutzt werden kann. In Java-Systemen können beispielsweise verschiedene Versionen von Bibliotheken nicht ohne Weiteres gleichzeitig genutzt werden. Also muss ein bestimmter Stack definiert und durchgesetzt werden. Soll nun aber in dem Beispiel für die Suche eine spezielle Suchtechnologie eingesetzt werden, wird die Einführung dieser Technologie durch die notwendigen Abstimmungen erheblich verzögert. Ebenso ist es praktisch unmöglich, für die Suche einen komplett anderen Technologiestack mit einer anderen Programmiersprache und anderen Bibliotheken zu nutzen, um beispielsweise die notwendige Performance zu erreichen.

Und natürlich müssen bei einem komplexen System die unterschiedlichen Änderungen koordiniert werden, bevor sie in Produktion ausgerollt werden können. Dazu zählt das Testen der Systeme im Rahmen einer Continuous-Delivery-Pipeline (siehe: Wolff, Eberhard: „Continuous Delivery: Der pragmatische Einstieg“, dpunkt.verlag, 2010). Aufgrund der Größe des Systems wird der Durchlauf sehr lange dauern. Die Risiken eines Deployments können so hoch sein, dass die Organisation versucht, nur mit einigen wenigen Releases pro Jahr auszukommen.

Technische Abhängigkeiten minimieren

Es gibt Möglichkeiten, die technischen Abhängigkeiten weitgehend zu beseitigen, und so den technischen Koordinationsaufwand zu minimieren. Dazu muss die Architektur der Komponenten so gewählt werden, dass die Teams möglichst viel technische Freiheit haben und sich wenig koordinieren müssen. So können der Entscheidungsspielraum und die Produktivität der Teams optimiert werden – und diesem Umstand ordnet der Architekturansatz die Einheitlichkeit bei der Technologie und die damit verbundenen Vorteile unter.

Der TechnologieStack muss also sehr flexibel sein. Beispielsweise könnte jedes Team eine virtuelle Maschine liefern, auf der ihre Komponente läuft – oder SoftwarePakete für eine automatisierte Installation beispielsweise mit einem Linux-Package-Manager. Dadurch können Teams nahezu beliebige Technologien nutzen.

Außerdem können die Komponenten nun unabhängig voneinander in Produktion gebracht werden; wenn die Architektur zusätzlich Vorkehrungen trifft, sodass andere Komponenten mit neuen Versionen eine Komponente umgehen können. Beispielsweise müssen die Schnittstellen abwärtskompatibel bleiben.

Wenn die Komponenten klein gewählt werden, sinkt auch die Komplexität der Continuous-Delivery-Pipelines und die Risiken bei einem Ausrollen in Produktion. So wird die Installation neuer Features in Produktion weiter vereinfacht.

Dieser Ansatz wird momentan unter dem Begriff Microservices diskutiert – wobei es noch keine einheitliche Definition gibt. Wesentliche Aspekte sind das unabhängige Deployment der Microservices und die damit einhergehende Möglichkeit, Microservices getrennt voneinander zu entwickeln. So grenzen sie sich von Deployment-Monolithen ab, die nur als Ganzes in Produktion gebracht werden können. Das unabhängige Deployment ist ein sehr wichtiger Vorteil; sogar noch wichtiger als die technische Flexibilität. Die Teams müssen sich nun nicht einmal mehr abstimmen, um eine neue Version der Software in Produktion zu bringen. So können sie völlig unabhängig voneinander arbeiten und viel mehr Features umsetzten. Auf diese Weise wird ein agiles Entwicklungsmodell ideal unterstützt, bei dem es mit einem schnellen Umsetzten von Features nicht getan ist: Die Features sollen anschließend auch schnell in Produktion gebracht werden, sodass Feedback erhalten werden kann.

Dabei muss ein Team nicht unbedingt an nur einem Microservice arbeiten. In der Praxis unterteilen Teams große Komponenten wie das Abbilden des Bestellprozesses in mehrere kleine Microservices. Diese Services können alle von einem Team entwickelt werden, das für die Fachlichkeit dieser Services zuständig ist. Insgesamt können Systeme bis zu hunderte von Microservices haben. Letztendlich wird so aus dem großen Projekt zur Entwicklung der E-Commerce-Systeme eine Menge kleiner Projekte, die jeweils einen Teil des Systems umsetzten.

Eine Kommunikation zwischen den Microservices ist zwar nicht einfach, aber möglich, schließlich müssen sie zusammen unter anderem das E-Commerce-System abbilden. Die Kommunikation kann über REST oder Messaging erfolgen. Dann ruft die Logik eines Microservice die Logik eines anderen Microservice auf. Zudem kann ein Microservice auf seiner HTML-Oberfläche ein Link anzeigen, der auf einen anderen Microservice verweist. Außerdem können die Microservices Daten replizieren – wie es für Data Warehouses schon lange gang und gäbe ist (Abb. 3). Das bietet sich vor allem an, wenn die Daten in eine andere Repräsentation überführt werden müssen, wie dies beispielsweise notwendig ist, wenn die Daten analysiert werden sollen.

Abb. 3: Kommunikation der Microservices

Abb. 3: Kommunikation der Microservices

Nachhaltige Entwicklung

Microservices haben noch weitere Vorteile, letztendlich sind sie aber nur eine Modularisierungstechnik. Ein wichtiger Unterschied zu anderen Modularisierungsansätzen ist die Herstellung der Kommunkikation untereinander. REST-Aufrufe oder Messaging können nur unter der Voraussetzung eingesetzt werden, dass entsprechende Schnittstellen eingeführt und genutzt werden. Ebenso ist es schwierig, Funktionalitäten von einem Microservice in einen anderen zu verschieben. Solche Abhängigkeiten einzuführen oder Code zu verschieben, ist in einem Deployment-Monolithen mit einer modernen IDE sehr schnell erledigt – auch wenn diese Änderungen die Architektur torpedieren. Eine wesentliche Aufgabe von Architektur ist es, die Abhängigkeiten zwischen Modulen so zu managen, dass ein Abhängigkeitschaos vermieden wird und die Software somit leicht verstehbar und änderbar bleibt. Microservices erzwingen durch technische Barrieren, dass die Architektur auch langfristig durchgehalten beibehalten wird. Deployment-Monolithen sind viel eher der Gefahr ausgesetzt, dass die einmal gewählte Architektur irgendwann durchbrochen wird. Die Folge ist dann oft ein unwartbares System.

Ein weiteres Problem mit Deployment-Monolithen: Wenn der Monolith nicht mehr erweitert werden kann, weil die Architektur oder die Codequalität nicht mehr stimmt, müsste er eigentlich durch eine Neuimplementierung abgelöst werden. Aber das Ablösen einer großen, komplexen Anwendung ist sehr schwierig. Viele große Projektfehlschläge sind darauf zurückzuführen, dass ein Deployment-Monolith abgelöst werden sollte und der Aufwand für die Ablösung am Ende viel zu groß war. Microservices helfen hier auf zweierlei Weise: Sie können einen Deployment-Monolithen ergänzen. So ist es möglich, zunächst einige Funktionalitäten durch Microservices abzulösen. So können bestimmte Anfragen beispielsweise schon von den neuen Services bearbeitet werden, während die anderen noch immer in dem Monolith bearbeitet werden. Die Microservices können dabei einen neuen modernen Technologiestack nutzen und sind auch ansonsten nicht mit den Problemen des Monolithen belastet. Ein solcher Ansatz wird von zahlreichen Projekten genutzt, um den Monolithen schrittweise durch Microservices abzulösen. Dadurch können die Vorteile moderner Technologien sofort genutzt und die Microservices beispielsweise auch einfacher in einer Continuous-Delivery-Pipeline ausgeliefert werden.

Außerdem kann durch Microservices vermieden werden, dass Software irgendwann weder weiterentwickelt noch abgelöst werden kann: Wegen der Größe können Microservice sehr leicht durch neue Implementierungen ersetzt werden, die auch andere Technologien nutzen können. So wird das Ablösen einer Microservice-Architektur erheblich erleichtert. Genau genommen wird noch nicht einmal die Architektur abgelöst, sondern nur einzelne Teile ausgetauscht. Eigentlich ist es ein Fehler, dass Softwarearchitekturen die Ablösung eines Systems bisher nicht wirklich in Betracht gezogen haben – denn in der Praxis tauchen gerade bei der Ablösung immer wieder massive Probleme auf.

Durch die Durchsetzung der Architektur und die Ersetzbarkeit einzelner Microservices versprechen Microservices eine nachhaltige Entwicklung von Software. Auch langfristig sollte es also möglich sein, Features mit einer hohen Geschwindigkeit umzusetzen. Damit wird also ein weiteres klassisches Problem aus der Softwareentwicklung mithilfe dieser Softwarearchitektur gelöst.

Wer tut es schon?

Microservices sind aus der Welt der Internetunternehmen nicht mehr wegzudenken. Amazon beispielsweise hat bereits 2006 davon gesprochen, dass Teams jeweils für einen Service zuständig sind und diese Services unabhängig voneinander weiterentwickeln können. Viele Internetunternehmen nutzen einen ähnlichen Ansatz, denn gerade in diesem Bereich ist schnelles Time-to-Market für den Erfolg wesentlich.

Ein weiteres Beispiel ist Netflix. Der Videostreamingservice baut auf der Amazon Cloud auf und ist für bis zu 30 Prozent des Internet-Traffics in den USA verantwortlich. Für Netflix ist die Microservice-Architektur so wichtig, dass die Firma eine umfangreiche technologische Infrastruktur für die Implementierung von Microservices erstellt hat. Sie besteht aus verschiedenen Bibliotheken und steht mittlerweile als Open Source zur Verfügung. Auch ansonsten ist die Netflix-Architektur eines der wesentlichen Beispiele dafür, wie ein System aus Microservices aufgebaut werden kann.

Bei den internetbasierten Unternehmen sind außerdem Twitter oder innerhalb Deutschlands SoundCloud und Otto zu nennen, die solche Ansätze ebenfalls umsetzten. Ein eher klassisches Unternehmen ist der Finanzdienstleister Hypoport, der auch schon im Java Magazin über seine Erfahrungen berichtet hat. Aber auch die Deutsche Post (EPost) verfolgt einen solchen Ansatz.

Aktuell ist das Thema mit einem gewissen Hype versehen – vermutlich, weil immer mehr Unternehmen vor den Problemen stehen, die durch Microservices gelöst werden können. Schließlich ist die schnelle Implementierung von Features und die nachhaltige Implementierung von Projekten eine zentrale Herausforderung in der Softwareentwicklung.

Microservices – ein Allheilmittel?

Allerdings sind Microservices auch mit neuen Herausforderungen für Teams verbunden:

  • Die Vielzahl an Services benötigt eine Infrastrukturautomatisierung. Manuelles Aufbauen von Umgebungen oder manuelles Ausrollen in Produktion sind bei 100 oder mehr Microservices schlichtweg nicht mehr möglich. Daher müssen Microservices mit Continuous Delivery einhergehen. Continuous Delivery ist ohnehin sehr nützlich, wenn das Time-to-Market optimiert werden soll. Außerdem wird der Aufbau einer Continuous-Delivery-Umgebung mit Microservices einfacher, da die Dienste kleiner sind, zudem das Risiko bei einem Deployment in Produktion sinkt. Oft bildet Continuous Delivery sogar den Anreiz für Teams, die Architektur auf Microservices umzustellen.
  • Der Betrieb und das Monitoring der Services müssen außerdem gewährleistet sein. Wesentlich ist auch in diesem Bereich die Automatisierung und die Einheitlichkeit. Ohne solche Vorkehrungen ist der Betrieb einer Microservices-Architektur kaum zu stemmen. Die betrieblichen Aspekte wie auch die Continuous-Delivery-Pipeline müssen zentral vorgegeben und von allen Komponenten eingehalten werden, um eine Entwicklung des Systems zu gewährleisten.
  • Um Teams noch unabhängiger zu machen, sollten Microservices idealerweise auch mit DevOps kombiniert werden. So werden auch die betrieblichen Aspekte von den Teams übernommen und können unabhängig voneinander bearbeitet werden.
  • Microservices sind verteilte Systeme. Das macht die Implementierung der Systeme komplexer, da Kommunikation über das Netzwerk länger dauert und unzuverlässiger ist. Wenn die Architektur aber gut umgesetzt ist, bietet es auch noch weitere Vorteile. Beispielsweise können Microservice-Architekturen den Ausfall eines Service kompensieren – während das bei einem Monolithen durchaus zum Ausfall des Gesamtsystems führen kann.

Aktuell entstehen viele Technologien wie zum Beispiel der zuvor erwähnte Netflix-Stack, Spring Boot und Spring Cloud für Java oder auch Docker als leichtgewichtige Ablaufumgebung. Diese Technologien vereinfachen die Umsetzung von Microservices, sodass bei neuen Projekten wesentlich kleinere Investitionen in Technologien anfallen und ein geringeres Risiko besteht, als dies bei den Pionieren der Fall war. Auch das trägt sicher dazu bei, dass Microservices gerade populärer werden.

Fazit

Architekturen können ihren Teil dazu beitragen, agile Softwareentwicklung zu skalieren. Grundsätzlich wird mit Ansätzen wie Microservices die Software soweit entkoppelt, dass die Kommunikation und Abstimmung zwischen den Teams erheblich vereinfacht wird und die Prozesse so schließlich viel einfacher abgestimmt werden können. Diese Interaktion und mögliche Synergie zwischen Prozessen, Softwarearchitektur, Teamorganisation und Kommunikation wird viel zu selten betrachtet.

Die Idee, Microservices einzusetzen, ist zudem sinnvoll, da die meisten Systeme eher zu große Deployment-Einheiten haben. Einige Technologien drängen Teams geradezu in diese Richtung. So hat Java EE umfangreiche Möglichkeiten, auch große Deployments zu ermöglichen.

So können Microservices fundamentale Probleme lösen – nicht nur agile Softwareentwicklung im größeren Kontext, sondern auch den Umgang mit Legacy-Anwendungen und die nachhaltige Weiterentwicklung von Systemen. Dafür sind die technischen Hürden akzeptabel – wenn auch in der Praxis sicher nicht einfach zu lösen.

Geschrieben von
Eberhard Wolff
Eberhard Wolff
Eberhard Wolff ist Fellow bei innoQ und arbeitet seit mehr als fünfzehn Jahren als Architekt und Berater, oft an der Schnittstelle zwischen Business und Technologie. Er ist Autor zahlreicher Artikel und Bücher, u.a. zu Continuous Delivery und Microservices und trägt regelmäßig als Sprecher auf internationalen Konferenz vor. Sein technologischer Schwerpunkt sind moderne Architektur- und Entwicklungsansätze wie Cloud, Continuous Delivery, DevOps, Microservices und NoSQL.
Kommentare
  1. Wie Microservices die Anwendungsentwicklung revolutionieren - Think Progress DE2016-02-18 02:18:29

    […] 2006 davon gesprochen, dass Teams jeweils für einen Service zuständig sind und diese Services unabhängig voneinander weiterentwickeln können. Ein weiteres Paradebeispiel ist Netflix. Für den Videostreamingservice ist die […]

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.