Cloud-Migration von Legacy-Anwendungen (Webinar)

Innovation mit Microsoft Azure: Software Development

Natürlich hätten wir Sie viel lieber zu unseren beliebten Technical Councils ins SDX Office eingeladen; leider ist dies immer noch nicht möglich.

Herzlich Willkommen deshalb zu unserem Microsoft Azure Live Meeting “Cloud-Migration von Legacy-Anwendungen • Lift & Shift, Cloud-Optimized, Cloud-Native”.

Die Session ist kompakt auf remote-gerechte 60 Minuten ausgelegt und findet als Microsoft Teams-Event statt (per App oder Browser). Nach Anmeldung erhalten Sie die nötigen Zugangsdaten.

.

Agenda

  • Übersicht über mögliche Migrationspfade am Beispiel einer konkreten Legacy-Webanwendung
  • Lift & Shift: Migration der bestehenden Infrastruktur nach Azure
  • Cloud-Optimized: Modernisierung des Legacy-Systems mithilfe von Containern
  • Cloud-Native: Microservices und Container-Orchestrierung mit Kubernetes
  • Azure-Kostenvergleich und Optimierungsoptionen
  • Fazit: Entscheidungskriterien und Handlungsempfehlungen

.

Referenten

  • Dr. Till Rebenich, Chief eXpert
  • Lukas Momot, Principal eXpert

.

Zielgruppe und Level

  • Entscheider, Architekten und Entwickler
  • Level 200 – 300

.

Datum

Mittwoch, 02.12.2020 um 11:00-12:00 Uhr

.

Anmeldung

Anmeldung per E-Mail an Innovation@sdx-ag.de

.

Technologie pur – versprochen  🙂

Wer uns kennt, weiß, dass wir in unseren Vorträgen tatsächlich kein Marketing machen, sondern technisches Know-How kompetent weitergeben. Denn alle Speaker sind als Microsoft Technologie Spezialisten im Tagesgeschäft in entsprechenden Enterprise Projekten aktiv.
Melden Sie sich an und überzeugen sich selbst!

Unsere eXperts freuen sich auf Ihre Teilnahme und einen spannenden Austausch.

Gerne können Sie die Innovations-Themen individuell für Ihr Team buchen, weitere Infos auch unter Predictive Analytics • Cloud-Migration • .NET Core • Microservices • MAUI • Serverless.

Time to Market mit Microservices DevOps reduzieren

Ein Ziel der Digitalisierung ist neben der Fokussierung auf Business Value und digitale Wertschöpfungsketten die Verkürzung von Softwarereleasezyklen, um die Time to Market zu reduzieren. Microservices können einen wichtigen Beitrag leisten, wenn sie mit DevOps-Methoden kombiniert werden.

Dieser Beitrag ist der 4. Teil einer Artikelserie zum Thema “Microservices”:

  1. Microservices als Teil der Digitalisierungsstrategie
  2. Microservices fachlich adäquat schneiden
  3. Geschäftsprozesse mit Microservices abbilden
  4. Time to Market mit Microservices DevOps reduzieren

Automatisierung

Microservices sind autonom hinsichtlich Entwicklung, Änderung und Deployment. Deshalb lassen sie sich gut in einem automatisierten Continuous-Delivery-Prozess einbinden. Das Ziel ist, neue und geänderte Features schneller produktiv zu nehmen, ohne hierbei auf andere Services Rücksicht nehmen zu müssen. Weiterhin sollen zeitaufwändige, manuelle Prozesse vermieden werden. Die Automatisierung ist also der Schlüssel zur Reduzierung der Time to Market.

Bei Microservices DevOps wird der Betrieb (Operations) explizit mit einbezogen, wie im folgenden Schaubild dargestellt.

Microservices DevOps

Der DevOps-Zyklus hat grob folgende Bestandteile:

  • Continuous Integration (CI), d. h. automatisierte Builds und Unit Tests. Dies sollte in Softwareentwicklungsprojekten bereits zum Standard gehören. Ob der CI-Build ein potentielles Release generiert oder z. B. ein separater Nightly oder ein manueller Ad-Hoc-Build verwendet wird, ist dem Team überlassen.
  • Testing: Automatisches Deployment des Release Candidates auf Test-Hostingumgebung(en)
    • Automatische Ausführung von Acceptance Tests. Abhängigkeiten zu anderen Microservices werden durch Service Stubs ersetzt.
    • Automatische Ausführung von Integration Tests. Hierbei wird ein Microservice nach erfolgreichem Durchlaufen aller Acceptance Tests und vor jedem Release im Zusammenspiel mit anderen Microservices im produktionsnahen Betrieb getestet.
  • Release: Automatisches Ausrollen in den Produktionsbetrieb nach erfolgreichem Durchlaufen aller Tests.

Alle Bestandteile befinden sich in einer automatisierten Continuous Delivery Pipeline, die man z. B. mit Visual Studio Team Services abbilden kann.

Organisatorische Konstellationen

Je nachdem, wie ein Unternehmen aufgestellt ist, kann Microservices DevOps unterschiedlich ausgestaltet sein. Die im Folgenden beschriebenen Ansätze sind aufsteigend sortiert nach dem Grad der Gesamtverantwortung des Microservice-Teams.

Getrenntes Operations-Team

In großen Unternehmen ist das Betriebsteam in der Regel von der Entwicklung getrennt. Dies hat oft regulatorische oder historische Gründe. Es lohnt sich, diese Konstellation grundsätzlich in Frage zu stellen, wenn eine Umstellung auf Microservices angestrebt wird. Allerdings impliziert dies einschneidende, organisatorische Maßnahmen, die sich nicht auf die Schnelle umsetzen lassen.

Microservice können auch mit einem separaten Operations-Team betrieben werden, wenn man mit gewissen Einschränkungen hinsichtlich Time to Market und Effizienz leben kann. Praktisch könnte dies über Vereinbarungen geschehen, die zwischen Microservice-Teams und dem Betrieb getroffen werden. Die Zielsetzung ist hierbei, eine Automatisierung bestmöglich zu unterstützen:

  • Buildprozess, dessen Ergebnis einfach auszurollende Deployment-Pakete sind
  • Automatische Tests
  • Skriptbasiertes Deployment und Servicekonfiguration
  • Steuerung: automatisierbare Mechanismen zum Starten, Beenden und Anhalten von Services
  • Zentrales Logging, um das Monitoring im Betrieb zu ermöglichen und zu erleichtern

Idealerweise gibt der Betrieb das Deployment nur noch frei und der Rest geschieht vollautomatisch. Nicht festgelegt werden sollten Implementierungsdetails, z. B. zu verwendende Technologien und Frameworks. Diese liegen im Verantwortungsbereich des Microservices-Teams.

Platform as a Service (PaaS)

Bei diesem Ansatz stellt der Betrieb den Microservice-Teams eine Hosting- und Managementplattform bereit. Diese gibt die meisten der im vorherigen Abschnitt genannten Kriterien explizit vor bzw. standardisiert diese. Das Operations-Team regelt nur Zugriffswege und Berechtigungen der einzelnen Teams sowie ggf. die dem Service zugewiesenen Ressourcen. Es führt auch kontinuierliches Plattform-Monitoring und Wartungsaufgaben durch (z. B. Einspielen von Plattform-Softwareupdates etc.). Alles weitere, darunter fällt insbesondere auch das Deployment, liegt in der Verantwortung des jeweiligen Microservice-Teams.

Microservice PaaS

Ich persönlich finde diesen Ansatz am elegantesten, da er den beteiligten Personen Entscheidungen abnimmt, die Einhaltung grundlegender Regeln erzwingt und je nach Plattform stark auf Automatisierung ausgerichtet ist. Der Nachteil ist, dass man sich mit der Wahl einer Plattform zusätzlich einschränkt, deswegen sollte diese wohlüberlegt sein.

Beispiele für Microservice-PaaS sind Azure Service Fabric (auch on-premise einsetzbar), Docker als Containerization-Plattform und Kubernetes.

You Build it, You Run it

Dieser Ansatz geht am weitesten in Bezug auf die individuelle Verantwortung jedes einzelnen Microservice-Teams. Von außen gibt es keine konkreten Vorgaben hinsichtlich Betrieb der Microservices, sondern nur Empfehlungen. Das Team trifft alle Entscheidungen, wählt die Infrastruktur und/oder Plattform aus und ist selbst für das Hosting verantwortlich.

In großen Unternehmen mit vielen Microservices und großem Regulierungsdruck (Compliance) ist dieser Ansatz problematisch und schwer umsetzbar.

Fazit

Auf dem Weg hin zu Microservices DevOps müssen zunächst organisatorische Entscheidungen getroffen werden, die auf Automatisierung ausgerichtet sind. Dabei kann es sinnvoll sein, eine strikte Trennung zwischen Operations und Entwicklung aufzulösen. Am erfolgversprechendsten scheint die Verwendung von PaaS-Lösungen, bei dem sich der Betrieb auf die Bereitstellung der Microservices-Plattform beschränkt und alles weitere in der Verantwortung des jeweiligen Microservice-Teams liegt.

Geschäftsprozesse mit Microservices abbilden

Microservices orientieren sich an der Realität: Die meisten fachlichen Prozesse laufen nicht isoliert im luftleeren Raum ab, sondern leben durch die Interaktion von Menschen oder Gruppen mit einem gemeinsamen Verständnis der Fachlichkeit (Bounded Contexts). Jeder Microservice implementiert die Logik, die den Prozess innerhalb des jeweiligen Kontexts optimal unterstützt. Die beteiligten Microservices werden in ein Gesamtsystem integriert.

Dieser Beitrag ist der 3. Teil einer Artikelserie zum Thema “Microservices”:

  1. Microservices als Teil der Digitalisierungsstrategie
  2. Microservices fachlich adäquat schneiden
  3. Geschäftsprozesse mit Microservices abbilden
  4. Time to Market mit Microservices DevOps reduzieren

Microservice Integration Patterns

Bei der Abbildung von fachlichen, serviceübergreifenden Prozessen innerhalb eines Microservices-Systems können zwei gegensätzliche Patterns angewendet werden [1].

Orchestration

Beim Orchestration-Pattern erfolgt die Abwicklung des Prozesses über ein steuerndes Organ; das ist in der Regel ein Microservice. Der steuernde Service kapselt das Wissen über den Gesamtprozess, insbesondere über die Abfolge der einzelnen Prozessschritte sowie die jeweils beteiligten Microservices. Er triggert diese direkt an, um den jeweiligen Prozessschritt auszuführen. Dieses Prinzip ist mit dem eines Orchesters vergleichbar, bei dem der Dirigent den Takt vorgibt.

Microservices Orchestration

Orchestration hat den Vorteil der einfachen Umsetzbarkeit; der Koordinator (= Dirigent) redet direkt mit der API der ausführenden Services. Allerdings zahlt man dafür einen Preis:

  • Der Koordinator muss die ausführenden Services “kennen”. Hierdurch entstehen direkte Abhängigkeiten und damit eine enge Kopplung.
  • Die Logik des Gesamtprozesses ist im Koordinator implementiert. Ändert sich der Gesamtprozess, müssen häufig sowohl der Koordinator als auch die ausführenden Services angepasst werden.
  • Der Ansatz verleitet zur Verlagerung von mehr und mehr Logik in den Koordinator, wodurch dieser aufgebläht und seine Wartung erschwert wird. Der Koordinator muss sich auch um etwaige Fehler im Gesamtprozess kümmern, wenn z. B. einer der aufgerufenen Services nicht verfügbar ist.
  • Ist eine größere Anzahl an Microservices involviert, können sogenannte “Chatty Services” entstehen, bei denen zu viel direkte Kommunikation über das Netzwerk stattfindet – mit entsprechender Zunahme der Latenzzeiten.

Choreography

Anders als beim Orchestration-Pattern gibt es beim Choreography-Pattern in der Regel keine zentrale Steuerungsinstanz. Stattdessen reagieren die am Geschäftsprozess beteiligten Services jeweils auf bestimmte Ereignisse im Prozess. Tritt ein Ereignis ein, wird dies über ein zentrales Nachrichtensystem an alle beteiligten Services kommuniziert. Das ist vergleichbar mit einem Ballett: die Tänzer orientieren sich an den Bewegungen der anderen und passen sich so an, dass ein synchroner Gesamteindruck entsteht.

Disclaimer:
Gemeint sind nicht Nachrichtensysteme à la Enterprise Service Bus (ESB), sondern Message-Broker-Systeme, die sich ausschließlich auf die Verteilung von Nachrichten konzentrieren. Das bedeutet: keine Businesslogik, keine Validierung und keine Transformationslogik auf dem Bus!

Microservices Choreography

Choreography hat den Vorteil, dass die beteiligten Microservices maximal entkoppelt werden. Es gibt keinen zentralen Koordinator, sondern die Koordination erfolgt ausschließlich über Nachrichten. Einzelne Services bekunden ihr Interesse an bestimmten Typen von Nachrichten durch eine Subscription auf dem Nachrichtensystem.

Auch Choreography hat ihren Preis:

  • Das Nachrichtensystem ist das Rückgrat und muss entsprechend robust ausgelegt sein. Besonderes Augenmerk liegt auf der zuverlässigen Zustellung von Nachrichten und einem konsistenten Verhalten im Fehlerfall. Moderne Nachrichtensysteme garantieren, dass Nachrichten Systemausfälle “überleben”.
  • Das Wissen über den Gesamtprozess ist auf die am Prozess beteiligten Microservices verteilt. Es gibt keine zentrale Instanz, die dieses Wissen vorhält. Entsprechend aufwändiger gestalten sich Integrationstests und Fehlersuche.

Welches Pattern ist “besser”?

Die Antwort auf diese Frage ist “It Depends!”. Beide Patterns haben ihren Platz. Am Ende hängt die Wahl von verschiedenen Faktoren ab:

  1. Anzahl der beteiligten Microservices: Je höher die Zahl der beteiligten Services, desto höher die Gefahr, dass bei Orchestration zu viel Logik beim Koordinator implementiert ist und “Chatty Services” entstehen. In diesem Fall ist Choreography als Pattern zu bevorzugen.
  2. Komplexität des fachlichen Prozesses: Je komplexer der fachliche Prozess, desto mehr sollte der Fokus auf Entkopplung, d. h. Choreography, liegen.
  3. Verfügbarkeit von Messaging-Infrastrukturen: Choreography setzt eine Messaging-Infrastruktur (Message Broker) voraus. Ist eine solche nicht vorhanden, ist das Pattern schwer umsetzbar.
  4. Entwicklerhintergrund und -erfahrung: Choreography erfordert ein Umdenken bei Entwicklern, da die Kommunikation komplett über asynchrones Messaging stattfindet. Dies muss bei der Implementierung berücksichtigt werden.
  5. Nichtfunktionale Anforderungen: Bei hohen Anforderungen hinsichtlich Verfügbarkeit und Antwortverhalten ist Choreography besser geeignet.

Ich persönlich bevorzuge das Choreography-Pattern, da es mir bestmögliche Entkopplung und Serviceautonomie ermöglicht. Die gemischte Verwendung von Orchestration und Choreography ist grundsätzlich möglich, allerdings rate ich aus Konsistenzgründen davon ab! Je größer das Gesamtsystem, desto schwieriger wird es, den Überblick darüber zu behalten, wo welches Pattern eingesetzt wurde. Das erschwert die Wartbarkeit.

Datenintegration

Microservices folgen dem “Share Nothing”-Prinzip. Das bedeutet unter anderem, dass Microservices jeweils auf ihrer eigenen Datenbank arbeiten und diese nicht mit anderen Microservices teilen. Eigene Datenbank bedeutet hierbei nicht, dass man eine separate DBMS-Instanz pro Service hosten muss. Eine logische Trennung ist ausreichend. Alternativ können auch Datenbanken unterschiedliche Arten von DBMS je Microservice eingesetzt werden.

Wie werden nun aber “gemeinsame” Daten zwischen Services ausgetauscht?

Hier gibt es je nach Integrations-Pattern zwei Möglichkeiten:

  1. Kommt Orchestration als Integrations-Pattern zum Einsatz, so muss eine Datenänderung, die potentiell andere Microservices betrifft, mittels direkter Aufrufe kommuniziert werden.
  2. Bei Choreography wird eine Datenänderung als Event im zentralen Messaging-System publiziert. Interessierte Microservices subscriben sich auf die Änderung und können ihrerseits darauf reagieren.

Jeder Service übernimmt nur die Daten, die er für die Abbildung der Fachlichkeit innerhalb seines Bounded Contexts tatsächlich benötigt. Bezogen auf das Beispiel aus meinem letzten Beitrag: Time Recording übernimmt den Namen des Kunden (Customer), während Billing nur dessen Rechnungsadresse und Zahlungsmodalitäten abgreift.

Generell gilt: Microservice-Systeme sind verteilte Syteme! Diese sind in aller Regel nicht konsistent im Sinne von ACID, sondern folgen dem BASE-Prinzip. Das Stichwort lautet “Eventual Consistency“. Eine Änderung wird an alle Teilnehmer des Systems propagiert. Nach einer gewissen Zeit sind alle Teilnehmer in Bezug auf die Änderung konsistent. Die Dauer richtet sich nach den Anforderungen des Gesamtprozesses. Verteilte ACID-Transaktionen sind aufgrund ihrer Nachteile keine Option!

Eventual Consistency orientiert sich an der Realität. Die meisten Prozesse im richtigen Leben (auch Banküberweisungen (!)) laufen nach diesem Prinzip ab.

Fazit

Für die Abbildung von Geschäftsprozessen müssen in der Regel mehrere Microservices in einem integrierten Gesamtsystem zusammenarbeiten. Hierfür stehen zwei Patterns zur Verfügung: Orchestration mit zentraler Prozesssteuerung und Choreography mit nachrichtenbasierter Kommunikation. Da Microservice-Systeme verteilte Systeme sind, kommt bei beiden Patterns Eventual Consistency zum Einsatz.

[1] Sam Newman, Building Microservices – Designing Fine-Grained Services, O’Reilly, 2015<

Microservices fachlich adäquat schneiden

Der Schnitt der Servicegrenzen spielt eine wichtige Rolle, denn dieser beeinflusst Faktoren wie Entkopplung, Kommunikation zwischen Services und damit Service-Autonomie und Komplexität. Wie geht man also am besten vor? Welche Methoden und Fallstricke gibt es?

Dieser Beitrag ist der 2. Teil einer Artikelserie zum Thema “Microservices”:

  1. Microservices als Teil der Digitalisierungsstrategie
  2. Microservices fachlich adäquat schneiden
  3. Geschäftsprozesse mit Microservices abbilden
  4. Time to Market mit Microservices DevOps reduzieren

Microservices = Fachlichkeit!

Im letzten Beitrag dieser Serie bin ich auf Microservices allgemein und deren Einsatz als Baustein der Digitalisierungsstrategie eingegangen. Microservices fokussieren auf einen klar abgegrenzten fachlichen Block innerhalb der Fachdomäne, in der man sich bewegt. Jeder Service sollte einen dieser Blöcke als autonomen Softwarebaustein (Self-Contained System) abbilden. Ein Microservices-Team bestehend aus Domänenexperten, Entwicklern und Betrieb übernimmt dabei idealerweise die fachliche und technische Gesamtverantwortung. Die Frage ist jetzt, wie man einen “fachlichen Block” identifiziert!

Ausgangslage

Je nachdem, woher man kommt, unterscheidet sich die Strategie zum Aufspüren der fachlichen Blöcke:

  1. Bei vorhandenem Monolithen ist die Gesamtfachlichkeit der Domäne (oder Teile davon) oft unter einem Dach vereint. Der Vorteil ist, dass alle Beteiligten bereits ein gutes fachliches Verständnis von der Domäne haben. Je nachdem, ob und wie bei der Entwicklung Modularisierung (Separation of Concerns) betrieben wurde, können Module als Grundlage für die Bildung fachlicher Blöcke dienen.
  2. Wenn man auf der “grünen Wiese” beginnt, ist es etwas komplizierter. Vorhandene Organisationsstrukturen können ein erster Indikator für einen fachlichen Schnitt sein. Letztendlich entscheidet aber der Dialog zwischen Fachanwendern (Domain Experts) und Entwicklern bzw. das darüber entwickelte gemeinsame Verständnis. Die Methodik, die mich am meisten überzeugt, ist Domain-Driven Design (DDD). Kombinieren kann man DDD zum Beispiel mit Event Storming; das verhindert ein zu frühzeitiges Fokussieren auf Daten, indem das Hauptaugenmerk zunächst auf fachliche Ereignisse gelegt wird.

Wichtig: Egal, woher Sie kommen, fangen Sie nicht mit einem Datenmodell an! Datenmodelle eignen sich nicht dafür, den richtigen fachlichen Schnitt für einen Microservice zu finden. Sie sind ein Implementierungsdetail!

Bounded Contexts

Der ideale Kandidat für einen Microservice ist ein Bounded Context im Sinne von DDD. Ziel ist die Erreichung hoher fachlicher Kohäsion bei gleichzeitig maximaler Entkopplung zwischen den Services. Aber was genau ist ein Bounded Context?

In hinreichend komplexen Fachdomänen verwenden Personen aus unterschiedlichen Abteilungen oder Gruppen innerhalb des Unternehmens oft unterschiedliche Begrifflichkeiten, um das vermeintlich selbe zu beschreiben. Bildlich gesprochen werden Dinge, Konzepte oder Prozesse innerhalb der Organisation aus unterschiedlichen Blickwinkeln und mit unterschiedlichem Detailgrad gesehen. Eine Vereinheitlichung in einem allumfassenden Softwaremodell würde zur Verwässerung der Bedeutung des betrachteten Konzepts im Kontext der jeweiligen Abteilung führen. Dies sollte vermieden werden! Die Grenzen des jeweiligen Bounded Context werden also dort gezogen, wo diese Bedeutungsnuancen auftreten.

Beispiel

Bounded Contexts sind sicherlich ein auf den ersten Blick schwierig zu greifendes Konzept. Deswegen an dieser Stelle ein kleines Beispiel:

Stellen Sie sich einen Dienstleister vor, der IT-Consulting-Dienstleistungen anbietet; die Domäne des Unternehmens nennen wir deshalb beispielhaft “IT-Consulting-Dienstleister”. Im Unternehmen gibt es zunächst Personen, die sich mit der Akquise von Aufträgen beschäftigen; klassischerweise der Vertrieb und/oder der Verkauf. Gleicherweise gibt es dort Menschen, welche sich um die Abwicklung von Aufträgen kümmern (Service Delivery). Wieder andere erbringen die eigentliche Consulting-Dienstleistung beim Kunden vor Ort. Schließlich müssen die erbrachten Leistungen abgerechnet werden; darum kümmern sich Mitarbeiter der Abrechnung.

Microservices Bounded Contexts

Bedeutungsnuancen finden

Bringt man Menschen in unserem Beispiel-Unternehmen zusammen, kristallisieren sich in Diskussionen über fachliche Zusammenhänge und Prozesse Bedeutungsnuancen heraus, zum Beispiel:

  • Mitarbeiter des Vertriebs sprechen von Kunde im Kontext von Kontakten und Entscheidern, mit denen sie dort in Verbindung stehen.
  • Mitarbeiter der Projektabwicklung (Service Delivery) sprechen von Kunde, wenn Sie Projektverantwortliche und/oder Mitarbeiter auf Kundenseite meinen.
  • Projektmitarbeiter sprechen von einem Projekt, das sie bei Kunde X mit einer bestimmten Laufzeit durchführen. Sie erfassen die dort erbrachten Leistungen im Kontext des Projekts.
  • Die Abrechnung interessiert vor allem, wie die Rechnungsadresse von Kunde X lautet und welche Abrechnungsmodalitäten für die erbrachten Leistungen gelten.

Der Kunde hat für jede dieser Personengruppen also eine unterschiedliche Bedeutung! Ferner sind auch je nach Personengruppe unterschiedliche Attribute des Kunden relevant. Das gleiche gilt für diverse andere Konzepte, z. B. Mitarbeiter. Beispielhaft ist dieses Prinzip in der folgenden Abbildung skizziert. Eine Entität kann natürlich über ihren Schlüssel kontextübergreifend identifiziert werden.

Bounded Contexts für die Fachdomäne "Consulting-Dienstleistung" als Grundlage für Microservices

 

 

Über diese Bedeutungsunterschiede kristallisieren sich unterschiedliche fachliche Konzepte von bestimmten Abläufen im Unternehmen heraus. Das jeweilige fachliche Konzept wird Domain Model genannt und definiert den Bounded Context. Häufig existiert eine 1:1-Beziehung zwischen einer klassischen Unternehmensabteilung und einem Bounded Context. Das muss aber nicht so sein.

Fachlicher Schnitt

Es empfiehlt sich, für jeden identifizierten Bounded Context mit einem Microservice zu starten, der das jeweilige fachliche Konzept implementiert. Kristallisieren sich im späteren Verlauf weitere Bounded Contexts heraus, kann der Schnitt iterativ verfeinert werden. Folgende Punkte sind dabei zu beachten:

  1. Initial lieber mit gröberen Schnitten arbeiten. Diese dann iterativ verfeinern, sofern notwendig.
  2. Technisch getriebene Serviceschnitte vermeiden.
  3. Nanoservices als Anti-Pattern: zu fein geschnittene Services führen zu künstlicher Auftrennung von zusammengehöriger Fachlogik (Verletzung des Cohesion-Prinzips) und damit zu höheren Latenzzeiten, da mehr Kollaboration über das Netzwerk stattfindet.
  4. Umgekehrt führen zu grob geschnittene Services zu “verteilten Monolithen”.

Alternativen?

Natürlich können die Grenzen von Microservices theoretisch auch auf andere Arten gezogen werden. Ideen, die man hierzu findet, sind vielfältig; deshalb hier ein paar Beispiele:

  • Aufteilung von Microservices anhand von Codemetriken, z. B. Lines of Code (LoC)
  • Technische Auftrennung, z. B. horizontal nach Schichten
  • Aufteilung nach geschätztem Entwicklungsaufwand, z. B. nicht länger als eine Woche Entwicklungszeit pro Microservice

Gleich vorweg: Ich persönlich rate von all diesen Ansätzen ab! Die genannten Kriterien konzentrieren sich zu stark auf die Größe der Microservices und weniger auf deren fachlichen Nutzen. Die Gefahr, die darin liegt: Durch die mehr oder weniger “künstliche” Trennung erhöht sich nicht nur der teamübergreifende Abstimmungsaufwand, sondern auch die zwischen den Services laufende Kommunikation. Dies wiederum wird in der Summe nicht zu einer zufriedenstellenden User Experience führen. Zusätzlich entsteht eine enge Kopplung zwischen Services, was wiederum die Vorteile des Microservices-Ansatz insgesamt zunichte macht.

Offene Fragen

Der fachliche Schnitt ist fertig, aber offene Fragen bleiben: Wie verhält es sich bei Geschäftsprozessen, die serviceübergreifend stattfinden, d. h. über zwei oder mehr Bounded Contexts? Wie gehe ich mit “gemeinsamen” Daten abseits der oben beschriebenen Bedeutungsnuancen um? Diese und andere Themen werden in entsprechenden Folgebeiträgen näher beleuchtet.

Fazit

Microservices dienen in erster Linie der Umsetzung von Fachlichkeit, um für das Unternehmen einen Business Value zu erzeugen. Dies geschieht durch Identifikation der Bounded Contexts, die sich über Bedeutungsnuancen innerhalb der Fachdomäne ermitteln. Wichtig ist, dass keine künstlichen oder technisch getriebenen Servicegrenzen definiert und Services nicht zu feingranular geschnitten werden.

Microservices als Teil der Digitalisierungsstrategie

Die digitale Transformation stellt Unternehmen vor viele Herausforderungen. Zentrales Augenmerk liegt unter anderem auf Time to Market und Effizienzsteigerung. Lösungen sollen schneller am Markt sowie im Unternehmen platziert und Wettbewerbsvorteile für das Unternehmen gesichert werden. Gleichzeitig taucht in diesem Kontext das Stichwort “Microservices” immer häufiger auf. Was genau verbirgt sich dahinter und wie kann man Microservices erfolgreich als Baustein einer Digitalisierungsstrategie nutzen?

Dieser Beitrag ist der Auftakt einer Artikelserie zum Thema „Microservices“:

  1. Microservices als Teil der Digitalisierungsstrategie
  2. Microservices fachlich adäquat schneiden
  3. Geschäftsprozesse mit Microservices abbilden
  4. Time to Market mit Microservices DevOps reduzieren

Digitalisierung

Digitalisierung ist ein vielschichtiges und je nach Unternehmen individuelles Thema. Im Zentrum steht aber das Ziel, die Time to Market für Lösungen oder Produkte signifikant zu reduzieren, um sich dadurch Wettbewerbsvorteile am Markt zu verschaffen. Ein weiterer Aspekt ist die Steigerung der Effizienz von Geschäftsprozessen. Dies gilt für die Weiterentwicklung bestehender Lösungen genauso wie für neue, innovative Lösungen, die auf Basis von permanentem Input durch Kunden und Mitarbeiter entwickelt werden. Im Kontext dieses Beitrags beschränken wir uns auf digitale Lösungen. Das sind Anwendungen, die digitale Technologie gewinnbringend zum Zwecke der Marktdifferenzierung außerhalb und Effizienzsteigerung innerhalb des Unternehmens einsetzen. Zu den Zielen der Digitalisierungsstrategie im Unternehmen siehe auch die unten stehende Grafik.

Microservices als Teil der Digitalisierungsstrategie

Microservices

Um zu verstehen, wie Microservices den Prozess der Digitalisierung im Unternehmen unterstützten können, muss man diese zunächst grob erklären. Microservices werden oft nur auf technischer Ebene betrachtet. In Wahrheit sind diese fachlich getrieben; die Technik steht an zweiter Stelle.

Ein Microservice ist demnach, grob gesprochen, die Abbildung einer klar abgegrenzten Fachlichkeit innerhalb eines autonom entwickelten und betriebenen Softwarebausteins, der nach außen eine API für leichtgewichtige Kommunikationszwecke bereitstellt. Das bedeutet, dass der Fokus auf dem durch den Service bereitgestellten Business Value liegt, denn damit verdient das Unternehmen sein Geld. Wie der Service technisch umgesetzt ist, folgt einer geringen Anzahl allgemeiner Regeln, die seine Entkopplung von anderen Services (und damit anderer fachlicher Zusammenhänge) sicherstellen sollen:

  1. Ein Microservice ist autonom, d. h. unabhängig von anderen Services änderbar, wartbar, deploybar und skalierbar.
  2. Die Kommunikation mit Microservices erfolgt über klar definierte, technologieunabhängige APIs (in der Regel REST).
  3. Microservices folgen dem “Share Nothing”-Prinzip. Das heißt, dass jeder Microservice exklusive Hoheit über die von ihm verwendeten Daten hat. Auch die Fachlogik (Code) sollte nicht mit anderen Services (z. B. über gemeinsame Libraries) geteilt werden.

Der Microservices-Ansatz steht im Kontrast zum monolithischen Ansatz, der traditionell in der Softwareentwicklung eingesetzt wird. Monolithen vereinen oft mehrere fachlich in sich geschlossene Blöcke unter einem Dach. Individuelle Änderbarkeit, Wartbarkeit und Deployment einzelner Fachfunktionen ist hierbei nicht gegeben. Die aus technologischer Sicht geringere Komplexität von Monolithen steht dabei einer ungleich höheren fachlichen Komplexität gegenüber.

Chancen nutzen

Warum sind Automonie, Entkopplung und fachlich sinnvolle Trennung wichtig? Wie unterstützen sie mein Unternehmen bei der Umsetzung von Digitalisierungsstrategien? Die Antwort ist vielschichtig.

Neue Features

Durch intelligenten fachlichen Schnitt, der damit einhergehenden geringen Größe und der Entkopplung von anderen Services lassen sich fachliche Anforderungen einfacher und schneller umsetzen als in einem großen, monolithischen System. Gleiches gilt auch für die spätere Produktivnahme (Deployment). Das reduziert nicht nur die Einstiegshürde für alle Beteiligten, sondern minimiert auch das Gesamtrisiko und die Time to Market. Gleichzeitig kann die Effizienz insgesamt gesteigert werden. Da jeder Microservice autonom entwickelt wird und keine Implementierungsdetails nach außen preisgibt, können je nach Anwendungsfall unterschiedliche Technologien zum Einsatz kommen.

Wartbarkeit

Fakt ist: Wer schon mit monolithischen Systemen gearbeitet hat, weiß, wie schwierig und langwierig es ist, diese zu ändern. Dies fängt bei unerwünschten Seiteneffekten an, geht über den erforderlichen Akzeptanztest der Anwendung als Ganzes und endet schließlich bei der Produktivnahme im Block. Hierbei sind organisatorische Prozesshürden noch gar nicht mitgerechnet.

Beim Microservices-Ansatz muss nur der Service geändert werden, den eine fachliche Änderung tatsächlich betrifft. Nur dieser Service muss später auch neu ausgerollt werden, ohne das Gesamtsystem zu beeinflussen. Dies reduziert auch das Risiko, das mit der Änderung einhergeht. Kommt es regelmäßig vor, dass fachliche Änderungen mehrere Microservices betreffen, sollte man über den fachlichen Schnitt nachdenken.

Innovation

Microservices unterstützen den Innovationsprozess sowohl fachlich als auch technisch. Durch die Aufteilung in fachlich abgegrenzte Blöcke entscheidet jeder Microservice zu einem guten Stück individuell über die spätere User Experience des Endanwenders. Neue, innovative Ideen können leichter und schneller umgesetzt werden. Hierdurch entsteht Marktdifferenzierung.

Gleiches gilt auch für technische Innovation. Neue Technologien und Frameworks können eingesetzt werden, ohne ständig das Gesamtsystem im Auge behalten zu müssen. Und noch besser: eine Technologieauswahl kann man je nach Fachlichkeit so treffen, dass sie dessen technische Implementierung optimal unterstützt. Hat man sich vertan, so ist die Entscheidung für ein “Refactoring” leichter getroffen als wenn man die möglichen Auswirkungen auf andere fachliche Blöcke zusätzlich beachten müsste.

Skalierbarkeit

In einer Welt, in der Kunden und Mitarbeiter auf immer neue Arten und auf verschiedenen Kanälen mit Teilen einer Anwendung interagieren, ist es nicht immer einfach, Konsequenzen für deren Verfügbarkeit und der damit verbundenen User Experience vorauszusehen. Man möchte die Möglichkeit haben, individuell auf ein sich änderndes Nutzungsverhalten reagieren zu können. Steigt die Last auf einem Service, da dessen Fachlichkeit vermehrt genutzt wird, möchte man diesen einen Service mehrfach redundant vorhalten und die Last gleichmäßig auf seine Instanzen verteilen können. Der Rest des Systems arbeitet normal weiter.

Fehlerisolation und Failover

Wenn es mal hart auf hart kommt, will man die potentiellen oder tatsächlichen Auswirkungen eines Fehlers auf das Gesamtsystem (und damit das Business) minimieren. Stichwort: “Graceful Degradation of Service”. Je autonomer ein Service gebaut ist, desto einfacher ist es, diesen mehrfach redundant vorzuhalten und im Fehlerfall auf eine Standby-Instanz umzuschalten. In der Zwischenzeit bleibt das Gesamtsystem verfügbar. Natürlich hilft Autonomie alleine nicht weiter. Aber in Kombination mit anderen Maßnahmen (z. B. Resilient Design) ist dies die Voraussetzung, um ein Höchstmaß an Verfügbarkeit zu gewährleisten. Das freut insbesondere die Benutzer des Systems, denn sie können (ggf. mit geringen Einschränkungen) weiterarbeiten. Auch das ist ein marktdifferenzierendes Merkmal und reduziert den potentiellen finanziellen Verlust im Fehlerfall!

Automatisierung

Je kleiner ein einzelnes Softwarepaket, desto einfacher ist es, dessen Deploymentprozess im Rahmen von Continuous Delivery zu automatisieren. Größe bezieht sich hier sowohl auf technische als auch auf fachliche Komplexität. Je abgegrenzter die Fachlichkeit, desto wahrscheinlicher ist es, dass man sogar Akzeptanztests automatisieren kann. Meine Erfahrung aus unzähligen Projekten: Manuelle Fachtests sind der Flaschenhals im Softwareproduktionsprozess. Automatisierung ist also ein weiterer Beitrag zur Reduzierung der Time to Market. Behaviour-Driven Testing Frameworks können dabei gute Dienste leisten.

Teams

Die Idee beim Microservices-Ansatz ist, dass ein Team bestehend aus Fachexperten, Entwicklern und Betrieb die fachliche und technische Gesamtverantwortung für einen Microservice übernimmt. Das Team ist, genau wie der Microservice selbst, weitgehend autonom. Das stärkt das Team Commitment, fördert die Motivation und baut Prozesshürden sowie Silodenken ab. Das gilt übrigens idealerweise auch für den Betrieb des Microservices: You build it, you run it!. Voraussetzung für all das ist, dass dem Team (in bestimmten Grenzen) Möglichkeiten und Befugnisse übertragen werden, die für die Übernahme der Gesamtverantwortung benötigt werden. Microservice-übergreifende Konzepte und Themen lassen sich über ein Macro-Architekturteam regeln.

Organisational Change

Neben autonomen und selbst-organisierenden Teams kann der Microservices-Ansatz auch Änderungsprozesse hin zu agilen Management- und Entwicklungsmethoden (Management 3.0, Scrum, etc.) unterstützen, die häufig im Kontext von Digitalisierungsstrategien vollzogen werden. Ziel hierbei ist, eine Kultur (Mindset) zu schaffen, die es dem Unternehmen ermöglicht, sich schneller an veränderte Marktbedingungen anzupassen.

Herausforderungen meistern

There is no free lunch! Microservices führen zu verteilten Systemen. Damit geht eine entsprechende technologische Komplexität einher, die bei der Anwendung dieses Architekturansatzes berücksichtigt werden muss. Dabei helfen Techniken, Tools, Patterns und Practices, denen ich mich jeweils in entsprechenden Folgebeiträgen widmen werde. Nicht zu verschweigen sind auch methodische Herausforderungen. Wie schneide ich meine Services fachlich “richtig”? Auch darauf werde ich eingehen.

Fazit

Microservices können ein möglicher Baustein bei der Umsetzung einer Digitalisierungsstrategie im Unternehmen sein. Zentraler Vorteil des Microservices-Ansatz ist seine Fokussierung auf fachliche Zusammenhänge mit dem Ziel, Business Value im Kontext der digitalen Wertschöpfungskette zu schaffen. Die Autonomie jedes einzelnen Microservice in Bezug auf Änderbarkeit, Skalierbarkeit und Deployment hilft, flexibel auf eine sich verändernde Umwelt zu reagieren, neue Technologien früh zu adaptieren und damit die Wettbewerbsfähigkeit des Unternehmens zu erhöhen. Fehler können isoliert werden; das reduziert das unternehmerische Risiko. Automatisiertes Continuous Delivery von kleinen, autonomen Einheiten erhöht zusätzlich die Time to Market. Die Aufstellung von autonomen Teams, die jeweils die Gesamtverantwortung für einen Microservice übernehmen, schafft Agilität und Entfaltungsmöglichkeiten.

Beim Einsatz von Microservices muss man die technische Komplexität verteilter Systeme beachten. Eine Vielzahl von Techniken hilft, diese zu beherrschen. Überlegt und schrittweise eingeführt überwiegen die Vorteile, gerade im Hinblick auf die Herausforderungen der Digitalisierung.

Reaktive Anwendungen mit dem Aktorenmodell – Eine Einführung

Die Komplexität reaktiver Enterprise-Anwendungen mit dem Aktorenmodell beherrschbar machen! Darum geht es in dieser mehrteiligen Serie, in der ich das Modell zunächst allgemein und später im Kontext verfügbarer Microsoft-Technologien (Service Fabric) vorstelle.

Alter Wein in neuen Schläuchen?

Das Aktorenmodell wurde schon 1973 von Carl Hewitt, Peter Bishop und Richard Steiger als mathematisches Modell für nebenläufige Berechnungen vorgestellt. Dies geschah zunächst unabhängig von einer konkreten Technologie. Hintergrund war die Idee, Berechnungen von unabhängigen, vernetzten Mikroprozessoren mit eigenem Speicher nebenläufig ausführen zu lassen. Mit dem neuen Modell sollten die vorhandenen Ressourcen effektiver ausgenutzt und folgende Probleme traditioneller Entwicklungsansätze adressiert werden:

  1. Threads: Für die parallele Ausführung von Berechnungsprozessen werden gemeinhin Threads verwendet. Bei einfachen, nicht-komplexen Aufgaben funktioniert dies gut. Werden die Aufgaben komplexer, so sind auch die entstehenden Systeme schwerer verständlich. Threads werden im System nicht-deterministisch ausgeführt und müssen synchronisiert werden. Hierdurch steigen insgesamt Komplexität und Wartungsaufwand.
  2. Verteilung: Soll ein System auf mehrere physikalische und/oder virtuelle Maschinen verteilt werden, z. B. um Hochverfügbarkeit zu gewährleisten, so steigt der Aufwand weiter, da zusätzlich Themen wie Load Balancing, Failover und Datenreplikation zwischen Maschinen adressiert werden müssen.
  3. Shared State: Bezieht sich hier einerseits auf Multi-Threading-Systeme, bei denen alle Threads dieselben Ressourcen im Anwendungsspeicher nutzen, und andererseits auf verteilte Systeme, deren Komponenten alle auf ein gemeinsames Repository, z. B. eine Datenbank, zugreifen. Steigt die Zahl der Anfragen von außen, können leicht Konflikte (Deadlocks) entstehen, was wiederum den Durchsatz des Systems insgesamt reduziert.

Mit der Verbreitung von Mehrkernarchitekturen und zuletzt der zunehmenden Verteilung von Computerressourcen, z. B. in der Cloud, stoßen traditionelle Enterprise-Anwendungsarchitekturen an ihre Grenzen. Dazu kommen immer öfter erhöhte Anforderungen an Reaktionszeit, Fehlertoleranz, Lastverteilung, Durchsatz und Verfügbarkeit. Große Anwendungen wie Facebook, Twitter und viele andere können ein Lied davon singen.

In diesem Kontext stellt das relativ alte Aktorenmodell die konsequente Weiterentwicklung objektorientierter Paradigmen dar. Mit seinen Regeln legt es zudem einen zusätzlichen Fokus auf die Verteilung der Anwendungskomponenten. Vereinfacht gesprochen gibt es dort nur Nachrichten und Aktoren.

Das Aktorenmodell: Alles ist ein Aktor!

Das Aktorenmodell stellt eine Reihe von Regeln auf, die im Folgenden näher betrachtet werden:

  1. Analog zur objektorientierten Philosophie gilt: Alles ist ein Aktor!
  2. Aktoren senden eine begrenzte Anzahl von Nachrichten an andere Aktoren, und zwar asynchron.
  3. Aktoren können Instanzen anderer Aktoren erzeugen, sofern sie deren Adresse kennen.
  4. Sie können ihr Verhalten während der Laufzeit ändern. (Anmerkung: Dies ist durchaus nicht trivial, wie wir später noch sehen werden).
  5. Jeder Aktor verwaltet nur seinen eigenen Zustand. Dieser wird nicht mit anderen Aktoren geteilt.

Was bedeutet das?

Die erste Regel macht zunächst den gedanklichen Übergang einfach: Wir können Aktoren als logische Einheiten mit begrenzter Verantwortlichkeit ansehen, ganz wie wir es aus der Objektorientierung gewohnt sind. Im Kontext von Domain-Driven Design zum Beispiel ist jedes Aggregat ein perfekter Kandidat für einen Aktor.

Die zweite Regel besagt, dass eine Kommunikation dieser Einheiten untereinander asynchron über Nachrichten zu geschehen hat. Wir können uns dies wie bei der Post vorstellen, allerdings deutlich schneller. Jeder Aktor hat ein Postfach. Dort werden die von anderen Aktoren gesendeten Nachrichten vom System zugestellt. Jeder Aktor verwaltet sein eigenes Postfach, arbeitet die darin liegenden Nachrichten der Reihe nach ab. Er kann seinerseits Nachrichten an andere, ihm bekannte Aktoren, senden. Hierfür muss er natürlich ebenfalls deren Postfachadresse kennen.

Innerhalb eines Aktorensystems herrscht Location Transparency, d. h. Aktoren wissen nicht, wo genau sie selbst oder andere Aktoren ausgeführt werden. Dies könnte im selben Prozess, auf derselben Machine oder sonstwo sein. Um die Zustellung der Nachrichten über sogenannte Channels kümmert sich das Aktorensystem (analog zur Post). Siehe hierzu auch Abbildung 1.

Schematische Darstellung zum Aktorenmodell
Abbildung 1: Beispiel eines Aktorensystems mit drei Aktoren A, B und C.

Vor- und Nachteile

Die Vorteile des Aktorenmodells liegen auf der Hand:

  • Aktoren lassen sich fast beliebig im System verteilen. Ein Aktorensystem ist deshalb beliebig skalierbar. Da der Ausführungsort der Aktoren transparent ist, funktioniert das System weiter, auch wenn sich deren physikalische Adresse innerhalb des (verteilten) Systems während der Laufzeit ändert. Die logische Adresse (das “Postfach”) bleibt stets gleich und das System allein weiß, wo die Instanz des Aktors läuft. Dies wird realisiert, indem Aufrufer nicht mit der Aktorinstanz selbst, sondern mit einer Proxy-Instanz kommunizieren, welche die gleiche Schnittstelle besitzt.
  • Da der Zugriff auf einen Aktor über dessen logische Adresse geschieht, kann dessen Implementierung einfach ausgetauscht werden.
  • Die explizite Kommunikation über Nachrichten erlaubt es, beliebige Aktorennetze (Topologien) aufzubauen. Darin können weitere logische Komponenten, z. B. Router, Splitter, Aggregatoren oder Prozess-Manager zum Einsatz kommen. Diese sind ihrerseits Aktoren, die Nachrichten nach bestimmten Regeln verteilen, transformieren oder deren Zustellung koordinieren.
  • Aktoren teilen untereinander nichts, außer natürlich über den Inhalt der Nachrichten, die sie miteinander austauschen. Diese sind grundsätzlich immutable. Locks auf Ressourcen sind deshalb nicht erforderlich.
  • Aktoren können als Zustandsmaschinen eingesetzt werden, da sie ihr Verhalten dynamisch zwischen zwei Nachrichten ändern können.
  • Viele Aspekte, die sich mit der Zustellung von Nachrichten, der Verteilung von Aktoren sowie deren Lebenszyklus innerhalb des Systems beschäftigen, werden implizit durch das Aktorensystem verwaltet. Entwickler müssen sich daher normalerweise um diese Aspekte nicht kümmern, sondern können sich stattdessen auf die Lösung des fachlichen Problems konzentrieren. Dies reduziert die Gesamtkomplexität des Systems.

Als Nachteile wären zu nennen:

  • Entwickler, die bisher nicht mit dem Aktorenmodell gearbeitet haben, müssen sich zunächst mit dessen Eigenheiten anfreunden.
  • Das dynamische Ändern des Verhaltens eines Aktors zur Laufzeit kann die Fehlersuche erschweren, insbesondere bei komplexer Aktor-Zustandslogik.
  • Viele Prozesse innerhalb eines Aktorensystems laufen “unter der Haube” ab (z. B. Zustellung von Nachrichten). Dies kann deren Nachvollziehbarkeit erschweren, insbesondere bei der Analyse von Fehlern.

Gibt’s da nicht was von…

Die Popularität des Aktorenmodells ist nicht zuletzt deswegen gestiegen, da es inzwischen in fast allen Umgebungen und Programmiersprachen Frameworks gibt, die das Modell unterstützen. Für Java und Scala ist das Akka Toolkit ein prominentes Beispiel. Es ist dort bereits seit einiger Zeit im Einsatz. In der .NET-Welt sind vor allem die folgenden Frameworks bekannt:

  • Akka.NET (Portierung des Akka Toolkits in die .NET-Welt). Das Framework orientiert sich sehr stark an den ursprünglich von Hewitt et al. aufgestellten Regeln. Dies bedeutet insbesondere, dass Aktoren in der Regel keine streng typisierte Schnittstelle für den Nachrichtenaustausch besitzen. Nachrichten sind hierbei explizit definierte (immutable) Datenstrukturen, die über einen Proxy an die Aktorschnittstelle Actor.Tell(object) geschickt werden. In Akka.NET besteht zwar grundsätzlich die Möglichkeit, auch typisierte Aktoren (Typed Actors) zu verwenden, dieser Ansatz ist aber nur in Ausnahmefällen sinnvoll.
  • Orleans (Aktoren heißen dort Grains). Das Framework wurde von Microsoft Research ins Leben gerufen, existiert schon etwas länger und weicht in einigen Belangen deutlich von Hewitt et al. ab. Primäres Ziel ist es, sogenannten “Non-Experts” die Umsetzung von verteilten, skalierbaren Anwendungen zu ermöglichen. Einer der Hauptunterschiede zu Akka ist, dass Aktoren (Grains) in Orleans grundsätzlich typisierte Schnittstellen besitzen, d. h. die Funktionalität der Aktoren wird über den Aufruf anwendungsfallspezifischer Methoden mit serialisierbaren Parametern auf dem Proxy getriggert.
  • Service Fabric Reliable Actors. Hierbei handelt es sich um die jüngste Implementierung des Aktorenmodells auf der Microsoft-Plattform. Service Fabric Reliable Actors setzen auf Service Fabric Reliable Services auf und können sowohl in der Cloud als auch on-premise gehostet werden. In der Summe unterscheiden sich Orleans und Service Fabric Actors nicht wesentlich voneinander, insbesondere hinsichtlich Typisierung der Aktorschnittstelle (siehe Orleans).

Fazit

Das bereits Anfang der 70-er Jahre entwickelte Aktorenmodell von Hewitt et al. erlebt vor dem Hintergrund fortschreitender Verteilung (Cloud) und komplexerer Anwendungen im Enterprisebereich eine kleine Renaissance. Dies liegt nicht zuletzt an der Tatsache, dass zunehmend mehr Frameworks für dessen Verwendung zur Verfügung stehen. Zu den ältesten Vertretern gehört Akka. Aber auch Microsoft erkennt zunehmend die Bedeutung des Aktorenmodells und stellte mit Service Fabric Reliable Actors jüngst ein weiteres Aktor-Framework auf der Service-Fabric-Plattform vor. In den folgenden Teilen der Serie werden Akka.NET und die Microsoft-Implementierung des Aktorenmodell auf der Plattform Service Fabric näher beleuchtet.

Referenzen

Softwaremodelle: Mehr als nur Diagramme

Mein letzter eXpert Talk am 13.02.2014 war mit dem Thema Softwaremodelle, deren Sinn und Bedeutung sowie geeigneten Tools zur Erstellung und Unterhaltung von Modellen überschrieben. Der eXpert Talk dient innerhalb der SDX dem technischen Austausch unter Kollegen und wird häufig gefolgt von einer Diskussion über die vorgetragenen Themen. Im Rahmen meines Vortrags habe ich zunächst die Grundlagen des modellgetriebenen Ansatzes erläutert und danach beispielhaft unter Verwendung von Sparx Enterprise Architect gezeigt, wie er im Softwareentwicklungsprozess erfolgreich in die Praxis umgesetzt werden kann, aber auch seine Nachteile und Fallstricke dargelegt. Dieser Beitrag soll die Inhalte des Talks kurz zusammenfassen.

Model-Driven Development (MDD)

Bei der modellgetriebenen Softwareentwicklung steht bekanntermaßen das Softwaremodell im Mittelpunkt. Es ist Ausgangspunkt und Richtschnur während des gesamten Softwareentwicklungsprozesses. Dieser Ansatz ist nicht neu. Bereits 2001 wurde Model-Driven Architecture (MDA) von der Object Management Group (OMG) formal ins Leben gerufen. MDA ist sozusagen die Implementierung des modellgetriebenen Ansatzes (MDD) unter Verwendung von Standards, die von der OMG vorgegeben werden. Gemeinsames Ziel ist die modellgetriebene Konzeption und Erstellung komplexer Software, wobei alle Entwickler durch die Konzentration auf das Modell zu einer sorgfältigeren, formaleren und abstrakteren Vorgehensweise beim Design der Anwendung gezwungen sind. Es soll dadurch unter anderem das sogenanntes “wild hacking” unterbunden werden, bei dem zu früh mit der Implementierung, d. h. dem Schreiben von Quellcode, begonnen wird. Somit kann die Produktivität und Softwarequalität erheblich gesteigert sowie gegebenenfalls Mehraufwand vermieden werden.

MDA
Abbildung 1: Schematische Darstellung des Roundtrip-Engineerings in MDA

Der Grundgedanke von MDA ist zunächst sehr theoretisch und kann in der Praxis auch eine Reihe von Nachteilen haben. Beispielsweise wird allen Entwicklern im Team ein hoher Abstraktionsgrad abverlangt, was bei inhomogener Teamzusammensetzung (und dies ist in der Praxis häufig der Fall) zu entsprechenden Problemen führen kann. Zum anderen wird für ein effektives Roundtrip-Engineering (siehe Abbildung 1), d. h. die fortwährende Übernahme von Codeänderungen ins Modell bzw. der Generierung von Code für neue Modellelemente, ein geeignetes Tool benötigt.

Im Laufe der Diskussion, die während des Talks aufkam, ist außerdem schnell klar geworden, dass Entwickler unterschiedliche Vorstellungen von und Erwartungen an MDD bzw. MDD-Tools haben, und dass nicht alle Erwartungen erfüllt werden können. Ich bin außerdem auch auf den empfohlenen Detailgrad von Modellen eingegangen; ein Overengineering (zu detailliertes Modell) sollte generell vermieden werden.

Sparx Enterprise Architect

Relativ zügig sind wir dann in die Demophase eingestiegen, in dessen Verlauf ich anhand eines einfachen Beispiels den Einsatz von Sparx Enterprise Architekt (EA) für MDD zeigen konnte. Anhand des SDX-Bewerbungsprozessen habe ich demonstriert, wie

  1. EA für die Erstellung eines plattformübergreifenden Modells (Platform-Independent Model, PIM), in unserem Fall ein Domänenmodell, verwendet werden kann.
  2. Das plattformübergreifende Modell mit Hilfe von konfigurierbaren Templates in ein plattformspezifisches Modell (Platform-Specific Model, PSM) transformiert werden kann.
  3. Plattformspezifische Details modelliert und Quellcode aus dem PSM generiert werden, auch hier mithilfe konfigurierbarer Templates.
  4. Die Funktionalität von EA durch Scripting und Add-Ins erweiterbar ist.
  5. EA für das Roundtrip-Engineering während des gesamten Softwareentwicklungsprozesses verwendet wird.
  6. Die Dokumentation unter Verwendung von Templates aus dem Modell generiert werden kann.

Zudem habe ich auch die anderen Features und Einsatzmöglichkeiten von EA kurz erläutert und bin auf Fragen eingegangen. Auch Nachteile/Einschränkungen des Tools sind dabei zur Sprache gekommen:

  • Eine effektive Generierung von Code aus Aktivitäts-/Sequenzdiagrammen ist nur eingeschränkt und auch nur am Anfang möglich.
  • Je nach verwendeter Plattform (Programmiersprache) werden nicht UML-konforme Codeelemente gegebenenfalls nicht unterstützt und können zu Problemen beim Reverse-/Forward-Engineering führen.
  • Ein effektives Code-Refactoring ist über EA nicht möglich und sollte deshalb besser in der Entwicklungsumgebung selbst vorgenommen werden.

Fazit

MDD kann unter Verwendung geeigneter Tools durchaus erfolgreich in der Praxis eingesetzt werden, um komplexe Systeme zu modellieren, Code aus Modellen zu erzeugen und Codeänderungen in das Modell zu übernehmen. Ich selbst setze EA für diesen Zweck bereits seit Jahren aktiv ein. Die grundsätzlich andere Herangehensweise an das Softwareprojekt, die der MDD-Ansatz Entwicklern abverlangt, kann jedoch unter Umständen zu Problemen führen. Ebenso müssen wie bei jeder Methode eventuell Kompromisse gemacht werden. MDD eignet sich vor allem in Projekten, bei denen die abzubildende Geschäftslogik einigermaßen komplex ist. Architekturvorgaben werden nicht gemacht und sind von der Anwendung selbst abhängig; MDD als Ansatz kann für die Abbildung einer Vielzahl von Architekturen und Patterns verwendet werden.

MDD im Praxistest: Visual Studio 2012

Im letzten Beitrag habe ich die Anforderungen von modellgetriebener Softwareentwicklung (MDD) erörtert und im Zuge dessen eine Liste von Evaluationskriterien aufgestellt. In Visual Studio 2012 Ultimate Edition versucht nun auch Microsoft, sich der Modellierung von Software mithilfe der UML zu nähern. In diesem Beitrag werde ich mich deshalb mit der Evaluation von VS 2012 unter Berücksichtigung der vorgenannten Kriterien widmen.

 

Modellieren mit Visual Studio 2012

Seit Visual Studio 2012 hat Microsoft mit der UML als Modellierungssprache seinen Frieden geschlossen. Davor wurden ausschließlich Konstrukte in einer (UML-ähnlichen) DSL unterstützt, da diese enger an die verwendete Technologie (hier: .NET) gebunden waren und deshalb einfacher für Codegenerierungszwecke verwendet werden konnten. Visual Studio 2012 Ultimate Edition stellt nun ein Modellierungsprojekttemplate zur Verfügung, das für die Erstellung von UML-Modellen geeignet ist. Über entsprechende Erweiterungen kann die Funktionalität von Visual Studio weiter angepasst werden.

UML-Modellierung

In UML-Modellierungsprojekten können die wichtigsten UML-Diagrammtypen verwendet werden (siehe Abbildung 1). Nicht unterstützt werden leider Zustandsdiagramme, die neben Klassen- und Aktivitätsdiagrammen wohl zu den am häufigsten genutzten UML-Diagrammtypen gehören. Allerdings bietet Visual Studio Layer-Diagramme an, die offiziell nicht Bestandteil der UML sind (dort wird diese Funktionalität über Komponentendiagramme abgedeckt). Der Funktionsumfang der Layer-Diagramme wurde von meinem Kollegen Alexander Jung hier eingehend betrachtet.

Diagramme unterstützen darüber hinaus alle gängigen UML-Konstrukte; Stereotypen können in sogenannten Profilen definiert und danach auf dafür vorgesehene UML-Konstrukte angewendet werden. Visual Studio sieht auch die Erstellung von PIMs (L2-/L3-Standardprofile) und PSMs (C#-Profil) vor. Sprachspezifische Konstrukte wie beispielsweise C#-Properties werden über entsprechende Stereotypen markiert und bei der Codegenerierung entsprechend berücksichtigt.

Diagrammtypen in VS 2012 UML-Modellierungsprojekten
Abbildung 1: Diagrammtypen in VS 2012 UML-Modellierungsprojekten

Codegenerierung

Die Generierung von Quellcode wird in Visual Studio 2012 über ein entsprechendes T4-Template ermöglicht. Ein Template für die Erzeugung von C#-Quellcode wird standardmäßig mitgeliefert, kann aber benutzerdefiniert angepasst werden. Für jedes UML-Modell und jeden Elementtyp (Klasse, Enumeration, Interface, Struct) können separate Templates konfiguriert werden.

Konfiguration des pro UML-Konstrukt zu verwendenden T4-Templates
Abbildung 2: Konfiguration des pro UML-Konstrukt zu verwendenden T4-Templates

Reverse Engineering

Das Reverse-Engineering ist mit Visual Studio 2012 grundsätzlich möglich, wobei dies im Praxistest nur mit erheblichen Einschränkungen funktionierte. In der Theorie lassen sich über den sogenannten Architecture Explorer Elemente aus referenzierten Assemblies oder Projekten per Drag & Drop auf Diagramme ziehen. In der Folge werden die in der Assembly oder dem Projekt existierenden Artefakte importiert und im UML Model Explorer als Packages, Klassen oder Schnittstellen (Interfaces) dargestellt. Im Test funktionierte dies in wenigen Fällen reibungslos, beispielsweise gab Visual Studio unter anderem folgende Meldungen aus:

Error: Object reference not set to an instance of an object. Reverse engineer process was stopped.
Message: (6) type(s) fully reverse engineered.
Message: (13) type(s) partially reverse engineered. These types were not included in the original selection so their full definitions are not reverse engineered. Instead, this shallow operation has simply ensured these types exist in the UML model because they are needed in order to fully describe other elements.

Manche Typen, zum Beispiel solche, die sich in einer anderen Assembly befinden, werden nur teilweise eingelesen, obwohl die Ursprungs-Assembly diese explizit referenziert. Die manuelle Korrektur solcher Fehler gestaltete sich im Test mühsam, denn auch alle bestehenden Assoziationen müssen dann manuell nachgetragen werden. Darüber hinaus werden Template-Parameter (Parameter generischer Typen) unter dem Package “Unspecified Types" als separate Konstrukte importiert, was wenig sinnvoll erscheint (siehe Abbildung 3).

Unspecified Types
Abbildung 3: Template-Parameter werden als “Unspecified Types” eingelesen

Synchronisierung

Eine Synchronisierung zwischen Modell und Code im Rahmen des Roundtrip-Engineerings ist in Visual Studio 2012 nicht vorgesehen. In der MSDN-Doku heißt es hierzu:

The code and the diagram are not updated automatically. You can update the diagram to discuss change proposals without affecting your code. Subsequent changes in the code will not automatically affect the diagram unless you again drag the classes onto the diagram. [1]

Dies bedeutet, dass eine automatische Übernahme/Synchronisation von Codeänderungen im Modell nur durch erneutes, manuelles Reverse Engineering über Drag & Drop erfolgen kann. In Anbetracht der Schwierigkeiten, die mit dieser Funktion im Praxistest auftraten, ist dies eine unbefriedigende Aussage. Die Reflexion von Modelländerungen im Code ist ebenfalls problematisch. Bei der Konfiguration von Templates für die Quellcodegenerierung (siehe Abbildung 2 oben) kann lediglich angegeben werden, ob vorhandene Dateien im Zielverzeichnis überschrieben werden sollen oder nicht. Ein Abgleich mit vorhandenem Quellcode findet damit nicht statt.

MDA

Obwohl die Erstellung sowohl von PIMs als auch von PSMs mit Visual Studio 2012 grundsätzlich über die Auswahl von Modellprofilen möglich ist, kann ein PIM in der Entwicklungsumgebung nicht ohne weiteres in ein PSM umgewandelt werden. Entsprechende Transformationsfunktionen sind nicht vorgesehen. Dies ist im Kontext dieser Evaluation jedoch irrelevant, denn durch die Verwendung von Visual Studio hat man sich bereits explizit für die .NET-Plattform entschieden. Die Erstellung von PIMs ist deshalb höchstens für Dokumentationszwecke sinnvoll.

Anpassbarkeit

Durch die Verwendung von T4-Texttemplates lässt sich die Generierung von Textartefakten beliebig anpassen. Die entsprechenden Template-Dateien finden sich im Verzeichnis “C:Program Files (x86)Microsoft Visual Studio 11.0Common7IDEExtensionsMicrosoftArchitecture ToolsExtensibilityTemplatesText” und enthalten Markups der Form “<# … #>” mit Codefragmenten, die für die Abbildung von Logik und Prozeduraufrufen verwendet werden. Eine Anpassung des Reverse Engineerings von bereits existierendem Code ist nicht über Templates möglich.

Fazit

Visual Studio 2012 unterstützt grundsätzlich die Modellierung von Anwendungen mithilfe der UML. Allerdings schneidet die Entwicklungsumgebung bei der Evaluierung der DDD-/MDD-Kriterien schlecht ab. Schwächen zeigten sich insbesondere (1) beim Reverse Engineering existierender Codefragmente, das nicht immer fehlerfrei ablief und manuelle Eingriffe nötig machte und (2) der Synchronisation von Modell und Code. Existierende Einschränkungen bei der Unterstützung von MDA-Transformationen sind hier irrelevant, da die Verwendung von Visual Studio bereits die Festlegung auf die .NET-Plattform impliziert, daher wurden diese bei der abschließenden Bewertung nicht berücksichtigt.

Referenzen

[1] MSDN: How to: Create UML Class Diagrams from Code, Online Publikation, http://msdn.microsoft.com/en-us/library/vstudio/ff657806.aspx, Stand: 7. Februar 2013

Model-Driven Development: Was muss ein gutes Tool können?

div class=”articleAbstract”>

In einem meiner letzten Beiträge habe ich eine kurze Einführung in Domain-Driven Design (DDD) und modellgetriebene Softwareentwicklung (MDD) gegeben. Bei beiden Methoden steht bekanntermaßen das Softwaremodell im Vordergrund, d. h. es werden idealerweise Tools für die Entwicklung und Wartung des Modells benötigt.

In diesem Beitrag werde ich zunächst die Anforderungen herausarbeiten, die sich aus der MDD-Methodik für die Praxis ergeben. Diese werden sodann als Kriterien für die Evaluation von Modelling- und Software-Tools in weiteren Beiträgen verwendet. Diese wurden auf der Grundlage diverser Quellen [1, 2] und den Informationen der vorherigen Beiträge zu diesem Thema erstellt.

Softwaremodelle als zentraler Aspekt

DDD und MDD erfordern, dass das Modell als zentrales Element im Softwareentwicklungsprozess steht, und zwar von Beginn an. Sofern nicht bereits Code eines Systems existiert, wird das Modell im ersten Schritt neu erstellt. Für das Requirements Engineering bietet UML Anwendungsfalldiagramme an. Diese können wiederum für die Modellierung einer groben Systemkomponentenstruktur (Komponentendiagramm) und deren Feinstruktur (Klassendiagramm) verwendet werden. Die vorgenannten Aspekte spiegeln vorwiegend strukturelle und statische Elemente des Softwaresystems wieder. Fachlogik und das dynamische Anwendungsverhalten werden dagegen mittels Sequenz-, Interaktions- und Aktivitätsdiagrammen modelliert; auch hier können Anwendungsfalldiagramme als Vorlage dienen. Jeder der drei vorgenannten Diagrammtypen stellt die Systemdynamik mit dem Fokus auf unterschiedliche Aspekte dar. Bei DDD spielt zudem der Objektzustand eine große Rolle, dieser kann mit Hilfe von Zustandsdiagrammen modelliert werden. Die Grundlagen der UML-Modellierung sind in [3] umfassend erläutert und werden hier deshalb nicht näher betrachtet.

Integration vorhandener Artefakte

Für bereits existierende Systeme kann in der Regel zumindest die existierende statische Struktur (Packages und Klassen) mittels Reverse Engineering in ein UML-Modell überführt werden. Es ist typischerweise relativ einfach möglich, Klassendiagramme auf der Basis von existierendem Quellcode in einer objektorientierten Programmiersprache zu erstellen. Verhaltensaspekte dagegen sind auf diesem Weg häufig schwieriger in das Modell zu integrieren.
DDD verlangt darüber hinaus, dass das Modell zu jedem Zeitpunkt den aktuellen Stand der Architektur und des Designs des Softwaresystems widerspiegelt. Dies erfordert, dass Modell- und Codeartefakte jederzeit miteinander synchronisiert werden können.

Plattformunabhängigkeit

Systeme bestehen außerdem in vielen Fällen aus inhomogenen Komponenten, die teilweise auf der Grundlage unterschiedlicher Technologien und/oder Plattformen entwickelt wurden. Ist dies der Fall, so muss auch das Modell diesem Umstand Rechnung tragen. In Model-Driven Architecture (MDA) [4] wird daher zwischen PIMs (Platform-Independent Models) und PSMs (Platform-Specific Models) unterschieden. Erstere verwenden nur Elemente, die in nahezu allen objektorientierten Programmiersprachen gleichermaßen vorkommen, während letztere spezielle Aspekte einer gewählten Sprache unterstützen. Beispielsweise kennt C# Properties, während Java für den gleichen Zweck Getter- und Setter-Methoden verwendet. Sofern ein Softwareprojekt, das aus inhomogenen Komponenten besteht, entwickelt wird, sollte ein Modellierungstool die Transformation von PIM in PSM unterstützen, so dass Aspekte zunächst allgemein und dann pro zu unterstützender Technologieplattform im Detail modelliert werden können. Mit anderen Worten: die Fachlichkeit wird zuerst ohne Bezug zu einer Programmiersprache und/oder Technologieplattform abstrakt modelliert.

Anpassung an verwendete Technologien

Schließlich spielen auch projektspezifische Besonderheiten eine Rolle. UML erlaubt die Verwendung von Stereotypen und Tagged Values, um Eigenschaften bestimmter Modellelemente semantisch zu beschreiben. Ein Modellierungstool sollte Funktionen bereitstellen, die dem Entwicklerteam die Anpassung des Modelltransformations- und Codegenerierungsprozesses erlauben.

Zusammengefasst können daher die folgenden Kriterien aufgestellt werden:

  1. UML-Modellierung: Das Tool muss die Erstellung der gängigen UML-Diagrammtypen erlauben. Idealerweise können Diagramme miteinander verknüpft werden, um die Navigation durch das Modell zu erleichtern.
  2. Codegenerierung: Die Generierung von Quellcode in einer vorgegebenen Programmiersprache muss unterstützt werden.
  3. Reverse Engineering: Existierender Code muss über Reverse Engineering in ein Modell überführt werden können, und zwar wenigstens für die statischen Strukturelemente des Softwaresystems, idealerweise auch für dynamische Elemente.
  4. Synchronisierung: Die Synchronisation von Quellcode und Modell muss möglich sein, d. h. geänderte Modellelemente müssen bei der Generierung auch im Quellcode geändert bzw. neu eingefügt und Codeänderungen in das Modell übernommen werden können.
  5. MDA: Sofern dies im Projekt benötigt wird, müssen sowohl PIMs als auch PSMs unterstützt werden. Erstere müssen dann in letztere transformierbar sein.
  6. Anpassbarkeit: Die vorgenannten Generierungs-, Transformations- und Synchronisationsprozesse müssen über Templates anpassbar sein, so dass projekt- oder technologiespezifische Besonderheiten berücksichtigt werden können.

Fazit

Ein gutes MDD-Tool unterstützt Fachseite, Softwarearchitekten und Entwickler gleichermaßen bei der Modellierung von Fachlichkeit im sogenannten Domain Model mit Hilfe der UML. Dies geschieht zunächst abstrakt und ohne Bezug zu einer konkreten Technologieplattform. Strukturelle Elemente des Modells können im nächsten Schritt, d. h. nach Wahl einer Technologieplattform, weitgehend automatisiert in einer beliebigen objektorientierten Programmiersprache abgebildet werden. Hierbei können spezielle Aspekte der Zielprogrammiersprache im plattformspezifischen Modell abgebildet und der Codegenerierungsprozess entsprechend angepasst werden. Sofern bereits Code aus existierenden Systemen vorhanden ist, so muss dieser in das Modell integriert werden können. Code und Modell werden während des gesamten Entwicklungsprozesses synchron gehalten, hierfür ist ebenfalls eine Toolunterstützung erforderlich.

Referenzen

[1] Evans, Eric: Domain-Driven Design – Tackling Complexity in the Heart of Software, Addison-Wesley 2004
[2] Fowler, Martin: Patterns of Enterprise Application Architecture, Addison-Wesley 2003
[3] Fowler, Martin: UML Distilled – A Brief Guide to the Standard Object Modeling Language, 3. Auflage, Addison-Wesley, 2004
[4] Wikipedia: Model-Driven Architecture, Online-Publikation, http://en.wikipedia.org/wiki/Model-driven_architecture, Stand: 7. Februar 2013