MAD Summit https://mad-summit.de/ MAD Summit Wed, 25 Feb 2026 09:30:50 +0000 de-DE hourly 1 https://wordpress.org/?v=6.7.2 Pattern in your Domain https://mad-summit.de/blog/domain-driven-design/algebraische-datentypen-ddd-tactical-design/ Sun, 22 Feb 2026 19:44:15 +0000 https://mad-summit.de/?p=1079635 Die Analyse der Fachlichkeit ist zu einem fundamentalen Schritt der Softwareentwicklung geworden, nicht zuletzt durch den Erfolg des Domain-Driven Designs. Dabei werden Erkenntnisse nicht nur für die Entwicklung selbst, sondern auch für Fachexperten gewonnen. Das Resultat einer erfolgreichen Analyse besteht aus einem Entwurf, der die Fachlichkeit in Struktur und Begrifflichkeit abbildet und sich an zukünftige Änderungen und Erweiterungen anpassen lässt. Ein Weg dorthin ist die Identifikation algebraischer Strukturen im Problemraum und ihre Implementierung als algebraische Datentypen im Lösungsraum.

The post Pattern in your Domain appeared first on MAD Summit.

]]>
Seit 2003 prägt Domain-Driven Design – manifestiert durch das sogenannte „Blue Book“ von Eric Evans – unsere Sicht auf Softwareentwicklung und deren Verbindung zu Organisation und Fachlichkeit. Durch leistungsfähige Infrastruktur bei stetig steigender inhaltlicher Komplexität verlagert sich der Fokus von Technik und Hardware hin zur Modellierung und dem logischen Schnitt von Systemen. Eben diesen Schritt setzt das Domain-Driven Design sowohl an den Beginn als auch in den Fokus des Entwurfs.

Aufgeteilt wird das Vorgehen dabei in zwei Teilbereiche: Strategic Design und Tactical Design. Ersteres wird benötigt, um den Problemraum abzugrenzen und in handhabbare und gut voneinander zu trennende Teile aufzuspalten. Das zweite findet Anwendung bei der Übersetzung des Problemraums in konkrete Software im Lösungsraum (Kasten: „Problem- und Lösungsraum“). Für beide Teilbereiche werden Informationen aus Organisation und Fachlichkeit herangezogen: von Geschäftsobjekten über Organisationsstrukturen und Teamzusammensetzungen bis hin zu Prozessen. Es ist die Technik, die sich an diese Gegebenheiten anzupassen und sie zu unterstützen hat, nicht andersherum.

 

Problem- und Lösungsraum

Der Problemraum beschreibt die Gesamtheit der zu lösenden Themen. Hierunter fallen funktionale und Qualitätsanforderungen sowie Randbedingungen. Formuliert wird der Problemraum von Stakeholdern, also jenen Rollen und Personen, die ein berechtigtes Interesse an den Themen haben. Zusammenfassend kann man sagen, dass sich aus dem Problemraum das Was des Entwurfs ergibt.

Der Lösungsraum umfasst die gestalterischen und technischen Mittel, mit denen die Themen des Problemraums gelöst werden können. Dazu gehören das Design, die Architektur und die Implementierung einer Software, wenn diese die Anforderungen zu erfüllen vermag. Aus dem Lösungsraum ergibt sich das Wie des Entwurfs.

Dem Blue Book von Eric Evans [1] folgten zahlreiche weitere Werke, die das Domain-Driven Design weiterentwickelten und anpassten. Unter anderem das „Red Book“ von Vaughn Vernon [2], das sich auf die praktische Umsetzung konzentriert, und das Werk von Vlad Kononov [3]. Diese Bücher veranschaulichen, wie für die Softwareentwicklung nützliche Patterns im Kontext des Tactical Designs identifiziert und in Software gegossen werden.

Entities und Value Objects

Beispiele für solche Patterns sind die Unterscheidung von Entities und Value Objects. Entities beziehen sich auf Geschäftsobjekte, die einen eindeutigen Identifikator besitzen und gleichzeitig einen Lebenszyklus durchlaufen. Diese Objekte verändern sich möglicherweise im Laufe ihres Lebens, der Identifikator dagegen bleibt immer gleich. Value Objects bilden Geschäftsobjekte ab, deren Identifikator durch ihre Identität abgebildet wird, sprich, durch die Gesamtheit ihrer Eigenschaften. Sie sind unveränderlich und besitzen keinen Lebenszyklus. Als Beispiel soll uns hier das klassische Geschäftsobjekt „Kunde“ dienen. Dieser entspricht einer Entity, hat meist eine eindeutig identifizierende Kundennummer und durchläuft einen Lebenszyklus.

Innerhalb dieses Lebenszyklus kann die Adresse oder auch der Name gewechselt werden (zum Beispiel durch einen Umzug oder Heirat). Die Adresse selbst stellt ein Value Object dar. Sie wird identifiziert durch die Gesamtheit ihrer Eigenschaften (Straße, Hausnummer, Stadt, Postleitzahl). Sie verändert sich nicht, sondern wird nur im Ganzen ausgetauscht.

Strukturen aus Geschäftsobjekten

Die Grundidee, Strukturen für Software aus den Geschäftsobjekten abzuleiten, die sie abbilden sollen, ist kein Novum des Domain-Driven Designs. Sowohl in der klassischen, objektorientierten als auch in der funktionalen Programmierung finden sich ähnliche Ansätze. In der objektorientierten Programmierung wird dies durch Klassen und Objekte umgesetzt, die die Geschäftsobjekte und deren Verhalten abbilden. Diese inhaltliche Nähe findet sich in der oben aufgeführten Literatur wieder, in der mit objektorientierten Beispielen gearbeitet wird. In der funktionalen Programmierung hingegen werden algebraische Strukturen gesucht, die der Fachlichkeit zugrunde liegen und deren Eigenschaften der Software zugutekommen. Die Kombination aus beidem möchte ich anhand eines Beispiels verdeutlichen.

Geschäftsobjekte

Als Beispiel beschäftigen wir uns mit der Software für die Zugangskontrolle in einem großen, kommerziellen Gebäudekomplex, einem Bürogebäude. In einem ersten Schritt müssen wir auf Basis von Stakeholder-Befragungen, des Studiums von Dokumentationen sowie bereits bestehender Systeme für die Zugangskontrolle relevante Geschäftsobjekte identifizieren. In jedem Umfeld (sei es ein Unternehmen oder eine Organisation) gibt es Unmengen an Geschäftsobjekten und für jedes eine Vielzahl von Eigenschaften. Im folgenden Schritt interessieren wir uns jedoch lediglich für Geschäftsobjekte, die im Kontext der Zugangskontrolle eine Rolle spielen, und bei diesen nur für jene Eigenschaften, die wiederum in unseren fachlichen Regeln eine Rolle spielen. Im Folgenden beschreiben wir die Geschäftsobjekte für unser Beispiel (Kasten: „Benennung von Geschäftsobjekten).

Benennung von Geschäftsobjekten

Das Domain-Driven Design führt den Begriff der Ubiquitous Language ein. Es handelt sich hierbei um eine gemeinsames Sprachverständnis aller Stakeholder eines Systems, technisch wie fachlich. Die Benennung der Geschäftsobjekte ist ein zentraler Bestandteil der Ubiquitous Language und kann nur im Einklang mit allen Stakeholdern erfolgen. Erst wenn sich die Fachbegriffe der Domänenexperten genauso auch im Entwurf und im Code wiederfinden, ist es möglich, ein alle Rollen übergreifendes Verständnis für sie zu entwickeln.

Gebäudekomplex, Gebäude, Etage, Raum

Alle aufgeführten Geschäftsobjekte beschreiben Bereiche innerhalb des Gebäudekomplexes bzw. den Gebäudekomplex selbst. Sie können in einer Art Hierarchie angeordnet werden. Für die Zugangskontrolle sind die individuellen Eigenschaften der Geschäftsobjekte – etwa die Adresse eines Gebäudes oder die Etagennummer – lediglich als Identifikatoren relevant. Daher können sie aus dieser Perspektive als Value Objects eines Typs Zugangsbereich abgebildet werden.

Mitarbeiter, Besucher

Diese Geschäftsobjekte beschreiben Personen, die Zugang zum Gebäude haben. Sie besitzen jeweils einen eindeutigen Identifikator (Mitarbeiter-ID, Besucherausweis) und eine Definition ihrer Zugangsberechtigung. Sie durchlaufen einen Lebenszyklus (Mitarbeiter können das Unternehmen verlassen, Besucher können nur temporär im Gebäude sein), daher lassen sie sich als Entities eines Typs abbilden: Person.

Zugang

Dieses Geschäftsobjekt beschreibt die Berechtigung, die eine Person benötigt, um einen bestimmten Zugangsbereich zu betreten. Es handelt sich hierbei um eine Referenz auf einen oder mehrere Zugangsbereiche, die mit einer gewissen Semantik versehen ist. Die Referenz befindet sich im Besitz einer Person. Dementsprechend lässt sich der Zugang als Value Object des Typs Zugangsberechtigung abbilden.

Potenzial der Geschäftsobjekte

Bevor wir uns auf die Suche nach möglichen algebraischen Strukturen machen, ist es sinnvoll, die Komplexität und das Potenzial der Geschäftsobjekte und ihrer resultierenden Typen einzuschätzen. Leitfragen hierbei sind:

  • Wie viel meiner fachlichen Komplexität bezieht sich auf das Geschäftsobjekt und dessen Typ?

  • Ergibt das Geschäftsobjekt in einer kombinierten Struktur (sprich, mehrere Instanzen des Geschäftsobjektes zusammen) Sinn?

  • Welches Potenzial ergibt sich aus der Kombination mehrerer Instanzen des Geschäftsobjekts?

Für die Typen der oben genannten Geschäftsobjekte treffen wir nun eine Einschätzung.

Zugangsbereich

Die fachliche Komplexität, die ein Zugangsbereich mit sich bringt, ist gering. Es handelt sich um einen logischen Baustein, der die statische Struktur einer Immobilie abbildet. Eine Kombination von Zugangsbereichen ist definitiv sinnvoll, da diese zueinander in einer hierarchischen Beziehung stehen (zum Beispiel ist eine Etage Teil eines Gebäudes, das wiederum Teil eines Gebäudekomplexes sein kann). Aus der Kombination von Zugangsbereichen ergibt sich eine deutliche Vereinfachung der Formulierung von Zugangsberechtigungen. Anstatt einer Person Zugangsberechtigungen für jeden einzelnen Zugangsbereich unterhalb eines großen Bereiches zu geben, reicht die Vergabe einer einzigen Berechtigung aus. Damit wird auch die übliche Ausdrucksweise und das Verständnis der Stakeholder ausgedrückt.

Person

Die fachliche Komplexität, die eine Person darstellt, ist ebenfalls gering. Für die Zugangskontrolle sind alle Eigenschaften von Personen irrelevant, außer deren Identifikatoren und konkreten Berechtigungen. Auch eine Kombination von Personen ergibt keinen Sinn, da Personen, die sich in der Regel individuell bewegen können, für die Zugangskontrolle nicht sinnvoll in Gruppen zusammengefasst werden können.

Zugangsberechtigung

Die fachliche Komplexität der Zugangsberechtigungen ist hoch, hier steckt der Kern dessen, womit sich die Zugangskontrolle beschäftigt. Die Kombination von Zugangsberechtigungen ermöglicht es, die individuellen Berechtigungen einzelner Personen abzubilden und diese flexibel anzupassen. Das Potenzial hierbei ist sehr groß, da die fachlichen Regeln in fast allen Fällen einer Komposition von Zugangsberechtigungen entsprechen. Zum Beispiel benötigt eine Person, die ein Büro im zweiten Stockwerk bezieht, Zugangsberechtigungen für den Eingangsbereich des Gebäudes sowie für die zweite Etage.

Algebraische Strukturen

Jetzt, da wir eine Einschätzung für die Typen der Geschäftsobjekte vorgenommen haben, können wir nach algebraischen Strukturen bei Zugangsbereichen und Zugangsberechtigungen suchen. Algebraische Strukturen entstammen der Mathematik und definieren sich anhand einer Menge von Elementen (in unserem Fall der Typ), einer kombinierenden Operation auf dieser Menge und Eigenschaften der Operation. Es gibt viele verschiedene algebraische Strukturen mit unterschiedlicher Komplexität; es ist sinnvoll hier mit den grundlegenden Strukturen zu beginnen und sich schrittweise zu den komplexeren vorzuarbeiten. Je komplexer die Struktur ist, die wir für einen Typ identifizieren, desto mehr Gesetzmäßigkeiten gelten für ihn.

Magma

Ein Magma ist eine Menge von Elementen, auf denen eine Operation definiert ist, die für jedes Paar von Elementen ein drittes Element derselben Menge liefert. Diese Struktur ist jedoch nicht notwendigerweise assoziativ oder kommutativ. Jedes sinnvoll kombinierbare Geschäftsobjekt bildet zusammen mit seiner Kombinationslogik ein Magma. Ein gut nachvollziehbares Beispiel für ein Magma ist die Division als Operation auf der Menge der positiven Zahlen:

Magma (M, *) mit *:M×M→M

Halbgruppe

Die Halbgruppe baut auf den Eigenschaften des Magmas auf. Für die Operation gilt allerdings das Assoziativgesetz. Bei einer mehrfachen Ausführung der Operation hintereinander spielt die Reihenfolge, in der die Ergebnisse kombiniert werden, demnach keine Rolle. Ein Beispiel für eine Halbgruppe ist die Menge der natürlichen Zahlen mit der Addition als Operation:

Halbgruppe (M,*) mit *:M×M→M; ∀a,b,c∈M:a*(b*c)=(a*b)*c

Monoid

Der Monoid baut auf den Eigenschaften der Halbgruppe auf. Für die Operation gilt nicht nur das Assoziativgesetz, zusätzlich wird ein sogenanntes neutrales Element benötigt. Wird dieses als einer der Parameter der Operation verwendet, entspricht das Ergebnis immer exakt dem anderen Parameter. Ein Beispiel für ein Monoid ist die Multiplikation der natürlichen Zahlen und der 1 als neutralem Element.

Monoid (M,*) mit *:M×M→M; ∀a,b,c∈M:a*(b*c)=(a*b)*c

und dem neutralen Element n mit ∀a∈M:a*n=a

Gruppe

Die Gruppe ist die nächstkomplexere Struktur auf Basis des Monoiden und erweitert diesen um ein Inverses. Das Inverse ist ein Element, das bei der Anwendung der Operation auf ein anderes Element das neutrale Element als Ergebnis zurückliefert. Es muss sich dabei nicht um genau ein Element handeln, es muss lediglich eines für jedes andere Element der Menge existieren. Ein Beispiel für eine Gruppe ist die Menge der ganzen Zahlen mit der Addition als Operation, wobei 0 das neutrale Element und -x das Inverse von x ist:

Gruppe (M,*) mit *:M×M→M; ∀a,b,c∈M:a*(b*c)=(a*b)*c

und dem neutralen Element n mit ∀a∈M:a*n=a

und dem Inversen ∀a∈M:a*a-1=n

Algebraische Struktur Assoziativität Neutrales Element Inverses
Magma X X X
Halbgruppe X X
Monoid X
Gruppe

Tabelle 1: Vergleich verschiedener algebraischer Strukturen

Algebraische Strukturen identifizieren

Nehmen wir uns also die Typen der Geschäftsobjekte mit dem größten Potenzial vor und schauen, ob und welche algebraische Struktur wir hier identifizieren können.

Zugangsbereich:

  • Elemente: Die Menge besteht aus allen Zugangsbereichen, die auf Gebäudekomplexen definiert werden können.

  • Operation: Eine Operation, um aus zwei Zugangsbereichen einen neuen zu bilden, ist die hierarchische Unterordnung. Mit dieser können zum Beispiel zwei Etagen zu einem Gebäude zusammengefasst werden. Bei Zugangsbereichen ist es außerdem möglich, mehr als nur zwei Elemente zu einem neuen Element zu kombinieren, zum Beispiel bei einem drei- oder vierstöckigen Gebäude.

  • Assoziativität: Die hierarchische Unterordnung von Zugangsbereichen ist nicht assoziativ. Es macht durchaus einen Unterschied, ob Etagen erst zu einem Gebäude und dann zu einem Gebäudekomplex zusammengefasst werden oder ob sie erst zu einem Gebäudekomplex und dieser dann einem Gebäude zugeordnet wird.

  • Neutrales Element: Aus der Fachlichkeit ergibt sich kein sinnvolles neutrales Element in der Menge der Zugangsbereiche. Es gibt keinen Zugangsbereich, der bei der hierarchischen Unterordnung keine Veränderung bewirken würde.

  • Inverses: Da es kein sinnvolles neutrales Element in der Menge der Zugangsbereiche gibt, kann auch kein Inverses definiert werden.

  • Fazit: Den Zugangsbereichen liegt nur eine sehr rudimentäre algebraische Struktur zugrunde. Durch die Kombinationsfähigkeit bilden sie ein Magma.

Zugangsberechtigung:

  • Elemente: Die Menge besteht aus allen Zugangsberechtigungen, die zwischen Gebäudekomplexen und Personen vergeben werden können.

  • Operation: Es gibt mehrere fachlich relevante Operationen, die auf Zugangsberechtigungen angewendet werden können. Die offensichtlichste ist die Vereinigung. Diese wird üblicherweise angewendet, wenn in der Beschreibung der Berechtigungen das Wort „und“ verwendet wird. Zum Beispiel der Zugang zum Eingangsbereich eines Gebäudes und zur zweiten Etage. Eine weitere Operation ist die Differenz. Diese wird verwendet, wenn in der Beschreibung das Wort „außer“ verwendet wird. Zum Beispiel der Zugang zur zweiten Etage außer dem Aktenlagerraum.

  • Assoziativität: Die Vereinigung von Zugangsberechtigungen ist assoziativ. Es spielt keine Rolle, in welcher Reihenfolge die Berechtigungen kombiniert werden, das Ergebnis bleibt immer gleich. Die Differenz ist jedoch nicht assoziativ, da die Reihenfolge der Operationen das Ergebnis beeinflusst.

  • Neutrales Element: Das neutrale Element für die Vereinigung ist die leere Zugangsberechtigung, da sie bei der Vereinigung keine Veränderung bewirkt. Sie entspricht dem fachlichen Umstand, dass eine Person schlicht keine Zugangsberechtigung hat. Für die Differenz gibt es kein neutrales Element.

  • Inverses: Jede Zugangsberechtigung erweitert die Menge der Zugangsbereiche, die eine Person betreten darf. Demnach ist es nicht möglich, Zugangsberechtigungen zu definieren, die der Person Zugang zu Bereichen entzieht. Es kann also kein Inverses definiert werden.

  • Fazit: Die Zugangsberechtigungen bilden mit der Vereinigung einen Monoiden. Mit der Differenz bilden sie jedoch lediglich ein Magma, da hier weder Assoziativität noch ein neutrales Element gegeben sind.

Algebraische Datentypen

Um die Erkenntnisse, die wir bis jetzt zu den Geschäftsobjekten gewonnen haben, in der Software abbilden zu können, bieten sich algebraische Datentypen an. Hierbei unterscheidet man zwei verschiedene Typen: Produkte und Summen (Kasten: „Produkt und Summe“). Produkte sind Datentypen, deren Wert sich aus anderen Datentypen zusammensetzt. Summen sind Datentypen, deren Wert sich aus einer Auswahl von Ausprägungen ergibt. Es gibt keine feste Zuordnung zwischen Entities und Value Objects auf der einen und Produkten und Summen auf der anderen Seite (Abb. 1).

schramm_pattern_1
Abb. 1: Schematische Darstellung algebraischer Datentypen

Produkt und Summe

Die Bezeichnung der algebraischen Datentypen Produkt und Summe ergibt sich aus der Rechenvorschrift, mit der die Menge aller Werte bestimmt werden kann. Die Menge aller möglichen Werte eines Produkttyps ergibt sich aus der Multiplikation der Menge aller Werte der einzelnen Eigenschaften. Die Menge aller möglichen Werte eines Summentyps ergibt sich aus der Addition aller möglichen Werte der einzelnen Ausprägungen.

Algebraische Datentypen definieren

Jene Geschäftsobjekte, denen wir algebraische Strukturen zuweisen konnten, lassen sich besonders gut durch Produkte abbilden (Abb. 2). Dabei werden einzelne Ausprägungen genutzt, um die rekursive Vereinigung oder Differenz darzustellen. In funktionalen Programmiersprachen werden algebraische Datentypen meist vom Typsystem direkt unterstützt. Objektorientierte Sprachen arbeiten hauptsächlich mit Produkten, es gibt jedoch auch Möglichkeiten, um Summen abzubilden (zum Beispiel per Sealed Classes in Java).

schramm_pattern_2
Abb. 2: Geschäftsobjekte als algebraische Datentypen

Beispiele

Die beiden Beispiele für Personen mit Zugangsberechtigungen in Abbildung 3 und 4 verdeutlichen, wie fachliche Regeln zur Zugangskontrolle in der Struktur der Datenobjekte abgebildet werden. Die Struktur allein ermöglicht den Rückschluss auf fachliche Regeln, ohne entsprechende Algorithmen oder Programmlogik zur Auswertung zu kennen. Die erste Person stellt einen Büroangestellten dar. Dieser hat Zugang zum Eingangsbereich des Gebäudes, dessen Tiefgarage sowie zur zweiten Etage, auf der sich sein Büro befindet (Abb. 3). Die zweite Person stellt einen Hausmeister dar. Dieser hat Zugang zum gesamten Gebäude, mit Ausnahme eines sensiblen Aktenlagerraumes (Abb. 4).

schramm_pattern_3
Abb. 3: Objekt des Büroangestellten, abgebildet durch algebraische Datentypen
schramm_pattern_4
Abb. 4: Objekt des Hausmeisters, abgebildet durch algebraische Datentypen

Fazit

Wir haben ein Modell entworfen, das die Fachlichkeit auf explizite Weise darstellt. Die Analyse auf Basis der algebraischen Strukturen ist dabei nicht nur für Entwickler, sondern auch für Fachexperten aufschlussreich und gewährt eine neue Perspektive und Einblicke in die Domäne. Auch wenn die Menge der Ausprägungen eines Produktes fest ist, so lässt sie sich doch im Verlauf der Entwicklung und des Betriebs durch Entwickler einfach erweitern, was die Flexibilität der Software gegenüber fachlichen Änderungen gewährleistet.

Die Rekursion innerhalb der Produkte ermöglicht den Aufbau nahezu beliebig komplexer Instanzen, ohne zusätzliche fachliche Regeln oder ein Aufblähen der Implementierung zu verursachen. Diese Eigenschaft resultiert aus den identifizierten Kombinatoren (Vereinigung, Differenz usw., Kasten: „Kombinatoren“) und lässt sich als fachliche Skalierbarkeit beschreiben.

Kombinatoren

Ein Kombinator beschreibt in der Softwareentwicklung eine in sich geschlossene Funktion. Diese benötigt nur die ihr übergebenen Parameter und greift sonst auf keine Konstanten oder Variablen ihrer Umwelt zu. Kombinatoren im Lösungsraum sind das Gegenstück zu jenen Funktionen, mit denen im Problemraum algebraische Strukturen gebildet werden.

🔍 Frequently Asked Questions (FAQ)

1. Was ist das Ziel der fachlichen Analyse im Domain-Driven Design?
Ziel ist ein Entwurf, der die Fachlichkeit in Struktur und Begrifflichkeit korrekt abbildet und auf zukünftige Änderungen anpassbar bleibt. Dabei steht die Modellierung des Problemraums im Zentrum, nicht die Technik.

2. Wie unterscheiden sich Problemraum und Lösungsraum?
Der Problemraum beschreibt das „Was“ eines Systems, also Anforderungen und fachliche Rahmenbedingungen aus Sicht der Stakeholder. Der Lösungsraum beschreibt das „Wie“, also Design, Architektur und Implementierung zur Umsetzung dieser Anforderungen.

3. Welche Rolle spielen Entities und Value Objects?
Entities besitzen einen eindeutigen Identifikator und einen Lebenszyklus, während Value Objects durch ihre Eigenschaften definiert und unveränderlich sind. Diese Unterscheidung hilft, Geschäftsobjekte strukturell korrekt im Modell abzubilden.

4. Wie werden Geschäftsobjekte im Beispiel der Zugangskontrolle eingeordnet?
Zugangsbereiche werden als Value Objects modelliert, Personen als Entities mit Identifikator und Lebenszyklus. Zugangsberechtigungen werden als Value Objects verstanden, die Referenzen auf Zugangsbereiche enthalten.

5. Warum werden algebraische Strukturen in der Domänenanalyse betrachtet?
Algebraische Strukturen beschreiben Mengen von Elementen mit definierten Operationen und deren Eigenschaften wie Assoziativität oder neutrales Element. Durch ihre Identifikation lassen sich fachliche Kombinationsregeln präzise und formal im Modell ausdrücken.

6. Welche algebraischen Strukturen wurden für Zugangsbereiche und Zugangsberechtigungen identifiziert?
Zugangsbereiche bilden aufgrund ihrer Kombinierbarkeit ein Magma. Zugangsberechtigungen bilden mit der Vereinigung einen Monoid, mit der Differenz hingegen nur ein Magma.

7. Wie werden diese Erkenntnisse in Software umgesetzt?
Die fachlichen Strukturen werden mithilfe algebraischer Datentypen, insbesondere Produkten und Summen, modelliert. Rekursive Produkte ermöglichen die Abbildung komplexer Berechtigungsstrukturen ohne zusätzliche fachliche Logik.

8. Welchen Nutzen bietet dieser Ansatz für Entwicklung und Fachlichkeit?
Die Struktur der Datenobjekte bildet fachliche Regeln explizit ab und erlaubt Rückschlüsse ohne zusätzliche Algorithmen. Gleichzeitig bleibt das Modell erweiterbar und unterstützt fachliche Skalierbarkeit durch klar definierte Kombinatoren.

The post Pattern in your Domain appeared first on MAD Summit.

]]>
Wie heutige AI-Mittel den Umgang mit Software verändern https://mad-summit.de/blog/modulare-software-architektur-microservices/ai-gestuetzte-softwarearchitektur-heutige-werkzeuge/ Mon, 20 Oct 2025 13:30:08 +0000 https://mad-summit.de/?p=107500 In einer Zeit, in der künstliche Intelligenz und AI-Werkzeuge zunehmend Teil der Softwareentwicklung werden, stellt sich die Frage: Wie verändert sich eigentlich die Softwarearchitektur, wenn Prototypen in Stunden statt Wochen entstehen und klassische Architekturarbeit neu gedacht werden muss? Dieser Artikel beleuchtet, wie AI-gestützte Softwareentwicklung, Vibe Coding, parallele Experimente und Context Engineering die Rolle von Entwickler:innen und Architekt:innen verschieben – und warum Architektur nach wie vor soziotechnisch bleibt.

The post Wie heutige AI-Mittel den Umgang mit Software verändern appeared first on MAD Summit.

]]>
Diskussionen über Artificial Intelligence (AI) drehen sich oft um zukünftige Möglichkeiten und überzogene Produktivitätsversprechungen. Relevanter ist jedoch, was gerade jetzt passiert: Was leisten heute verfügbare AI-Werkzeuge und welche Auswirkungen hat das auf unsere Art, Softwaresysteme zu entwickeln?

Wir lesen täglich Marketingmeldungen von der „Disruption durch AI“, von „10x Produktivität“ von „AGI“ (Allgemeiner künstlicher Intelligenz), die angeblich nicht mehr lange auf sich warten lässt. Immer wieder wird spekuliert, welche Rollen und Jobs in Zukunft nicht mehr gebraucht werden. Auf technischer Seite explodiert die Anzahl von Copiloten, MCP-Servern und Agentic Systems. Es ist anstrengend und geil zugleich.

Auch wenn AI auf Marketingebene nur schwer zu ertragen scheint, ist die technische Seite interessant bis inspirierend. Je mehr man sich mit den Möglichkeiten und Werkzeugen auseinandersetzt, desto klarer wird, dass AI-gestützte Softwareentwicklung auch „nur“ Engineering ist – gleichzeitig ist der Umgang mit Werkzeugen anders, Aufgaben verschieben sich und einige Wahrheiten der Softwareentwicklung werden infrage gestellt. Ganz unaufgeregt und nüchtern lässt sich nach einigen Jahren Erfahrung feststellen: Softwareentwicklung, Systemdesign und Architektur verändern sich.

Softwarearchitektur schafft traditionell Ordnung im Komplexen – sie strukturiert, abstrahiert und investiert bewusst in das „schwer Änderbare“. Als Planungsdisziplin erfordert Architekturarbeit einen gewissen Abstand von der konkreten Implementierung und hat den Zweck, teure Fehlentwicklungen zu vermeiden. Mit Sprachmodellen, Coding Assistants und Context Engineering wird nun vieles günstiger: Prototypen entstehen in Stunden statt Wochen, Varianten lassen sich schneller umsetzen, Lösungen rascher portieren. Die Grenze zwischen „schwer“ und „leicht änderbar“ wird neu gezogen. Gleichzeitig entstehen neue Herausforderungen, denen wir uns stellen müssen.

 

Vibe Coding: die Einstiegsdroge

Der erste Kontakt mit AI-Tools ist wohl meist das Chatinterface eines Large Language Models (LLM). Diese Modelle geben bei Fragen gerne mal konkrete Antworten und wenn man technische Fragen stellt, entstehen auf einmal Schnittstellenspezifikationen, Teilimplementierungen oder ausführbare Skripte. Mit CLI-Tools (Command Line Integration) oder LLM-Integration in IDEs ist es verlockend, diese Lösungen direkt zu verwenden. Mit etwas Routine könnte man in einen „Flow-Zustand“ rutschen, in dem man AI-Tools eher sorglos nutzt, pragmatische Bedenken beiseiteschiebt und dafür rasche Fortschritte feiert. Vibe Coding ist geboren.

In experimentellen, risikoarmen Umfeldern kann dieses Vorgehen durchaus überzeugen und so haben viele von uns schon mal ein kleines Tool zum persönlichen Gebrauch „gevibet“. Der Fortschritt kann beim richtigen Problem beeindruckend sein, die Produktivitätsversprechen der AI Bubble wirken realistischer. Dieser erste Rausch erzeugt im Gleichschritt Anhänger der AI-Bewegung und Leute, die solch oberflächliche Entwicklungspraktiken ablehnen. Ist das tatsächlich der Kern dessen, was LLMs für die Softwareentwicklung leisten können?

 

Systeme modernisieren statt nur migrieren

Power-Workshops zu Modernisierung & Service-Architektur (22. - 26. Juni 2026, München)

 

AI-gestützte Softwareentwicklung

Vibe Coding ist ein mentales Modell oder eine Einstellung, orthogonal zur konkreten Werkzeugwahl. Wir können Softwarelösungen auch außerhalb des AI-Kontexts schnell und ohne viel Designaufwand umsetzen. Früher haben wir es Quick Hack genannt oder auf höherer Ebene von „zufälliger Architektur“ gesprochen. Trennen wir diesen Zugang von den technischen Möglichkeiten, lässt sich ein ganzes Feld aufspannen, in dem wir professionell AI-gestützt Software entwickeln.

toth_ai_architektur_1
Abb. 1: Ansätze für AI-gestützte Softwareentwicklung

Abbildung 1 zeigt unterschiedliche Entwicklungsparadigmen, die mit LLM-Unterstützung entstehen:

  • Conversational Programming: Software wird iterativ im Dialog mit einem LLM entwickelt (z. B. ChatGPT, IDE-Plug-ins, Copilots).

  • Prompt-Driven Development (PDD): Low-Level Coding wird durch natürliche Sprachspezifikationen ersetzt. Wichtig sind Prompt-Templates, Spezifikation-zu-Code-Pipelines und Prompt-Management-Systeme.

  • Agentic Development: Entwicklung wird an autonome Agenten delegiert, die Aufgaben planen, umsetzen und orchestrieren (Agent-Frameworks, Swarm-Ansätze, Orchestration Runtimes, Shared Memory).

Komplexere Set-ups wie Product Requirement Prompts (PRPs [1]) oder Agent Swarms (z. B. Claude Flow [2] oder Archon [3]) versuchen zwar, Kernprobleme des einfacheren Conversational Programmings zu adressieren, bringen jedoch neue Herausforderungen mit sich. Wir können mit einem Konzert aus Agents z. B. einfachen Halluzinationen begegnen, bringen allerdings auch mehr Autarkie in die Entwicklung. Dadurch können inhaltsleere Dummy-Implementierungen, übersprungene Tests oder zu einfache Lösungsalternativen entstehen. Bei naiver Anwendung ist man häufig mit Erfolgsmeldungen konfrontiert, die wenig mit dem tatsächlichen Implementierungserfolg zu tun haben. Jeder Ansatz hat seinen eigenen Sweet Spot, alle haben aber Schwierigkeiten damit, sich konsistent an eine Entwicklungskultur zu halten, Tokens sparsam zu behandeln, funktionierende Lösungen zu schützen oder Daten out of the box zu schützen.

Auch der Aufwand einer Umsetzung schwankt durch den Einsatz von AI-Mitteln mehr. Gab es früher etwas Varianz im Entwicklungsaufwand zu schätzen, ist es heute schwierig zu sagen, ob eine Lösung fast gratis umgesetzt werden kann oder man nach einigen gescheiterten AI-Versuchen doch bei einer manuellen Implementierung als einziger Lösungsmöglichkeit landet.

All das erfordert nicht nur Erfahrung im Umgang mit den genannten Ansätzen und Werkzeugen, sondern auch einige neue Strategien im Umgang mit AI-gestützter Entwicklung.

 

Strategien für bessere AI-Entwicklung

Einige Best Practices im Umgang mit AI-Werkzeugen bilden sich mit etwas Erfahrung recht schnell heraus. In unseren Implementierungsvorhaben schneiden wir Aufgaben klein (auch jene für Agents) und nutzen evtl. vorhergehende, breitere Prompts, um Lösungsmöglichkeiten zu verstehen und das Problem zu strukturieren. Die Ergebnisse solcher frühen Prompts werfen wir meist weg. Wir arbeiten prinzipiell in Sandboxes [4], in kleinen Schritten und bereichsintensiv. Wir versuchen den Aktionsradius von LLMs über globale Regeln zu beschränken und darin auch Entwicklungskultur zu definieren. All das hat eine architektonische Komponente, wir können AI-Tools aber auch wirklich als Architekturwerkzeuge verstehen und einsetzen. Mit diesem Blickwinkel lassen sich einige Verschiebungen beobachten – ich greife in der Folge drei davon heraus. Sie sind in Abbildung 2 illustriert.

toth_ai_architektur_2
Abb. 2: Der neue Fokus von Architekturarbeit 

Vom impliziten zum expliziten Kontext

Wir können nicht annehmen, dass AI-Werkzeuge über gesunden Menschenverstand verfügen oder schon länger dabei sind und deshalb schon wissen, wie ein Problem in unserem Umfeld richtig gelöst wird. AI-Werkzeuge können zwar schnell generischen Code produzieren, ein großer Hebelum architektonisch brauchbare Lösungen zu erhalten, liegt jedoch im Context Engineering [5]. Was früher implizit in Teamkulturen und Entwicklerköpfen existierte, muss maschinenlesbar werden: Wir betten Architekturprinzipien, Kommunikationsmuster oder für die Architektur wichtige Qualitätskriterien deshalb in den Kontext von LLMs ein. Von einfachen Sichtbarkeitsregeln oder dem gewollten Einsatz von Technologien können wir bis zu kodifizierten Compliance-Anforderungen gehen.

Neben einfachen Einträgen in ein agents.md-File gibt es auch größere Keulen. Wir können z. B. jede AI-gestützt entwickelte Funktion automatisch gegen regulatorische Vorgaben prüfen, die wir zuvor in einem Knowledge-Graph hinterlegt haben. So wird klar, ob personenbezogene Daten korrekt verschlüsselt sind oder ob die Auditlogs vollständig sind. Diese Explizitmachung betrifft alle Ebenen der Architektur. Service-Graphen können erlaubte Kommunikationswege dokumentieren. Architecture Decision Records (ADRs) werden zu strukturierten Daten, die LLMs verstehen und berücksichtigen können. Commit-Messages folgen Schemata, die Intentionen maschinell auswertbar machen …

 

 

Von sequenzieller zu paralleler Lösungsfindung

Das klassische Vorgehen Analyse → Design → Implementierung wird unpraktikabel, wenn Lösungen sehr schnell (prototypisch) umgesetzt werden können. Wir arbeiten deshalb intensiver mit hypothesengetriebener Entwicklung und parallelen Experimenten.

Nehmen wir an, wir beobachten immer höhere Lastspitzen in unserer Anwendung und brauchen eine Strategie, um damit umzugehen. Während wir früher viel auf Diskussions- und Analyseebene geregelt haben, können wir heute Varianten definieren, umsetzen und testen: Hilft uns horizontale Skalierung mit Kubernetes? Sollten wir einen Cache-Layer mit Redis einführen, um die Datenbanklast zu reduzieren? Können Serverless Functions für einige zentrale Endpunkte helfen?

In mehreren Projekten konnten wir solche Fragen praxisnah klären, indem wir die Hypothesen AI-gestützt umgesetzt haben, statt sie lange theoretisch zu diskutieren. Lasttests liefern anschließend belastbare Daten, die Entscheidung basiert auf Fakten, nicht auf Vermutungen.

Dieses Vorgehen birgt Vorteile auf vielen Ebenen: Um belastbare Vergleiche anzustellen, beschäftigen wir uns intensiver mit Zielsetzungen und deren Quantifizierung. Architekturrollen, die etwas umsetzungsferner waren, finden zurück zum Code. Probleme mit Kompatibilitäten oder andere versteckte Schwierigkeiten sind im Auswahlprozess sichtbar.

 

Von Vorarbeit zu kontinuierlicher Begleitung

Architekturarbeit beinhaltet Überlegungen zu technischen Optionen und Risiken, versucht Erkenntnisse aus Umsetzungsarbeit zu verallgemeinern und so Qualitätsziele zu erreichen. Wenn wir nun hypothesenorientiert arbeiten und potenziell mehrere Optionen parallel verfolgen, wird es wichtiger, kontinuierliches Feedback in Form von Guardrails und Fitness Functions zu etablieren. Statt Lösungen einzuschränken und punktuell manuell zu kontrollieren, entstehen kontinuierliche Leitplanken. Sie machen Vergleiche technischer Alternativen möglich, Qualität neutral bewertbar und dezentral auswertbar. Konkret verwenden wir z. B. Review-Agents, die bei jedem Pull Request Architekturvorgaben prüfen (Schichtung, API-Kommunikation, Pattern- und Technologieverwendung …).

Diese Guardrails kann man mit Fitness Functions wie Performancebenchmarks und Security-Scans ergänzen und so stetig prüfen, ob architektonische Eigenschaften gegeben sind, ohne die Architekturidee selbst zu stark einzuschränken. Jeder generierte Code durchläuft sofort Unit- und Policy-Checks. Agenten, die Varianten erzeugen, stoppen automatisch, wenn Abbruchkriterien verletzt sind. Zusammen mit explizitem Kontext entsteht eine Klammer um AI-unterstützte Lösungen, die uns einen sicheren Umgang mit AI-Werkzeugen ermöglicht, ohne zu viel Entwicklungsgeschwindigkeit zu rauben.

 

Neue Wege für klassische Architekturaufgaben

Die drei Verschiebungen – expliziter Kontext, parallele Experimente, kontinuierliche Guardrails – verändern Architekturarbeit. Sie wird kleinteiliger, experimenteller, automatisierter. Gleichzeitig verschieben sich auch Informationen wie Architekturmuster, Qualitätsziele oder Prinzipien „weicher“ Dokumente wie Wikis im systematischen Kontext für LLMs. Architekturkonzepte haben so gesichert Auswirkungen auf die Lösung, müssen aber auch expliziter und genauer beschrieben werden als bisher. Neben diesem Wandel können wir aber auch eher klassische Aufgaben der Architektur mit AI-Werkzeugen unterstützen. Abbildung 3 zeigt einige Ideen dafür.

toth_ai_architektur_3
Abb. 3: Möglichkeiten der AI-Unterstützung in der Softwarearchitektur

AI Agents sind prädestiniert dafür, die typischen „Metaaufgaben“ der Architektur zu unterstützen, also Aufgaben, die eine übergreifende Sichtweise und Einordnung brauchen. Eine Schwachstelle sind im Moment noch Kategorisierungsaufgaben, in denen etwa Probleme nach ihrer Ähnlichkeit zusammengefasst werden sollen. Selbst dort ist aber die Interaktion mit LLMs und spezifisch orientierten Agents für die Unterstützung der eigenen Gedankengänge hilfreich. Wenn es darum geht, den Lösungsraum zu strukturieren oder technische Optionen zu finden, wird die Unterstützungsleistung besser. Gut funktionieren analytische Agents, die Code, Dokumentation, Konfiguration oder Monitoringdaten als Grundlage verwenden. Damit sind Gap-Analysen, einfachere Dokumentationsaufgaben (die nicht zu sehr abstrahieren) und Reviews oft im Fokus von AI-Architekturinitiativen.

Bei all diesen Möglichkeiten ist jedoch Vorsicht geboten. Nicht jede Architekturaufgabe sollte in Zukunft eins zu eins mit AI Agents ersetzt werden (selbst wenn das ginge). Einige Aufgaben werden sich in Zukunft grundlegend ändern. Nehmen wir z. B. das Thema Architekturdokumentation. In der Vergangenheit haben wir uns darauf fokussiert, Wissen zur Lösung so aufzubereiten, dass es bei zukünftigen Problemen helfen kann. Zum Dokumentationszeitpunkt kennen wir diese Probleme allerdings nicht genau und dokumentieren deshalb eher allgemein. (Alternativ kann auch viel spezifische Dokumentation entstehen, die wegen ihrer Größe dann oft nicht mehr gewartet wird und veraltet.)

So oder so bleibt festzuhalten, dass Dokumentation für ein Problem gebaut wird, das wir noch nicht haben. Ist es eine neue Mitarbeiterin, die einen allgemeinen Überblick braucht? Ist es ein größeres Refactoring, das unser Datenmodell zerpflücken soll? Sind es Performanceprobleme, die unsere Verwendung von Events infrage stellen? Wir wissen es nicht. Mit LLMs und AI Agents sind wir plötzlich in der Lage, zum Zeitpunkt des eigentlichen Problems die richtigen Informationen zusammenzustellen. Wir haben ein Instrument, das viele Informationen kennt – Code, Tickets, Monitoring, vergangene Prompts zur Entwicklung usw. Statt den Umweg über bekannte Dokumentationstemplates zu gehen, bietet es sich hier an, eher Context Engineering für das LLM zu betreiben und ein Unterstützungswerkzeug für die Problemlösung zu schaffen. Manuell erstellte Architekturdokumentation reduziert sich eher auf vereinfachende Übersichten, die in ihrer Knappheit leicht zu pflegen sind und ohnehin nicht im Sweet Spot momentaner LLMs liegen.

 

Flow und Vibe kaputt?

In diesem Artikel haben wir den Bogen von Vibe Coding und AI-gestützter Entwicklung bis hin zur Architekturdisziplin geschlagen. Schon heute lässt sich beobachten, dass sich klassische Rollen wie Entwicklerinnen und Architektinnen anders verhalten (können), wenn es einen Kontext von AI-unterstützenden Werkzeugen gibt. Doch was macht den Wandel durch AI eigentlich aus? Ist es die rohe Geschwindigkeit der Entwicklung? Ist es zehnfache Produktivität? In der Praxis zeigt sich eher, dass Softwareentwicklung auch mit AI-Unterstützung ein tiefes Engineering-Feld bleibt.

Softwarearchitektur erlebt eine Verschiebung hin zu kontinuierlicher, expliziterer Architekturarbeit, die parallele Experimente unterstützt. Bei richtigem Tooleinsatz und Vorgehen bremst das alles wenig, verschafft aber etwas mehr Überblick und Zielrichtung.

 

Stay tuned

Immer auf dem Laufenden bleiben! Alle News & Updates:

[mc4wp-simple-turnstile]

 

 

🔍 Frequently Asked Questions (FAQ)

1. Was verändert sich durch AI-Werkzeuge in der Softwareentwicklung?

AI-Werkzeuge wie Coding-Assistenten, Agent-Frameworks oder LLM-Plug-ins beschleunigen viele Entwicklungsprozesse erheblich. Prototypen entstehen schneller, Varianten lassen sich leichter testen, und Architekturentscheidungen können datenbasiert überprüft werden.

2. Was bedeutet „Vibe Coding“?

„Vibe Coding“ beschreibt eine spontane, oft unstrukturierte Nutzung von AI-Tools beim Programmieren – meist über Chatinterfaces oder IDE-Integrationen. Entwickler:innen folgen dabei eher dem Flow und setzen Lösungen pragmatisch um, statt formale Architekturprozesse zu befolgen.

3. Welche neuen Ansätze gibt es für AI-gestützte Softwareentwicklung?

Zu den neuen Paradigmen zählen Conversational Programming, Prompt-Driven Development (PDD) und Agentic Development. Diese Methoden ermöglichen es, Code und Systeme durch Dialog, Prompts oder autonome Agents zu entwickeln und zu orchestrieren.

4. Was ist „Context Engineering“ und warum ist es wichtig?

Context Engineering beschreibt die bewusste Gestaltung maschinenlesbarer Kontexte für AI-Tools. Architekturprinzipien, Qualitätsregeln und Teamkultur werden explizit hinterlegt, damit LLMs und Agents bessere, konsistente Ergebnisse liefern können.

5. Wie verändern AI-Tools die klassische Architekturarbeit?

Architekturarbeit wird iterativer und datengetriebener. Statt einmaliger Vorarbeit steht heute eine kontinuierliche Begleitung mit Guardrails, Fitness Functions und automatisierten Architekturprüfungen im Fokus. Das schafft Qualitätssicherung bei hoher Umsetzungsgeschwindigkeit.

6. Welche Chancen bieten parallele Experimente in der Architektur?

Mit AI-Unterstützung können Architekt:innen mehrere Lösungsvarianten parallel implementieren und testen. So entstehen belastbare Vergleiche auf Basis echter Performance- und Qualitätsdaten, statt rein theoretischer Annahmen.

7. Wer profitiert besonders von AI-gestützter Architekturarbeit?

Vor allem Softwarearchitekt:innen, Entwickler:innen und DevOps-Teams profitieren: Sie können effizienter planen, schneller experimentieren und fundiertere Entscheidungen treffen. Auch Organisationen gewinnen, da Wissen strukturierter und reproduzierbarer wird.

The post Wie heutige AI-Mittel den Umgang mit Software verändern appeared first on MAD Summit.

]]>
API Observability richtig umsetzen: Performance, Sicherheit & Zuverlässigkeit steigern https://mad-summit.de/blog/api/api-observability-tools-best-practices/ Tue, 12 Aug 2025 09:09:15 +0000 https://mad-summit.de/?p=107357 API Observability ist der Schlüssel zu leistungsstarken, sicheren und zuverlässigen Schnittstellen. Durch verteiltes Tracing, API Monitoring und den Einsatz moderner Observability Tools wie Prometheus, Grafana oder Jaeger lassen sich Leistungsengpässe frühzeitig erkennen, API Performance optimieren und Sicherheitsbedrohungen effektiv abwehren.

The post API Observability richtig umsetzen: Performance, Sicherheit & Zuverlässigkeit steigern appeared first on MAD Summit.

]]>
Wie lässt sich Observability in API-Umgebungen implementieren – und warum sollten wir das überhaupt tun? Wir überprüfen die Auswirkungen in Hinblick auf Leistung, Fehlerbehebung und Sicherheit. Und schließlich schauen wir auf Best Practices zur Integration von Observability in die API-Infrastruktur, mit deren Hilfe wir optimale Leistung und Ausfallsicherheit gewährleisten können.

APIs sind in der heutigen digitalen Landschaft das Rückgrat moderner Software. Sie steuern alles von einfachen Webanwendungen bis hin zu komplizierten Microservices-Architekturen. Je komplexer API-Ökosysteme werden, desto wichtiger ist auch ihre zuverlässige Überwachung und eine effektive Fehlerbehebung. Dabei hilft uns die Praxis der „Observability“.

 

Observability

Im Wesentlichen ist die Observability eine Strategie zum Verständnis des internen Zustands eines Systems durch die Analyse seiner externen Ausgaben. Einfach ausgedrückt ist es so, als könnte man in eine komplexe Maschine „hineinsehen“ und Einblicke in ihre Abläufe gewinnen, auch ohne direkten Zugang zu ihren internen Komponenten.

Mit diesem Ansatz ermöglicht die Observability den Ingenieur:innen,

  • Grundursachen von Problemen schnell zu erkennen und zu diagnostizieren,

  • potenzielle Probleme oder Ausfälle zu antizipieren, bevor sie entstehen, und

  • Systeme für maximale Effizienz und Leistung zu optimieren.

Entwickle Schnittstellen, die einfach funktionieren

Power-Workshops zu API Design & Entwicklung (22. - 26. Juni 2026, München)

 

Wie unterscheiden sich Observability und Monitoring?

Obwohl sie eng miteinander verbunden sind, dienen Monitoring und Observability unterschiedlichen Zwecken:

  • Monitoring erfasst bestimmte Messwerte und richtet Warnmeldungen ein, um den Zustand und die Leistung eines Systems zu verfolgen, ähnlich wie bei Körpertemperaturmessungen von Patient:innen, um Fieber zu erkennen.

  • Observability ist ein breiteres Konzept, das ein tieferes Verständnis des internen Zustands eines Systems durch die Analyse seiner externen Ausgaben ermöglicht. Es ist vergleichbar mit der Diagnose einer komplexen Krankheit durch Interpretation der Symptome und der Krankengeschichte von Patient:innen.

Beim Monitoring geht es darum, festzustellen, was während eines Problems passiert, während Observability ein proaktiver Ansatz ist, der dazu beiträgt, Probleme zu antizipieren und zu verstehen, bevor sie entstehen.

 

Herausforderungen bei der Anwendung von Observability in API-Umgebungen

API-Aufrufe gehen oft über ein einfaches Anfrage-Antwort-Muster hinaus. Asynchrone Vorgänge – wie z. B. solche, die Nachrichtenwarteschlangen oder Webhooks beinhalten – können es erschweren, Anfragen genau nachzuvollziehen.

Besonders komplex sind APIs, die mit mehreren Diensten, Datenbanken und externen Systemen interagieren. Eine Anfrage zu verfolgen, während sie diese verschiedenen Komponenten durchläuft, erfordert fortschrittliche Techniken. Außerdem können APIs verschiedene Datenformate verwenden (z. B. JSON, XML, Protobuf), welche die Datenanalyse und die Korrelationsbemühungen weiter verkomplizieren.

Ohnehin verarbeiten moderne APIs riesige Mengen an Datenverkehr, wobei sie ebenso große Mengen an Protokollen und Metriken generieren – dabei haben nicht alle Daten den gleichen Stellenwert. Für eine effektive Analyse ist es wichtig, das Rauschen herauszufiltern, um sich auf relevante Informationen zu konzentrieren.

Daten aus verschiedenen Quellen (z. B. Protokolle, Metriken und Traces) zu korrelieren, kann herausfordernd sein, insbesondere in verteilten Systemen, in denen das Zuordnen von Erkenntnissen über verschiedene Komponenten oft komplex ist.

Auch ist es wichtig, API-Probleme schnell zu erkennen, um deren Auswirkungen zu minimieren. Jedoch kann ein Übermaß an Warnmeldungen dazu führen, dass es schwieriger wird, Prioritäten zu setzen und auf kritische Probleme zu reagieren. Die Entwicklung effektiver Warnmeldungen erfordert eine sorgfältige Auswahl von Schwellenwerten, Bedingungen und Benachrichtigungsmethoden, um zeitnahe und umsetzbare Reaktionen zu gewährleisten.

APIs verarbeiten häufig sensible Daten, sodass es von entscheidender Bedeutung ist, sich vor unbefugtem Zugriff, Datenpannen und Bloßstellung zu schützen. Um Sicherheitsbedrohungen zu erkennen, kann es helfen, Metriken wie Authentifizierungsfehler, unbefugte Zugriffsversuche und Ereignisse zur Ratenbegrenzung zu überwachen. Außerdem muss die Erfassung und Analyse von API-Verkehrsdaten den Datenschutzbestimmungen wie der DSGVO und dem CCPA (California Consumer Privacy Act – Datenschutzgesetz aus Kalifornien, USA) entsprechen.

Um diese Sicherheits- und Observability-Herausforderungen zu bewältigen, ist es für Unternehmen ratsam, auf eine Kombination verschiedener Strategien zu setzen:

  • robuste Observability-Tools: mit Hilfe von Tools wie Prometheus, Grafana, Jaeger und Zipkin Daten sammeln, analysieren und visualisieren

  • effektives Filtern und Korrelation: Protokollfilter verwenden, Metriken und verteiltes Tracing gruppieren, um Rauschen zu reduzieren und Dateneinblicke zu verbessern

  • Echtzeit-Monitoring und -Warnungen: Konfiguration rechtzeitiger Warnmeldungen für kritische Metriken mit fortschrittlichen Warnsystemen zur Rauschminimierung

  • starke Sicherheitspraktiken: Durchsetzung robuster Authentifizierungs-, Autorisierungs- und Verschlüsselungsprotokolle sowie Durchführung regelmäßiger Sicherheitsaudits und Schwachstellenbeurteilungen

Wenn Unternehmen diese Herausforderungen sorgfältig angehen und geeignete Strategien anwenden, können sie Observability erfolgreich in ihren API-Umgebungen implementieren – und damit Leistung, Zuverlässigkeit und Sicherheit begünstigen.

 

Best Practices für die Implementierung von Observability in API-Umgebungen

Die richtigen Tools wählen

Bei der Auswahl von Observability-Tools sollten Faktoren wie die Fachkenntnisse des Teams, die Komplexität der API-Infrastruktur und spezifische Monitoring-Anforderungen berücksichtigt werden. Die Lösung sollte einen umfassenden Überblick über das API-Ökosystem bieten, einschließlich:

  • verteilte Tracing-Systeme: den Fluss der Anfragen im System verfolgen, um Leistungsengpässe und Fehler zu erkennen

  • Plattformen zur Log-Aggregation: Zentralisieren und Analysieren von Protokolldaten, um Trends, Anomalien und potenzielle Sicherheitsrisiken zu erkennen.

  • Metrik-Datenbanken: Speichern und Analysieren von Zeitreihendaten wie Responsezeiten, Fehlerraten und Ressourcennutzung, um Einblicke in die Leistung und den Zustand der API zu erhalten

Klare Monitoring-Ziele festlegen

Bevor Observability implementiert wird, sollten klare Ziele festgelegt werden. Zu den wichtigsten in Betracht zu ziehenden Zielen, gehört es,

  • Leistungsengpässe zu identifizieren: Antwortzeiten, Fehlerraten und Ressourcennutzung überwachen, um Bereiche mit Optimierungsbedarf zu erkennen.

  • Sicherheitsbedrohungen zu erkennen: Auf ungewöhnliche Datenverkehrsmuster, unbefugte Zugriffsversuche und andere Indikatoren für Sicherheitsrisiken achten.

  • Benutzerfreundlichkeit zu verbessern: Metriken wie Seitenladezeiten und Fehlerraten verfolgen, um Probleme zu erkennen, die die Zufriedenheit der Benutzer:innen beeinträchtigen könnten.

  • Hochverfügbarkeit sicherzustellen: Die Systemverfügbarkeit überwachen, um mögliche Ausfälle schnell zu erkennen und zu beheben.

  • Ressourcenauslastung zu optimieren: Ressourcennutzung analysieren, um Möglichkeiten für Kosteneinsparungen zu identifizieren.

Effektive Protokollierung implementieren

Gut strukturierte Protokolle sind für eine effektive Fehlersuche und -analyse unerlässlich. Einige grundlegende Praktiken sind die folgenden:

  • geeignete Protokollebenen verwenden: Verschiedene Protokollebenen verwenden – wie DEBUG, INFO, WARN und ERROR – um relevante Informationen zu erfassen, ohne die Protokolle zu überladen. Jede Stufe sollte die Signifikanz und Dringlichkeit der Meldung widerspiegeln.

  • kontextbezogene Informationen bereitstellen: Wichtige Details wie Zeitstempel, Anfrage- und Antwort-Header, Fehlermeldungen und Nutzer-IDs einfügen, um jeden Protokolleintrag aussagekräftig und umsetzbar zu machen.

  • strukturierte Protokollierung implementieren: Ein strukturiertes Format wie JSON oder YAML verwenden, damit die Protokolle leichter mit automatisierten Tools geparst und analysiert werden können.

  • Protokolle regelmäßig rotieren: Eine Strategie für die Rotation von Protokollen festlegen, um Speicherplatzprobleme zu vermeiden und die langfristige Verfügbarkeit von Protokollen zu gewährleisten, indem Protokolldateien und Aufbewahrungsrichtlinien verwaltet werden.

Verteiltes Tracing einsetzen

Verteiltes Tracing bietet Transparenz über den Fluss von Anforderungen in komplexen Systemen. Es kann dabei helfen,

  • weitergegebene Anfragen nachzuverfolgen: Der Weg sämtlicher Anfragen wird nachverfolgt, während diese verschiedene Dienste durchlaufen, um Einblicke in Abhängigkeiten und Interaktionen zu gewinnen.

  • Latenz zu messen: Engpässe und Verzögerungen werden durch genaues Bestimmen von langsamen Diensten oder Funktionen erkannt.

  • Fehlerraten zu analysieren: Komponenten mit hohen Fehlerquoten werden identifiziert, um die Systemzuverlässigkeit durch gezielte Korrekturen zu erhöhen.

Einsatz von Metriken und Warnungen

Metriken bieten entscheidende Einblicke in die Leistung und den Zustand des Systems. Die wichtigsten zu überwachenden Metriken sind unter anderem:

  • Antwortzeit: Misst die Zeit, die eine API benötigt, um eine Anfrage zu beantworten und hilft dabei, die Benutzerfreundlichkeit zu bewerten und mögliche Verlangsamungen zu erkennen.

  • Fehlerquote: Verfolgt den Prozentsatz der Anfragen, die zu Fehlern führen, was die Zuverlässigkeit des Systems aufzeigt und dabei hilft, Probleme zu priorisieren.

  • Durchsatz: Überwacht das Volumen der Anfragen, die pro Zeiteinheit bearbeitet werden und spiegelt die Systemkapazität und Skalierbarkeit wider.

  • Ressourcenauslastung: Analysiert die CPU-, Speicher- und Festplattenauslastung, um Ressourcenbeschränkungen zu erkennen und die Systemeffizienz zu optimieren.

Indem man effektive Warnmeldungen einrichtet, kann man sicherstellen, dass in Echtzeit über mögliche Probleme informiert wird. Für die Verwaltung und Priorisierung von Warnmeldungen empfiehlt sich die Nutzung von Tools wie Alertmanager oder PagerDuty.

 

Vorteile der Anwendung von Observability in API-Umgebungen

Observability-Tools erlauben wichtige Einblicke in die Systemleistung und ermöglichen es Teams, Probleme proaktiv zu erkennen und zu beheben. Durch die Analyse von Metriken wie Antwortzeiten, Fehlerraten und Ressourcennutzung helfen die Tools dabei, bestimmte API-Komponenten zu identifizieren, die Leistungsengpässe verursachen könnten, sodass Entwickler:innen den Code optimieren, die Hardware aufrüsten oder die Konfigurationen anpassen können. Die kontinuierliche Überwachung von API-Metriken ermöglicht es Teams außerdem, potenzielle Probleme frühzeitig zu erkennen und Präventivmaßnahmen zu ergreifen.

Observability-Tools ermöglichen die Echtzeiterkennung von Ausfällen und Fehlern. Durch die Analyse von Protokollen und Traces können Teams die Ursachen schnell diagnostizieren – unabhängig davon, ob sie auf Code, Infrastruktur oder externe Abhängigkeiten zurückzuführen sind.

Observability-Tools erlauben es den Teams weiter, durch die Identifizierung von Trends und Mustern im Systemverhalten potenzielle Ausfälle vorherzusagen und proaktive Maßnahmen zu ihrer Vermeidung zu ergreifen. Darüber hinaus können diese Tools die Sicherheit verbessern, indem sie verdächtige Aktivitäten wie ungewöhnliche Datenverkehrsmuster oder unbefugte Zugriffsversuche erkennen. Durch die Analyse von Protokollen und Sicherheitsmetriken können Teams schnell auf Sicherheitsvorfälle reagieren und den potenziellen Schaden minimieren.

Observability hilft auch bei der Einhaltung von Vorschriften, indem Zugriffskontrollen, Datenflüsse und andere sicherheitsrelevante Aktivitäten verfolgt werden. Dadurch lässt sich die Einhaltung von Sicherheitsstandards gewährleisten und gesetzliche Anforderungen erfüllen.

Schlussendlich trägt Observability zu einer zuverlässigeren und reaktionsfähigeren API bei, was für eine positive Benutzererfahrung entscheidend ist. Durch die Verringerung der Fehlerhäufigkeit von Anfragen und die Sicherstellung einer konsistenten Leistung tragen Observability-Tools zu einer hohen Benutzerzufriedenheit bei und stellen sicher, dass APIs verfügbar sind und wie erwartet funktionieren.

Stay tuned

Immer auf dem Laufenden bleiben! Alle News & Updates:

[mc4wp-simple-turnstile]

 

Fazit

Observability ist eine wichtige Komponente der modernen API-Entwicklung. Durch die Einführung von Observability-Praktiken können Unternehmen Leistungsengpässe erkennen und beheben, um die Reaktionszeiten und den Durchsatz von APIs zu optimieren.

Fehler können nicht nur sofort erkannt und behoben werden, es werden auch Ausfallzeiten minimiert und die Benutzerfreundlichkeit verbessert. Außerdem werden Sicherheitsbedrohungen identifiziert und entschärft, sensible Daten geschützt und die Einhaltung von Vorschriften gewährleistet.

Observability-Daten können genutzt werden, um strategische Entscheidungen über die Entwicklung, Bereitstellung und Wartung von APIs zu treffen.

Da sich API-Systeme ständig weiterentwickeln und immer komplexer werden, wird der Bedarf an robusten Observability-Lösungen immer größer. Durch Investitionen in Observability können Unternehmen zuverlässigere, effizientere und sicherere APIs erstellen, die ihren Kunden einen Mehrwert bieten.

 

🔍 Frequently Asked Questions (FAQ)

1. Was ist API Observability?
API Observability bezeichnet die Fähigkeit, den internen Zustand einer API und ihrer Infrastruktur anhand externer Signale wie Logs, Metriken und Traces zu verstehen. Sie geht über klassisches Monitoring hinaus und ermöglicht ein tieferes Verständnis komplexer Systeme.

2. Worin liegt der Unterschied zwischen API Observability und Monitoring?
Monitoring verfolgt gezielt vordefinierte Metriken und meldet Abweichungen, während Observability ein umfassendes Bild liefert, um auch unbekannte Probleme zu erkennen und deren Ursachen zu analysieren.

3. Was bringt Observability für die API-Performance?
Durch präzise Metriken wie Antwortzeiten, Fehlerraten und Ressourcenauslastung hilft Observability, Engpässe zu identifizieren, Optimierungen vorzunehmen und die Stabilität der API langfristig zu sichern.

4. Welche Tools werden für API Observability genutzt?
Zu den gängigen Tools zählen Prometheus für Metriken, Grafana für Visualisierungen, Jaeger und Zipkin für verteiltes Tracing sowie zentrale Log-Aggregationsplattformen.

5. Wer profitiert von API Observability?
Entwickler:innen, DevOps-Teams und Security-Spezialist:innen nutzen Observability, um Fehler schneller zu beheben, Ausfälle zu vermeiden und Sicherheitsvorfälle frühzeitig zu erkennen.

6. Welche Sicherheitsvorteile bietet API Observability?
Sie hilft, unbefugte Zugriffe, verdächtige Datenverkehrsmuster und Authentifizierungsfehler zu identifizieren. So können Sicherheitsbedrohungen proaktiv erkannt und behoben werden.

7. Wie lässt sich API Observability erfolgreich implementieren?
Die Umsetzung erfordert die Wahl geeigneter Tools, klare Monitoring-Ziele, strukturierte Protokollierung, verteiltes Tracing, effektive Warnmeldungen und die Einhaltung von Datenschutz- und Sicherheitsstandards.

The post API Observability richtig umsetzen: Performance, Sicherheit & Zuverlässigkeit steigern appeared first on MAD Summit.

]]>
Postman Alternativen im Test: Insomnia vs. Hoppscotch https://mad-summit.de/blog/api/postman-alternativen-insomnia-vs-hoppscotch/ Wed, 16 Apr 2025 13:29:41 +0000 https://mad-summit.de/?p=107052 Im Zeitalter der Cloud-Entwicklung und API-Integration ist Postman lange Zeit das führende Tool für API-Entwickler gewesen. Doch mit der Entscheidung, Postman nur noch cloudbasiert anzubieten, suchen immer mehr Entwickler nach Postman Alternativen. In diesem Artikel vergleichen wir zwei der besten Alternativen: Insomnia und Hoppscotch. Welche der beiden Optionen für deine Bedürfnisse am besten geeignet ist, erfährst du hier.

The post Postman Alternativen im Test: Insomnia vs. Hoppscotch appeared first on MAD Summit.

]]>
Im vergangenen Jahr hat Postman eine neue Richtung eingeschlagen und ist seither nicht mehr offline nutzbar – nur noch in der Cloud. Doch nicht alle wollen diesen Weg mitgehen. Vielleicht suchst auch du nach Alternativen, die nützliche Features bieten, ohne Daten in der Cloud speichern zu müssen? Mit Insomnia und Hoppscotch nehmen wir zwei Postman-Alternativen genauer unter die Lupe. Welches Tool ist das richtige für deinen Use Case? 

Wenn wir heute Software entwickeln, sind an irgendeinem Punkt Web-APIs involviert. Im Cloud-Zeitalter sind APIs das Fundament für die Kommunikation zwischen Anwendungen. Egal, ob HTTP, REST, GraphQL oder gRPC – der Zugriff auf APIs, ihre Dokumentation und ihre Tests sind zentrale Aufgaben im Entwicklungsprozess. Hier kommen API-Clients als entscheidende Werkzeuge ins Spiel.

Entwickle Schnittstellen, die einfach funktionieren

Power-Workshops zu API Design & Entwicklung (22. - 26. Juni 2026, München)

 

Postman: das Standardwerkzeug für APIs

Lange Zeit war Postman das Schweizer Taschenmesser der API-Entwicklung, die unangefochtene Nummer eins. Gestartet im Jahr 2012 als Tool zum Teilen von API-Tests, ganz am Anfang sogar noch als Erweiterung in Google Chrome, hat sich Postman in mehreren Iterationen zur API-Plattform und Ende-zu-Ende-Lösung für die Arbeit mit APIs entwickelt. Der Fokus wurde dabei zunehmend auf die cloudbasierte Zusammenarbeit von Teams gelegt. Dafür ist schon lange ein User Account erforderlich. Parallel war es aber möglich, rein offline mit lokaler Speicherung zu arbeiten: Man verzichtete auf die Anmeldung und arbeitete im „Scratch Pad“ genannten Modus, bei dem keine Daten automatisch in die Cloud hochgeladen wurden. 

Wollte man in Collections organisierte API-Requests und dazugehörige Environments austauschen, wurden die Daten als JSON-Dateien exportiert. Collections und Environments sind dabei auf unterschiedliche Dateien verteilt. Die exportierten Dateien konnten anschließend beispielsweise über Git-Repositorys für die gemeinsame Arbeit ausgetauscht werden. Dadurch, dass die Environments (die potenziell sensible Informationen wie Zugangsdaten enthalten) in eigenen Dateien exportiert werden, können sie gezielt verschlüsselt werden, bevor man sie im Repository ablegt, beispielsweise mit SOPS oder Git-crypt. Wurde in den gemeinsam genutzten Dateien im Git Repository etwas verändert, importierte man die geänderten Collections oder Environments einfach neu. Dabei konnte es zwar unter Umständen zu Duplikaten kommen, aber dennoch hat diese Arbeitsweise lange gut funktioniert.

Vor inzwischen rund fünf Jahren kam zum ersten Mal eine Alternative zu Postman ins Gespräch: Insomnia. Das neue Tool, bereitgestellt von Kong, sollte leichtgewichtiger und moderner sein. Ich habe es damals ausprobiert, bin auf eine Reihe von Bugs gestoßen und habe es wieder beiseitegelegt. Fazit: In mancher Hinsicht ein wenig anders als Postman, vielleicht ganz cool (weil neuer), jedoch im direkten Vergleich kein echter Mehrwert – vor allem angesichts der teils gravierenden Bugs. Es bestand kein Anlass für einen Umstieg auf Insomnia.

Ab Mitte Mai 2023 änderte sich das plötzlich drastisch: In einem Blogartikel mit dem Titel „Announcing the new lightweight Postman API client and sunsetting Scratch Pad“ [1] verkündete Postman, dass das Scratch Pad ab sofort in Neuinstallationen nicht mehr verfügbar sei. Ab September würde es ganz eingestellt werden. Damit war die Suche nach alternativen API-Clients plötzlich hochaktuell. Und es musste auch tatsächlich Ersatz gefunden werden.

Warum war es nicht möglich, Postman weiterhin zu verwenden? Im Enterprise-Kontext sind wir mit der Auflage konfrontiert, keine Daten unserer Kunden in der Cloud zu speichern bzw. keine nicht ausdrücklich freigegebenen Cloud-Anwendungen einzusetzen. Postman zählte nicht dazu, sodass unbedingt ein neues Tool für die Arbeit mit APIs gefunden werden musste.

 

Rahmenbedingungen und Anwendungskontext von API-Clients

Bevor wir uns im Folgenden näher mit verschiedenen API-Clients befassen, treten wir einen Schritt zurück und betrachten, wofür wir einen API-Client eigentlich verwenden wollen oder müssen und wofür nicht. Hierbei schlagen verschiedene Teams mitunter unterschiedliche Wege ein. Der beschriebene Einsatz soll damit insbesondere die Auswahl der später betrachteten Optionen nachvollziehbar machen. Keinesfalls verfolgt die Beschreibung einen Anspruch auf Allgemeingültigkeit. Vielmehr handelt es sich um eine Praxis, die sich insbesondere in den Teams, mit denen ich arbeite, bewährt hat.

Der Anwendungskontext lässt sich grob anhand manueller Interaktion mit APIs umreißen. Es geht etwa darum, Endpunkte zu testen, Fehler zu debuggen oder neue Funktionen auszuprobieren. Wir nutzen API-Clients im Rahmen der Entwicklung und des Betriebs der von uns erstellten und betreuten Systeme. Ein typisches Beispiel ist die Arbeit mit Managementendpunkten oder den Endpunkten von Spring Boot Actuator. Daneben spielt die manuelle Interaktion mit den APIs von Drittsystemen eine wesentliche Rolle bei ihrer Integration. Wir wollen schließlich sichergehen, dass Requests und Responses unseren Erwartungen entsprechen, bevor alles implementiert ist.

Und dann gibt es natürlich noch jede Menge „Nur mal schnell“-Anwendungsfälle, in denen es einzig darum geht, auf ein beliebiges API zuzugreifen, ohne dass dafür besondere Konfigurationen oder Vorbereitungen nötig sind – also wie curl auf der Kommandozeile, nur komfortabler. Dabei müssen wir die folgenden Rahmenbedingungen berücksichtigen:

  • Import von Projekten aus Postman: Tools sollten die Möglichkeit bieten, bestehende Projekte oder Collections aus Postman zu importieren, um einen reibungslosen Wechsel zu gewährleisten.
  • Lokale Datenhaltung: Daten wie API-Spezifikationen, Request-Sammlungen und natürlich auch Zugangsdaten sowie andere Umgebungsvariablen sollten lokal gespeichert werden können, um Datenschutz und Offlinenutzung sicherzustellen.
  • Kollaborative Nutzung: Trotz lokaler Datenspeicherung soll die Zusammenarbeit von Teams möglich sein. Konkret bedeutet das, dass Import- und Exportfunktionalitäten gegeben sein müssen, die es erlauben, die Projekte über Git-Repositorys auszutauschen und gemeinsam damit zu arbeiten.
  • Fokus auf HTTP APIs: Der Hauptfokus liegt auf der Unterstützung von HTTP APIs (insbesondere REST). Andere Protokolle oder Events sind für die Auswahl eines API-Clients als Alternative zu Postman nicht entscheidend.
  • Cross-Platform-Kompatibilität: Der Client sollte auf verschiedenen Betriebssystemen (Windows, macOS, Linux) einheitlich funktionieren, um vielseitig einsetzbar und für das gesamte Team und gegebenenfalls auch externe Stakeholder verfügbar zu sein.
  • Kein Selfhosting: Die Nutzung des Tools sollte ohne zusätzliche Infrastruktur oder Overhead wie selbstgehostete Server möglich sein, wir wollen einen API-Client nutzen und nicht betreiben oder managen müssen.

 

Aus diesen Rahmenbedingungen ergibt sich außerdem indirekt, dass Open-Source-Software oder frei und ohne kostenpflichtige Subscriptions nutzbare Tools klar bevorzugt werden. Während sich für das Team selbst der Erwerb von Lizenzen noch mit überschaubarem Aufwand organisieren ließe, würde sich die übergreifende Zusammenarbeit, ja schon das Herstellen der dafür notwendigen Voraussetzungen, deutlich schwieriger gestalten. Andersherum betrachtet kann das Ergebnis der Toolauswahl ohne diese Einschränkungen oder mit anderen Rahmenbedingungen gänzlich anders ausfallen.

 

Stay tuned

Immer auf dem Laufenden bleiben! Alle News & Updates:

[mc4wp-simple-turnstile]

 

(Nicht) benötigtes Featureset

Bleiben wir aber bei unserem Kontext und widmen uns den funktionalen Anforderungen: Was soll der API-Client der Wahl können, um für uns als Postman-Alternative infrage zu kommen?

 

  • Workspaces mit Collections und Ordnern: Die Organisation von API Requests in Workspaces, Collections und Ordnern ermöglicht uns eine strukturierte und übersichtliche Verwaltung selbst komplexer Projekte. Wir können so verschiedene APIs und deren Endpunkte klar voneinander trennen und effizient bearbeiten. Das ist insbesondere für die Separierung integrierter Fremdsysteme wichtig.
  • Mehrere Environments: Die Unterstützung mehrerer Umgebungen (Environments) ist entscheidend, um Anfragen flexibel an unterschiedliche Kontexte wie Entwicklung, Test und Produktion anzupassen. Variablen wie Basis-URLs oder Zugangsdaten können dabei je nach Umgebung definiert werden. Ein Pluspunkt dabei wäre, wenn die Environments unabhängig von den Collections gespeichert werden können, um eine gezielte Verschlüsselung sensibler Informationen zu ermöglichen. Mit den getrennten Dateien, wie sie aus Postman exportiert werden können, ist das für uns gängige Praxis.
  • Verschiedene Authentifizierungsmechanismen: APIs erfordern oft unterschiedliche Methoden zur Authentifizierung, darunter API Keys, OAuth, HTTP Basic Authentication oder Bearer Tokens. Ein passender API-Client sollte diese Mechanismen standardmäßig unterstützen, um die Arbeit mit geschützten Endpunkten zu erleichtern. Insbesondere OAuth 2.0 ist dabei für uns von Bedeutung. Hier benötigen wir die Unterstützung sowohl des Client Credentials Flows als auch des Authorization Code Flows. Außerdem sind in einigen Fällen Clientzertifikate erforderlich.
  • Pre-Request Scripts: Damit kann Logik vor einer Anfrage ausgeführt werden, beispielsweise, um dynamische Werte wie Zeitstempel oder Tokens zu generieren. Das erhöht die Flexibilität und Automatisierung bei der Arbeit mit APIs, insbesondere beim Testen.
  • Parameter für Header, Pfad und Query: Mit der Möglichkeit, Parameterwerte für Header, Pfadvariablen und Querystrings (unabhängig voneinander für verschiedene Anfragen) zu definieren, lassen sich Anfragen spezifisch für den jeweiligen Anwendungsfall gestalten, ohne dabei die reine Spezifikation des API mit konkreten Anfragen zu vermischen. Das kann vor allem bei Pfadvariablen relevant sein, um das URL-Template spezifischer Pfade von den Variablen zugewiesenen Werten zu unterscheiden. Parameter sind besonders bei der Arbeit mit komplexen APIs mit vielen Parametern oder Konfigurationsmöglichkeiten essenziell.

 

Neben diesen für uns wichtigen Funktionalitäten gibt es auch einige, die wir bewusst außer Acht lassen, obwohl uns klar ist, dass sie durchaus zum Funktionsumfang von Postman (oder Alternativen) gehören:

 

  • API-Designer: Mit einem integrierten API-Designer können APIs von Grund auf entworfen werden, beispielsweise, indem der API-Client als OpenAPI-Editor fungiert.
  • Testautomatisierung: Wie eingangs beschrieben, war das Teilen von API-Tests initiale Motivation für die Entstehung von Postman. Bei unserer Arbeit erscheint uns das Definieren von Tests mit dem Ziel der anschließenden Automatisierung jedoch in vielen Fällen wie ein Medienbruch: Unit-Tests und Integrationstests sind bereits in Java implementiert. Um in diesem Umfeld zu bleiben, nutzen wir daher lieber Bibliotheken wie REST Assured [2] für die Umsetzung automatischer API-Tests.
  • CI- und Kommandozeilenintegration: Wenn die Testautomatisierung nicht im Fokus steht, ist es auch nicht unmittelbar notwendig, den API-Client in Build-Prozesse oder Workflows der Continuous Integration und Continuous Delivery einzubinden. Daher ist nicht relevant, ob ein API-Client auch über die Kommandozeile genutzt werden kann, auch wenn einige Tools das unterstützen.
  • Mock-Server: Für das Mocken von APIs haben wir in unserer Arbeit bereits seit Längerem andere Lösungen gefunden und umgesetzt, zum Beispiel simple Node.js-Anwendungen mit einfacher Logik oder WireMock [3]. Es bestand also kein Bedarf, diese Funktionalität mit einem API-Client abzubilden.
  • API-Monitoring: Das kontinuierliche Überwachen von APIs in Produktionsumgebungen ist eine Aufgabe, für die wir dedizierte Monitoringtools wie Datadog verwenden. Das muss nicht der API-Client abdecken.

 

Für diese Aspekte gibt es also andere Mittel und Wege und sie gehören für uns nicht zum unmittelbar relevanten Funktionsumfang eines API-Clients. Entsprechend können diese Punkte auch als Beispiele verstanden werden, die Postman Kritik eingebracht haben: Für „Puristen“ war das Tool mit seinem Funktionsumfang für alle Eventualitäten irgendwann deutlich zu aufgebläht – ein weiterer Anlass für die Suche nach Alternativen. Ohne in die Kritik an Postmans Funktionsumfang einstimmen zu wollen, teilen wir den Wunsch nach Einfachheit und einem aufs Wesentliche reduzierten Werkzeug.

 

Alternativen zu Postman

Bevor wir uns dem Überblick über mögliche alternative API-Clients widmen, sei der Vollständigkeit halber erwähnt, dass es auch noch eine Postman-Variante gibt, die offline und ohne Account verwendet werden kann: der Lightweight-API-Client, der zusammen mit dem Ende des Scratch Pads angekündigt wurde. Er ist in seinem Funktionsumfang jedoch so eingeschränkt, dass er hier direkt durchs Raster fällt. Im Detail bedeutet das: eine Request-Historie hat Collections ersetzt, Environments gibt es gar nicht mehr. Und ohne Anmeldung (die zur Übertragung aller Daten in die Cloud führt) können lediglich Requests von curl importiert werden. Mit anderen Worten: Der leichtgewichtige neue Client von Postman ist eher eine grafische Alternative zu curl als ein vollwertiger API-Client. Verständlich, ist es doch in Postmans Interesse, dass die eigentliche Anwendung mit all ihren Fähigkeiten genutzt wird.

In diesem Artikel werfen wir einen intensiveren Blick auf zwei Werkzeuge, die als Alternativen zu Postman infrage kommen könnten:

  • Insomnia
  • Hoppscotch

 

Alle haben gemein, dass sie für den beschriebenen Kontext unter den gegebenen Rahmenbedingungen ohne (zu große) Einschränkungen zum Einsatz kommen können. Für einen weitergehenden Überblick über weitere API-Clients, die aus verschiedenen Gründen hier nicht infrage kommen (nicht selten aufgrund eingeschlafener Entwicklung) verweise ich an dieser Stelle auf einen Blogartikel [4], der bereits vor längerer Zeit während einer intensiven Recherchephase entstand – getrieben von der Befürchtung, dass es neben Postman und vielleicht Insomnia keine nennenswerten aktuellen API-Clients geben könnte. Der Verdacht hat sich nicht bestätigt, sodass wir uns jetzt mit einer Reihe verschiedener Werkzeuge befassen können, die alle in irgendeiner Weise eine Besonderheit mitbringen.

 

 

Insomnia: flexibel durch Plug-ins und Git, aber mit Cloud-Fragen

Insomnia [5] steht bei den möglichen Alternativen an erster Stelle, da es bei der Suche auch der erste Anlaufpunkt war, nach ersten Erfahrungen in der Vergangenheit. Wie Postman hat auch Insomnia bereits eine längere Geschichte: 2016 wurde das Tool von Gregory Schier ins Leben gerufen, als er an einem transaktionalen E-Mail-API arbeitete und feststellte, dass es keinen einfachen Weg gab, mit diesem API zu interagieren. Die Lösung des Problems war der Beginn von Insomnia [6].

Das Projekt wurde später von Kong übernommen und hat sich zu einer der beliebtesten Alternativen zu Postman entwickelt – gleichzeitig dient es heute als Ausgangspunkt für die Suche nach weiteren Ausweichlösungen und wird daher oft in einem Atemzug mit Postman genannt. Doch warum? Ziemlich genau zwei Wochen, nachdem Postman im September 2023 die Unterstützung der Offlineversion final eingestellt hatte, führte Kong für Insomnia das große Update auf die zu dem Zeitpunkt grundlegend neue Version 8 durch [7]. Eine der wichtigsten Neuerungen war die Einführung einer Plattform für die Cloud-basierte Zusammenarbeit. Gleichzeitig wurde das bei Postman gerade erst abgeschaffte Scratch Pad in Insomnia eingeführt. Das täuschte jedoch nicht darüber hinweg, dass für den Zugriff auf bestehende Projekte plötzlich (und ohne Vorankündigung) ein Account erforderlich war. Und war man eingeloggt, fanden sich die Daten direkt in der Cloud wieder. Ausschließlich offline und lokal gespeichert wurden nur die Daten aus dem Scratch Pad. Die Aufregung war – wenig überraschend – groß.

Praktisch sofort erblickte Insomnium als Fork der letzten Insomnia-Version ohne Cloud-Zwang das Licht der Welt – und war zum damaligen Zeitpunkt die Rettung für alle, die bereits von Postman auf Insomnia umgestiegen waren. Etwa drei Wochen später gestand Marco Palladino, Kong CTO und Co-Founder, in einem weiteren Blogartikel [8] die faktische Abschaffung lokaler Projekte als Fehler ein und kündigte gleichzeitig die überarbeitete Version 8.3 an, in der lokale Projekte ihre Rückkehr feierten. Das wesentliche Alleinstellungsmerkmal von Insomnium wurde damit schon wieder obsolet und das Tool nicht mehr weiterentwickelt. Inzwischen wurde das Git-Repository [9] archiviert und Insomnium allen anfänglichen Absichtserklärungen zum Trotz offiziell eingestellt.

Das Feld gehört also wieder Insomnia. Mit welchen Besonderheiten bietet es sich als Alternative zu Postman an? Als Erstes ist hier die Möglichkeit zu nennen, Projekte über Git zu synchronisieren, sodass nicht auf Teamkollaboration verzichtet werden muss, die Daten aber unter der eigenen Kontrolle bleiben. Doch: Dieses Feature steht ohne kostenpflichtige Subscription nicht mehr zur Verfügung (wobei es mit der Version 10.2 noch einmal interessanter wurde, als Insomnia auch eine Möglichkeit zum Auflösen von Konflikten bekam [10]). Und das Repository scheint von Insomnia intern verwaltet zu werden. Konfiguration von Verschlüsselung insbesondere für sensible Informationen in den Environments ist so nicht möglich.

Abhilfe kann hier die Verwendung eines Git-Plug-ins schaffen. Das funktioniert auch ohne Subscriptions und ist gleichzeitig ein gutes Beispiel für ein Alleinstellungsmerkmal von Insomnia: Über Plug-ins kann jede:r das Werkzeug an die eigenen Anforderungen anpassen. Es gibt eine umfangreiche Bibliothek mit von der Community bereitgestellten Plug-ins, beispielsweise für die Verwendung von Secrets aus Azure Key Vault, aus der macOS Keychain, aus 1Password oder Keepass und es gibt verschiedene Scripting-Plug-ins und einen Collection Runner für Tests.

Das zeigt: Verschiedene von Postman vertraute Funktionalitäten gibt es in Insomnia nicht out of the box, sie müssen über Plug-ins nachgerüstet werden. Das gilt nach wie vor selbst für die Darstellung mehrerer Requests in Tabs. Man kann die Plug-ins also sowohl als Quelle großer Flexibilität wie auch als Notwendigkeit sehen. Da ist es umso erstaunlicher, dass dieses Feature nach wie vor als experimentell gilt (und vor einigen Jahren wesentlich zur Instabilität und Fehleranfälligkeit der gesamten Anwendung beitrug – das ist heute anders).

Ein weiteres Feature, das es so meines Wissens nirgends sonst gibt, ist das Verketten von API-Aufrufen mittels Tags im Request, das sogenannte Request Chaining: In Postman ist es möglich, per Post Request Script den nächsten API-Aufruf auszulösen. Scripting gab es in Insomnia jedoch sehr lange nicht. Schließlich gab und gibt es eine andere Möglichkeit, API-Aufrufe zu verketten: Tags ermöglichen an beliebiger Stelle im Request die Verwendung eingebauter Funktionen (und auch von Plug-ins). Das können Referenzen auf andere Requests in der Collection sein. So lassen sich Daten feingranular aus anderen Requests übernehmen, wobei definiert werden kann, ob diese anderen Requests auch in jedem Fall, nur nach Ablauf einer vorgegebenen Zeit oder ob generell bereits vorhandene Antworten verwendet werden sollen. Weitere Möglichkeiten für die Verwendung von Tags sind die Nutzung von Cookies, Eingabe-Prompts, Dateizugriffe oder die Ausführung von Hashfunktionen.

 

Hoppscotch: Einfachheit und Effizienz für webbasierte API-Tests

Die Geschichte von Hoppscotch [11] begann im August 2019 auf dev.to, ursprünglich noch unter dem Namen Postwoman: Liyas Thomas beschrieb dort, wie er an einem API-Integrations-Projekt arbeitete und sein Rechner keine weitere Electron-App wie Postman verkraftete. In diesem Moment fasste er den Entschluss, seine eigene Plattform für das Testen von APIs zu entwickeln [12]. Sie sollte online laufen, verschiedene Plattformen und Geräte unterstützen und so von überall nutzbar sein. Postman verfügte zu diesem Zeitpunkt bereits über ein mehrstufiges Preismodell. Bei Hoppscotch standen jedoch Einfachheit, Effizienz und Nutzungserfahrung im Mittelpunkt. Ebenso sprach sich Liyas Thomas klar für Open Source aus. Dasselbe wird uns später, praktisch in identischer Form, bei Bruno erneut begegnen.

Im November 2019 wurde Hoppscotch v1 veröffentlicht [13] und unterstützte neben HTTP bereits MQTT und WebSockets. Die GitHub Stars gingen vom Start weg durch die Decke und stehen heute bei weit über 60 000 (mehr als Insomnia und Bruno zusammen). Dabei gab es sehr lange keine Desktopversion – sie wurde lange angekündigt, jedoch erst im November 2023 veröffentlicht.

Das Herz von Hoppscotch schlug offenbar nach wie vor überwiegend online. Das zeigt sich auch im Alleinstellungsmerkmal des API-Clients: Will man nicht auf die Möglichkeiten Cloud-basierter Zusammenarbeit verzichten, gleichzeitig jedoch die volle Kontrolle behalten, kann man sich dafür entscheiden, Hoppscotch selbst zu hosten. Klar, das muss man wollen und man zahlt auch seinen Preis dafür, aber man ist eben nicht auf die Cloud-Angebote und Subscription-Modelle des Anbieters angewiesen. Mit hoppscotch.io gibt es daneben nach wie vor die als Service bereitgestellte Onlineversion, die auch die Zusammenarbeit in Teams ermöglicht.

Trotz des hohen GitHub-Rankings sticht Hoppscotch ansonsten insgesamt wenig hervor. Das größte Ärgernis, das das Tool für mich lange nicht (sinnvoll) nutzbar machte, ist inzwischen behoben: Für die Nutzung von OAuth 2.0 kann mittlerweile der Grant Type festgelegt werden. Unterstützung für Pfadparameter andererseits gibt es bis heute nicht. Unterm Strich macht der API-Client offenbar vieles richtig und wenig falsch und erledigt einfach seinen Job.

 

Ausblick

In einem zweiten Artikel wollen wir uns die Tools Bruno und Kreya gründlich anschauen. Außerdem werfen einen kurzen Blick darauf, was der Markt sonst noch zu bieten hat. Schließlich beantworten wir die entscheidende Frage: Was sollen wir denn nun nutzen? 

 

🔍 Frequently Asked Questions (FAQ)

1. Warum suchen Entwickler nach Postman-Alternativen?
Postman hat sich vollständig in die Cloud verlagert, was für viele Entwickler, die offline arbeiten oder ihre Daten lokal speichern möchten, problematisch ist. Daher suchen sie nach Alternativen wie Insomnia und Hoppscotch, die diese Anforderungen erfüllen.

2. Was sind die Hauptmerkmale von Insomnia?
Insomnia bietet eine benutzerfreundliche Oberfläche, unterstützt REST, GraphQL und gRPC und ermöglicht es, APIs lokal zu testen und zu dokumentieren, ohne Daten in der Cloud zu speichern.

3. Wie unterscheidet sich Hoppscotch von Insomnia?
Hoppscotch ist eine Open-Source-Alternative, die im Browser läuft und eine schnelle, leichtgewichtige Lösung für API-Tests bietet. Es unterstützt ebenfalls REST, GraphQL und WebSockets.

4. Für welche Anwendungsfälle eignet sich Insomnia besonders?
Insomnia ist ideal für Entwickler, die komplexe API-Tests durchführen müssen, eine umfangreiche Dokumentation benötigen oder in einer Umgebung ohne ständige Internetverbindung arbeiten.

5. Wann ist Hoppscotch die bessere Wahl?
Hoppscotch eignet sich hervorragend für schnelle, unkomplizierte API-Tests im Browser, besonders wenn eine leichte und sofort einsatzbereite Lösung gewünscht ist.

6. Welche Sicherheitsaspekte sind bei der Nutzung dieser Tools zu beachten?
Bei der Verwendung von Insomnia und Hoppscotch sollten Entwickler sicherstellen, dass sensible Daten lokal gespeichert werden und keine unverschlüsselten Verbindungen verwendet werden, um die Sicherheit zu gewährleisten.

7. Wie integrieren sich Insomnia und Hoppscotch in bestehende Entwicklungsworkflows?
Beide Tools lassen sich nahtlos in CI/CD-Pipelines integrieren und unterstützen Automatisierung, was sie zu wertvollen Werkzeugen in modernen Entwicklungsumgebungen macht.

 

Links & Literatur

[1] https://blog.postman.com/announcing-new-lightweight-postman-api-client/

[2] https://rest-assured.io

[3] https://wiremock.org

[4] https://4ndrs.xyz/posts/web-api-clients/

[5] https://insomnia.rest

[6] https://codestory.co/podcast/bonus-greg-schier-insomnia-client/

[7] https://konghq.com/blog/product-releases/insomnia-8-0

[8] https://konghq.com/blog/product-releases/insomnia-8-3

[9] https://github.com/ArchGPT/insomnium

[10] https://konghq.com/blog/product-releases/insomnia-10-2

[11] https://hoppscotch.com

[12] https://dev.to/liyasthomas/i-created-postwoman-an-online-open-source-api-request-builder-41md

[13] https://company.hoppscotch.io/hoppscotch-v1

The post Postman Alternativen im Test: Insomnia vs. Hoppscotch appeared first on MAD Summit.

]]>
Modulare Architekturen: Spring Modulith vs. Axon Framework im Vergleich https://mad-summit.de/blog/modulare-software-architektur-microservices/spring-modulith-vs-axon-architekturvergleich/ Tue, 11 Feb 2025 13:58:59 +0000 https://mad-summit.de/?p=106938 Die Modularisierung von Software ist essenziell für Skalierbarkeit und Wartbarkeit. CQRS, Event Sourcing, Modulithen und Microservices bieten unterschiedliche Ansätze. Spring Modulith und Axon helfen bei der strukturierten Entkopplung. Dieser Artikel vergleicht beide Frameworks und zeigt Best Practices für modulare Architekturen.

The post Modulare Architekturen: Spring Modulith vs. Axon Framework im Vergleich appeared first on MAD Summit.

]]>
Die Modularisierung von Anwendungen ist heutzutage in aller Munde. Liegt es nur daran, dass die Zahl der monolithischen Anwendungen – oft als Spaghetticode bezeichnet – stetig wächst, oder gibt es mittlerweile bessere Architekturansätze und Frameworks, um die Modularisierung zu fördern?

In der modernen Softwareentwicklung ist die Modularisierung von Anwendungen ein zentraler Aspekt, um deren Wartbarkeit, Erweiterbarkeit und Skalierbarkeit zu gewährleisten. Seit einigen Jahren dominiert dabei der Trend zu Microservices, die die Anwendungen in kleine, möglichst unabhängige Einheiten aufteilen. Diese Architektur verspricht eine größere Flexibilität und Unabhängigkeit der einzelnen Komponenten. Doch trotz der theoretischen Vorteile zeigt die Praxis oft ein anderes Bild: Viele Unternehmen stolpern über die Herausforderungen, die mit der Implementierung von Microservices einhergehen, und landen nicht selten bei dem, was als „verteilter Monolith“ bezeichnet wird. Aus diesem Grund wenden sich immer mehr Entwickler modularen Architekturansätzen zu, insbesondere den sogenannten Modulithen (Abkürzung für modularer Monolith).

 

Warum modulare Architekturen?

Software nimmt einen immensen Stellenwert in der gesamtwirtschaftlichen Wertschöpfung ein. Technologischer Fortschritt einerseits, aber auch hoher Wettbewerbsdruck und hohe Geschäftsdynamik aufgrund wechselnder Marktbedingungen andererseits erhöhen den Druck auf die Softwareentwicklung. Gefragt ist hohe Qualität bei gleichzeitiger schneller Anpassbarkeit.

Modulare Architekturen haben hier eine Reihe von Pluspunkten. Zu den wesentlichen technischen Vorteilen zählen:

  1. Bessere Wartbarkeit, höhere Wiederverwendbarkeit und Anpassbarkeit: Durch die Aufteilung der Software in Module mit klar definierten Verantwortlichkeiten wird der Code verständlicher und besser wartbar. Änderungen an einem Modul haben minimalen oder keinen Einfluss auf andere Module in Form von Seiteneffekten. Modulare Komponenten können leichter wiederverwendet oder angepasst werden.

  2. Skalierbarkeit und technologische Flexibilität: Module können im Sinne einer effizienteren Ressourcennutzung unabhängig voneinander skaliert werden. Zudem lassen sich verschiedene Module mit Hilfe unterschiedlicher, passend für den jeweiligen Anwendungsfall ausgewählter Technologien entwickeln, was der Flexibilität zugutekommt.

  3. Trennung von Geschäftslogik und Technologie: Die Trennung von fachlichem (Geschäftslogik) und technischem Code verbessert sowohl die Verständlichkeit und Wartbarkeit des Systems als auch die Robustheit und Testbarkeit.

Systeme modernisieren statt nur migrieren

Power-Workshops zu Modernisierung & Service-Architektur (22. - 26. Juni 2026, München)

 

Modularisierung auf Programmcode-Ebene

Als Ausgangspunkt für die Modularisierung gilt häufig der Programmcode einer Anwendung. Hierzu können Java Packages verwendet werden. Ein üblicher Schnitt des Codes orientiert sich dabei an den Fachlichkeiten. Jedes Modul bietet ein API, das von anderen Modulen konsumiert und benutzt werden kann. Zugriffe auf Implementierungsdetails eines Moduls, zum Beispiel direkt auf die Persistenz eines anderen Moduls, sollten nicht stattfinden. So wird das Prinzip der Single Responsibility gefördert. Ein Modul bietet eine bestimmte Funktion an, die Implementierungsdetails obliegen aber dem Modul selbst. Auch die Verwendung unterschiedlicher Technologien in verschiedenen Modulen wird so ermöglicht. Jedes Modul kann für seinen Anwendungsfall optimiert, unabhängig erweitert oder restrukturiert werden. Dadurch, dass die Module ausschließlich über die definierten Schnittstellen kommunizieren, lässt sich eine losere Kopplung der Fachlichkeiten erreichen.

Betrachten wir eine Beispielanwendung, in der Benutzer Feed-Einträge veröffentlichen können. Die Anwendung bietet also Funktionalitäten rund um das Benutzerprofil, aber auch rund um die Feed-Einträge. Daher sollen diese in unterschiedlichen Packages gebündelt werden. Eine beispielhafte Package-Struktur ist in Listing 1 dargestellt.

Listing 1

de.dxfrontiers.example/
├─ feed/
│  ├─ persistence/
│  │  ├─ FeedEntryEntity.java
│  │  ├─ FeedEntryRepository.java
│  ├─ FeedService.java
│
├─ user/
│  ├─ persistence/
│  │  ├─ UserEntity.java
│  │  ├─ UserRepository.java
│  ├─ UserService.java
│
├─ messaging/
│  ├─ OutboundMessaging.java
│  ├─ message/
│  │  ├─ FeedEntryPublishedMessage.java
│
├─ FeedApplication.java

Jedes der beiden Packages user (Benutzer) und feed bietet dabei die Service-Klasse als API an, über das die Funktionalität des Moduls genutzt werden kann. Die Implementierungsdetails – genutzte Persistenztechnologie, Struktur der Persistenz, interne Implementierungen – sollten dabei nur innerhalb des Packages oder entsprechender Sub-Packages nutzbar sein. Lediglich das API kann von anderen Modulen genutzt werden.

Ein weiteres Package in Listing 1 ist messaging. Anders als die Packages für Benutzer und Feeds ist dieses nicht entlang einer Fachlichkeit geschnitten, sondern adressiert einen querschnittlichen Aspekt. Das ist dann sinnvoll, wenn etwa ein technisches Problem unabhängig von der Fachlichkeit übergreifend durch ein einheitliches API und eine einheitliche Implementierung gelöst werden kann. Das Messaging ist hierfür oft ein sehr gutes Beispiel, da die eigentliche Entscheidung, dass eine bestimmte Nachricht versandt werden soll, einfach aus dem fachlichen Code aufgerufen werden kann. Die technischen Details wie die Struktur der Nachricht, die zu verwendende Messaging-Technologie und Topics sollten jedoch vor dem fachlichen Code verborgen bleiben.

Listing 2 zeigt beispielhaft eine Methode aus der FeedService-Klasse, um einen neuen Feed-Eintrag (FeedEntry) zu veröffentlichen.

Listing 2

@Transactional
public void publishFeedEntry(
  UUID userId,
  String category,
  String entry) {

    UserEntity user = userRepository.findByUserId(userId);

    UUID feedEntryId = UUID.randomUUID();
    Instant publishedAt = Instant.now();

    feedEntryRepository.save(
      new FeedEntryEntity(
        feedEntryId,
        userId,
        category,
        entry,
    publishedAt));

    outboundMessaging.publish(
      new FeedEntryPublishedMessage(
        feedEntryId,
        userId,
        category,
        entry,
    publishedAt));

    user.setLastUpdatedAt(publishedAt);
    userRepository.save(user);
}

Der Code aus Listing 2 lädt dabei zunächst den Benutzer aus der Persistenz, um zu validieren, dass der übergebene Benutzer existiert. Anschließend wird ein neuer Feed-Eintrag angelegt und gespeichert. Dann wird über einen Message-Bus die Nachricht veröffentlicht, dass ein neuer FeedEntry angelegt wurde. Zuletzt wird der Zeitstempel der letzten Aktivität des Benutzers aktualisiert.

Der Code aus Listing 2 weist allerdings, obwohl er funktionsfähig ist, einige Probleme hinsichtlich der Modularisierung auf. Deren Ziel ist es, alle Implementierungsdetails in einem Modul zu verbergen und ein dediziertes API zur Verfügung zu stellen. Dieses API ist oftmals die Service-Klasse. Der FeedService im Listing verwendet jedoch nicht den UserService, um mit dem User-Modul zu interagieren, sondern greift direkt auf dessen Persistenz zu. Auch die Struktur der veröffentlichten Nachricht findet sich im eigentlich fachlich orientieren Code wieder und ist nicht im Messaging-Modul gekapselt.

Auch wenn diese Verstöße gegen die Modularisierung bei einer gründlichen Review auffallen, bedeutet es dennoch manuellen Aufwand, sie aufzufinden. Gerade in komplexerem Code können solche Probleme bei manueller Überprüfung untergehen. Ziel sollte es daher sein, die Modularisierung automatisiert sicherzustellen. Java als Sprache bietet mit der Visibility package-private zwar eine Möglichkeit an, Klassen nur innerhalb desselben Packages nutzbar zu machen, jedoch verhindert dieses Vorgehen auch jegliche Strukturierung in Form von Sub-Packages, was gerade bei größeren Modulen die Übersichtlichkeit innerhalb des Moduls behindert.

Diese Lücke schließt zum Beispiel ArchUnit [1], indem es die Struktur des Codes und Zugriffe auf bestimmte Klassen in speziellen Tests analysiert und gegen Regeln abgleicht. Spring Modulith [2] vereinfacht die Integration solcher Verifikationstests in Spring-Boot-Anwendungen. Gleichzeitig bringt Spring Modulith eine einfache Standardkonfiguration mit, die leicht zu verstehen ist und ohne zusätzlichen Konfigurationsaufwand für viele Module bereits eine sehr gute Strukturierung erlaubt. In der Standardkonfiguration sind Zugriffe auf andere Top-Level-Packages nur dann erlaubt, wenn der Zugriff auf eine Klasse auf oberster Ebene im anderen Modul stattfindet. In unserem Anwendungsfall kann zum Beispiel aus dem Package feed nur auf den UserService aus dem Package user zugegriffen werden, da dieser auf oberster Ebene in einem anderen Modul liegt. Zugriffe auf alle anderen Klassen im User-Modul werden von den Verifikationstests als Verstoß erkannt. Da der UserService als API zum User-Modul dienen soll, alle anderen Klassen jedoch Implementierungsdetails darstellen, die außerhalb des User-Moduls nicht bekannt sein sollen, erreichen wir unser Ziel.

Einen Verifikationstest zeigt der folgende Code, die FeedApplication-Klasse ist dabei die Klasse mit der main-Methode, und der Spring-Boot-typischen Annotation @SpringBootApplication.

@Test
public void isModularized() {
  ApplicationModules.of(FeedApplication.class).verify();
}

Führt man diesen Test aus, erkennt er auch genau die im Text genannten Verstöße. Ein Ausschnitt aus der Konsolenausgabe des Tests ist in Listing 3 dargestellt.

Listing 3

Violations:
- Module 'feed' depends on non-exposed type UserRepository within module 'user'!
...
- Module 'feed' depends on non-exposed type UserEntity within module 'user'!
...
- Module 'feed' depends on non-exposed type FeedEntryPublishedMessage within module 'messaging'!

Aufgeführt werden jeweils die Zugriffe auf Klassen, die nicht aus dem API des anderen Packages stammen, insbesondere die Persistenzschicht des User– und die Nachrichtenklasse des Messaging-Moduls. Diese Fehler sind einfach zu korrigieren, indem das API der jeweiligen Module passende Methoden anbietet. Für das User-Modul wird eine Möglichkeit benötigt, die Existenz eines Users zu prüfen sowie die letzte Aktivität des Benutzers zu setzen. Das Messaging-Modul muss ein API anbieten, um Informationen über neue Feed-Einträge zu veröffentlichen, anstatt eines generischen API, um Nachrichten zu veröffentlichen. Wie genau die Nachricht hierfür aufgebaut sein muss, liegt dann in der Verantwortung des Messaging-Moduls.

Die dahingehend angepasste Implementierung des FeedService ist in Listing 4 zu sehen.

Listing 4

@Transactional
public void publishFeedEntry(
  UUID userId,
  String category,
  String entry) {

    if (!userService.isValidUser(userId))
      throw new RuntimeException("invalid user");

      UUID feedEntryId = UUID.randomUUID();
      Instant publishedAt = Instant.now();

    feedEntryRepository.save(
      new FeedEntryEntity(
        feedEntryId,
        userId,
        category,
        entry,
    publishedAt));

    outboundMessaging.publishFeedEntryPublishedMessage(
      feedEntryId,
      userId,
      category,
      entry,
    publishedAt);

    userService.updateLastUserActivity(userId, publishedAt);
}

Herausforderungen in CRUD-basierten Anwendungen

Die Implementierungsdetails der einzelnen Module sind jetzt zwar voreinander verborgen, doch sind die Module über Dependency Injection sehr eng miteinander gekoppelt. Hierbei wird deutlich, dass die Modularisierung von auf CRUD (Create, Read, Update, Delete) basierenden Anwendungen eine besondere Herausforderung darstellt. Abbildung 1 verdeutlicht diese Verflechtung, die oftmals in Spaghetticode mündet, anhand des chronologischen Gesamtablaufs der Operation.

neugebauer_scheffler_modulith_axon_1
Abb. 1: Beispielhafter Kommunikationsfluss innerhalb einer CRUD-Anwendung
  • Ein REST-API-Controller nimmt Anfragen zur Nachrichtenerstellung entgegen. Diese sind einem registrierten Benutzer zugeordnet und werden an die Geschäftslogik des Feed-Moduls weitergeleitet.

  • Die für die Verarbeitung der Nachrichtenerstellung benötigten Daten werden aus der Datenbank ausgelesen und geprüft. Dadurch wird z. B. sichergestellt, dass der entsprechende Nachrichtenkanal bereits existiert.

  • Die Nachricht wird erstellt und in einem geeigneten Format an einen Nachrichtenbroker übergeben, um Umsystemen die Möglichkeit zu geben, darauf entsprechend zu reagieren.

  • Die neu erstellte Nachricht wird schließlich in der Datenbank gespeichert.

  • Das UserProfile-Modul wird informiert, dass vom entsprechenden Benutzer eine neue Nachricht erstellt wurde.

  • Die Geschäftslogik des UserProfile-Moduls lädt daraufhin die Benutzerdaten aus der Datenbank, um den Zeitpunkt der letzten Benutzeraktivität zu aktualisieren. Andernfalls würden Benutzer nach einer gewissen Zeit der Inaktivität automatisch aus dem System entfernt.

  • Es wird eine Ausgangsnachricht an den Nachrichtenbroker übergeben, um andere Systeme darüber zu informieren.

  • Anschließend wird der Zeitpunkt der letzten Benutzeraktivität in der Datenbank gespeichert.

  • Schließlich wird die erfolgreiche Ausführung der Operation über das REST API an den Aufrufer quittiert.

Das Anwendungsbeispiel zeigt verschiedene Arten der Kopplung auf, die der Modularisierung entgegenwirken. Dazu zählen im Wesentlichen:

  • Kopplung auf Ebene des Programmcodes und des Datenmodells: CRUD-Anwendungen haben oft stark verflochtene Codebasen, in denen Geschäftslogik über verschiedene Schichten und Module hinweg verteilt ist. Es fehlen klare Abgrenzungen der Verantwortlichkeiten oder schlichtweg geeignete Möglichkeiten der Erweiterbarkeit. So ist in unserem Beispiel die Mitteilung an das UserProfile-Modul über die Benutzeraktivität innerhalb des Feed-Moduls durch einen expliziten Aufruf – also eine direkte Kopplung bzw. Abhängigkeit zwischen den Modulen – umgesetzt. Das wird häufig noch durch die Verknüpfung der Datenmodelle in relationalen Datenbanken verschärft, und zwar dadurch, dass viele Module die gleichen Datenstrukturen nutzen. Änderungen an diesen Strukturen können sich somit schnell auf viele verschiedene Module auswirken.

  • Technologische und transaktionale Kopplung: Die Integration zentraler Ressourcen wie Datenbanken, Nachrichtenbroker oder Caches erfolgt in CRUD-Anwendungen oft direkt innerhalb der Geschäftslogik. Zwar wird versucht, diese Kopplung durch entsprechende Schichten technologisch zu abstrahieren, jedoch kann die Geschäftslogik dadurch nicht von der Verantwortung entbunden werden, zu wissen, welche Schichten und Umsysteme aufzurufen sind. Verschärft wird diese Kopplung zudem durch die Anforderungen an die schichten- und modulübergreifende Datenkonsistenz. Die dafür genutzten technischen Transaktionen sind durch entsprechende Frameworks zwar ebenfalls wegabstrahiert, die Aufrufreihenfolge obliegt jedoch nach wie vor der Geschäftslogik. So ist es in unserem Beispiel eben nicht egal, ob der Nachrichtenbroker vor oder nach der Speicherung einer Änderung in der Datenbank informiert wird. Je nach Reihenfolge ergeben sich z. B. komplett unterschiedliche Zustellgarantien für ausgehende Nachrichten. Diese Reihenfolgen korrekt einzuhalten, ist Aufgabe der Geschäftslogik und kann im Fall modulübergreifender Transaktionen – wie in unserem Beispiel der Nachrichtenversand aus unterschiedlichen Modulen – schnell zur Herausforderung werden.

 

Command Query Responsibility Segregation

Command Query Responsibility Segregation (CQRS) ist ein Architekturmuster, das die Trennung der Verantwortung für Schreib- und Leseoperationen in einer Anwendung propagiert. Die Grundidee ist, dass Operationen, die Daten ändern (Commands), von Operationen, die Daten lesen (Queries), getrennt werden.

 

CQRS-basierte Anwendungen

Als vorherrschende Motivation für den Einsatz CQRS-basierter Anwendungen wird in aller Regel zusätzlich noch die bessere Skalierbarkeit angeführt. Schreib- und Leseoperationen können unabhängig voneinander skaliert werden, was sich in vielen Anwendungen – unabhängig von der geringeren Kopplung – positiv auf die Performanz auswirkt, da die Lese- die Schreibzugriffe sehr oft dominieren.

Diese Trennung wird durch die Einführung verschiedener Modelle erreicht:

  • Schreibmodell (Command Model): enthält sämtliche für die Geschäftslogik relevanten Daten, um Änderungen am System vorzunehmen bzw. vorab zu prüfen, ob diese zulässig sind, z. B. für die Erstellung neuer Nachrichten in einem Feed.

  • Lesemodell (Query Model): enthält projizierte Daten, die den Ist-Zustand des Systems abbilden, z. B. die Liste aller erfolgreich erstellten Nachrichten in einem Feed. Diese Daten können speziell auf die Abfrage zugeschnitten und optimiert sein, da sie redundant zum Schreibmodell gepflegt werden. Das impliziert, dass es auch mehrere Lesemodelle für unterschiedliche Abfragen geben kann.

Auf unser Anwendungsbeispiel bezogen lassen sich sowohl die Nachrichtenerstellung im Feed-Modul als auch die Benutzeraktualisierung im UserProfile-Modul als Commands abbilden. Die Geschäftslogik innerhalb der jeweiligen Module entscheidet dann anhand des jeweiligen Schreibmodells, das etwa aus der Datenbank geladen werden kann, ob der mit dem Command implizierte Änderungswunsch durchgeführt werden soll oder nicht. Im Fall einer erfolgreichen Command-Verarbeitung muss die daraus resultierende Änderung im Command Model – für zukünftige Commands – gespeichert und anschließend in die Lesemodelle überführt werden.

Im gezeigten Beispiel lassen sich zwei unterschiedliche Lesemodelle identifizieren: die Datenhaltung für eine Abfrage zuvor erfasster Nachrichten und Feeds über das REST API sowie der Nachrichtenversand an den Nachrichtenbroker. Letzteres mag auf den ersten Blick seltsam anmuten, da man bei Lesemodellen eher an persistente Datenhaltung denkt. Letztlich kann aber jegliche Projektion vollzogener Änderungen im System als Lesemodell verstanden werden, also auch die auf ein oder mehrere Commands folgenden Ausgangsnachrichten.

Die durch CQRS erzwungene Trennung von Lese- und Schreibmodellen bedingt typischerweise einen gewissen Mehraufwand in der Umsetzung derartig aufgebauter Systeme, fördert aber deren Modularisierung. Betrachten wir die zuvor erwähnten negativen Kopplungen von CRUD-Anwendungen, so lassen sich daraus für CQRS-Architekturen die folgenden Vorteile ableiten:

  • Erstens gibt es durch die Trennung von ändernden und lesenden Operationen klarere Verantwortlichkeiten und Strukturen im Programmcode. Die Geschäftslogik eines Moduls fokussiert sich auf die Verarbeitung von Commands und die dafür notwendige Vorabprüfung der Geschäftsregeln unter Zuhilfenahme des jeweiligen Schreibmodells. Durch Verlagerung der Leseoperationen auf dedizierte Lesemodelle verhindert man zudem die unnötige Kopplung auf Datenstrukturebene. Schreibmodelle sind in gewisser Weise vollständig durch ihre Geschäftslogik gekapselt. Die Übergabe von Daten an Dritte – also auch an andere Module – geschieht ausschließlich durch Projektion entsprechender Lesemodelle.

  • Die Trennung in Command-Verarbeitung und Lesemodell-Projektion(en) fördert zweitens die Entkopplung von Technologien. Die Geschäftslogik, die die schreibenden Operationen verarbeitet, wird technologieagnostisch über Commands angesteuert, also z. B. simple Data Transfer Objects (DTO). Zur Prüfung der Geschäftsregeln wird lediglich ein Schreibmodell angebunden, das sämtliche Informationen enthält, die dafür benötigt werden. Die erfolgreiche Command-Verarbeitung spiegelt sich ebenfalls ausschließlich darin wider. Ob das Schreibmodell nun in einer Datenbankressource oder einem Event Store abgelegt ist, spielt dabei eine eher untergeordnete Rolle. Viel wichtiger ist, dass es sich um genau eine Ressource handelt und die transaktionale Kopplung an weitere Ressourcen vollständig vermieden wird.

Die oft mehrere Ressourcen überspannenden Transaktionen in CRUD-Anwendungen und die damit verbundenen Herausforderungen, was die Commit-Reihenfolge angeht, werden durch die Auslagerung auf dedizierte, in aller Regel asynchron arbeitende Projektoren für die jeweiligen Lesemodelle vermieden.

 

CQRS und Events

Obwohl CQRS die Modularisierung erheblich unterstützen kann, steht eine wesentliche Herausforderung im Mittelpunkt: die effektive Synchronisation zwischen den getrennten Lese- und Schreibmodellen. Angesichts der Tatsache, dass die Änderungen an einem Schreibmodell auf mehrere Lesemodelle projiziert werden müssen, stellt sich unmittelbar die Frage, ob diese Synchronisation direkt – also etwa im Programmcode oder über Datenbank-Trigger – oder nachgelagert – also asynchron – erfolgen sollte.

Die direkte Synchronisation, insbesondere im Programmcode, führt den Grundgedanken der Trennung von Lese- und Schreibmodellen schnell ad absurdum. Die Trennung der Verantwortlichkeiten wird dadurch gewissermaßen aufgehoben, da die Geschäftslogik (Schreibmodell) nun erneut in der Pflicht ist, sämtliche Lesemodelle zu kennen und diese instant im Rahmen der Command-Ausführung zu aktualisieren. Das sorgt neben der engen Kopplung – und somit schlechterer Modularisierung – für eine steigende Latenz bei den Schreiboperationen. Zudem lassen sich nachträglich nur schwer neue Lesemodelle etablieren, die die historischen Änderungen ebenfalls abbilden sollen.

Die nachgelagerte (asynchrone) Synchronisation der Lesemodelle zeichnet sich durch eine deutlich losere Kopplung aus und lässt sich bei steigender Anzahl der Lesemodelle besser und zielgerichteter skalieren. Die zwei wesentlichen Herausforderungen dieses Ansatzes sind die inhärente „Eventual Consistency“ – die in verteilten Systemen aber sowieso unvermeidbar ist –, und die Frage, wie sich die im Schreibmodell vollzogenen Änderungen nachträglich überhaupt erkennen lassen. Ist das Schreibmodell selbst nämlich so aufgebaut, dass es lediglich den aktuellen Stand, also z. B. das aktuelle Benutzerprofil, abbildet, lassen sich die Änderungen daran nachträglich nicht mehr nachvollziehen und folglich auch nicht in die Lesemodelle projizieren.

An dieser Stelle kommen Events ins Spiel, die die vollzogenen Änderungen am Schreibmodell beschreiben. Speichert man diese Events, z. B. in einem Event Store, kann man auf deren Basis zurückliegende Änderungen nachvollziehen und Lesemodelle aktualisieren. Die Projektion der Lesemodelle lässt sich folglich durch eine Art Abonnement auf den Event Store, der durch die Änderungen an Schreibmodellen gefüllt wird, umsetzen, um den Fortschritt bei der Projektion zu verwalten.

Zudem reduziert sich durch die Einführung der Events die Kopplung zwischen Lese- und Schreibmodellen, was der Modularisierung wiederum zugutekommt. So lassen sich dedizierte Projektoren beispielsweise in eigenständige Komponenten auslagern, solange sie Zugriff auf den Event Store und ihr Abonnement haben, und sogar individuell deployen, falls dies erforderlich sein sollte.

 

CQRS mit Axon

Das Axon Framework [3] unterstützt die Umsetzung CQRS-basierter Anwendungen auf der JVM und bringt gleichzeitig einen datenbankbasierten Event Store mit. Es kann besonders gut in Spring-Boot-basierten Anwendungen eingesetzt werden. Zentrale Framework-Komponenten werden dabei direkt als Beans im Spring-Kontext bereitgestellt, und Axon-Annotationen innerhalb anwendungsspezifischer Komponenten ermöglichen die Deklaration geeigneter Command und Event Handler. Die folgenden Elemente sind notwendig für die Umsetzung CQRS-basierter Anwendungslogik mit Axon:

  • Commands und Events: Commands repräsentieren Änderungswünsche, während Events vollzogene Änderungen widerspiegeln. Für beide Anwendungszwecke lassen sich bei Axon reguläre Java-Klassen oder Records verwenden. Es ist lediglich darauf zu achten, dass speziell die Events serialisierbar sein sollten, um sie z. B. als JSON im Event Store speichern zu können.

  • Command Gateway: Das vom Axon Framework bereitgestellte Command Gateway verantwortet die Übermittlung von Commands an die entsprechenden Command Handler. Diese werden anhand des Command-Typs ausgewählt und entsprechend aufgerufen. Listing 5 zeigt beispielhaft den Command-Aufruf aus einem Spring REST API Controller heraus.

Listing 5

private final CommandGateway commandGateway;

@PostMapping("/feed")
public void publishFeedEntry(@RequestBody PublishFeedRequest request) {
  commandGateway.sendAndWait(
    new PublishFeedEntryCommand(
      request.userId(),
      request.category(),
      request.entry()
    )
  );
}
  • Command Handler: Mit @CommandHandler annotierte Methoden verantworten die Ausführung der entsprechenden Commands und müssen somit anhand des Command-Typs eindeutig identifizierbar sein. Ihnen obliegt es, anhand des Schreibmodells zu entscheiden, ob die gewünschte Command-Ausführung zulässig ist oder nicht. Im Fall einer erfolgreichen Ausführung steht in aller Regel die Aktualisierung des Schreibmodells sowie die Veröffentlichung ein oder mehrerer neuer Events, andernfalls wird eine entsprechende Exception geworfen. Listing 6 zeigt eine mögliche Command-Handler-Implementierung für das PublishFeedEntryCommand.

Listing 6

private final EventGateway eventGateway;

@CommandHandler
public void handle(PublishFeedEntryCommand command) {
  if (!userService.isValidUser(command.userId()))
    throw new RuntimeException("invalid user");
    UUID feedEntryId = UUID.randomUUID();
    Instant publishedAt = Instant.now();

  feedEntryRepository.save(
    new FeedEntryEntity(
      feedEntryId, 
      command.userId(), 
      command.category(), 
      command.entry(), 
      publishedAt
    )
  );
  eventGateway.publish(
    new FeedEntryPublishedEvent(
      feedEntryId, 
      command.userId(), 
      command.category(), 
      command.entry(), 
      publishedAt
    )
  );
}
  • Event Gateway: Das von Axon bereitgestellte Event Gateway verantwortet die Übermittlung und Speicherung der Events im Event Store entsprechend der Reihenfolge ihrer Veröffentlichung. Die Events werden dazu in ein konfigurierbares, serialisierbares Format, z. B. JSON, umgewandelt.

  • Event Handler: Mit @EventHandler annotierte Methoden verantworten die Projektion zuvor gespeicherter Events. Sie werden standardmäßig ebenfalls in der Reihenfolge der Veröffentlichung aufgerufen, jedoch asynchron. Axon überwacht zudem den Fortschritt der Verarbeitung, ruft fehlgeschlagene Handler ggf. erneut auf und setzt die Verarbeitung nach dem zuletzt erfolgreich verarbeiteten Event fort – auch über Systemgrenzen hinweg. Listing 7 zeigt exemplarisch die Eventverarbeitung zum Versenden von Ausgangsnachrichten.

Listing 7

@EventHandler
public void on(FeedEntryPublishedEvent event) {
  FeedEntryPublishedMessage message = new FeedEntryPublishedMessage(
    event.feedEntryId(),
    event.userId(),
    event.category(),
    event.entry(),
    event.publishedAt()
  );

  publish("feed-topic", message);
}
  • Query Gateway und QueryHandler: Der Vollständigkeit halber seien hier auch das Query Gateway und die mit @QueryHandler annotierten Methoden erwähnt. Diese ermöglichen es – vergleichbar mit dem Command Gateway – ein Routing entsprechender Clientabfragen an ein Lesemodell zu kapseln. Ihr Einsatz ist jedoch nicht verpflichtend, und es können – sofern das Routing nicht benötigt wird –zuvor persistierte Lesemodelle oftmals auch direkt vom Client abgefragt werden, sei es durch einen simplen REST-API-Controller oder eine direkt abfragbare Datenbank.

Stay tuned

Immer auf dem Laufenden bleiben! Alle News & Updates:

[mc4wp-simple-turnstile]

 

CQRS mit Spring Modulith

Das Axon Framework unterstützt die Erstellung CQRS-basierter Anwendungen in Java und integriert sich dabei hervorragend in Spring-Anwendungen. Dennoch bringt es auch zusätzliche Komplexität mit sich. Damit stellt sich die Frage, ob CQRS-Architekturen nicht auch mit Spring-Bordmitteln umgesetzt werden können.

Das Veröffentlichen von Events innerhalb einer Spring-Anwendung wird seit jeher vom Spring Framework unterstützt. Der ApplicationEventPublisher steht in jeder Spring-Anwendung als Bean zur Verfügung und kann per Dependency Injection zur Veröffentlichung von Events verwendet werden. Er bietet dazu eine Methode publishEvent an, die ein beliebiges Object als Event Payload akzeptiert. Der Code aus Listing 4 könnte also unter zu Hilfenahme von Spring Application Events stark vereinfacht werden, sodass er vergleichbar mit Listing 6 wird. Die Kapselung der einzelnen Methodenparameter in einem DTO (Command) kann durch Java-Standardmittel erreicht und damit ebenfalls in unserer Spring-Anwendung umgesetzt werden. Die klarere Benennung als Command macht die Änderungsabsicht deutlich, das DTO stellt somit ein präziseres API dar. Listing 8 zeigt diesen Code.

Listing 8

private final ApplicationEventPublisher eventPublisher;

@Transactional
public void handle(PublishFeedEntryCommand command) {
  if (!userService.isValidUser(command.userId()))
    throw new RuntimeException("invalid user");
  UUID feedEntryId = UUID.randomUUID();
  Instant publishedAt = Instant.now();

  feedEntryRepository.save(
    new FeedEntryEntity(
      feedEntryId, 
      command.userId(), 
      command.category(), 
      command.entry(), 
      publishedAt
    )
  );
  eventPublisher.publishEvent(
    new FeedEntryPublishedEvent(
      feedEntryId, 
      command.userId(), 
      command.category(), 
      command.entry(), 
      publishedAt
    )
  );
}

Das Konsumieren dieser Events ist Spring-typisch über annotierte Methoden in Spring Beans (z. B. Services, Components oder Configurations) möglich. Die Standardannotation hierfür ist @EventListener. Entsprechend annotierte Methoden werden in dem Moment synchron aufgerufen, in dem ein Event veröffentlicht wird. Für die Entkopplung der Module ist dieser Ansatz jedoch noch nicht ausreichend, da Seiteneffekte unserer Businesslogik, z. B. eine fehlschlagende Nachrichtenveröffentlichung, immer noch unseren eigentlichen fachlichen Code unterbrechen würde.

Das Verlagern der Seiteneffekte an das Ende der Transaktion verringert diese Problematik und ist über die Annotation @TransactionalEventListener möglich. Hierbei wird die Event-Listener-Methode standardmäßig nach dem Commit der Transaktion ausgeführt, die den Event veröffentlicht hat. Nach dem Commit heißt dabei jedoch nicht, dass die Transaktion nicht verlängert würde (die Event-Listener-Methode wird in der Clean-up-Phase der Transaktion ausgeführt). Das bedeutet auch hier, wenn die eigentliche Fachlogik erfolgreich durchläuft, kann der Aufrufer dennoch durch Seiteneffekte Fehler erhalten. Um das zu verhindern, sollte der Event Listener dediziert in einer neuen Transaktion ausgeführt werden. Dies kann durch die zusätzliche Annotation @Transactional(propagation = Propagation.REQUIRES_NEW) erreicht werden.

Möchte man die Event-Listener-Methode zusätzlich vollständig asynchron ausführen, sodass der eigentliche Programmablauf weiterlaufen kann, während Seiteneffekte ausgeführt werden, kann die Annotation @Async benutzt werden. In Summe trägt eine typische Event-Listener-Methode dann drei Annotationen – wie in Listing 9.

Listing 9

@TransactionalEventListener
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Async
public void on(FeedEntryPublishedEvent event) {
  ...
}

Möchte man Spring Application Events nutzen, um Businesslogik und Seiteneffekte voneinander zu trennen, ist dies eine sehr häufig genutzte Kombination von Annotationen. Spring Modulith bündelt diese daher in einer eigenen Annotation: @ApplicationModuleListener. Das Aktualisieren der letzten Aktivität eines Benutzers könnte entsprechend in einem eigenen Event Listener erfolgen, wie es in Listing 10 gezeigt ist.

Listing 10

@ApplicationModuleListener
public void on(FeedEntryPublishedEvent event) {
  UserEntity user = this.userRepository.findByUserId(event.userId());

  user.setLastUpdatedAt(event.publishedAt());

  userRepository.save(user);
}

Wie auch im Axon-Beispiel, in dem wir unseren fachlichen Code bis auf die beidseitig bekannte Event-Klasse vollständig vom Messaging entkoppelt haben, haben wir einen sehr hohen Grad der Entkopplung zwischen den Modulen User und Feed erlangt. Der Feed muss nicht mehr wissen, dass die letzte Aktivität eines Benutzers getrackt wird, er muss nicht mehr wissen, wie diese zu aktualisieren ist oder Ähnliches. Die gesamte Schnittstelle beschränkt sich auf das veröffentlichte Event.

Ein weiteres Problem, das mit Axon sehr einfach zu lösen ist, ist die Persistierung der Events und die garantierte Zustellung an jeden Event Handler. Analog müssen wir auch mit Spring Application Events sicherstellen, dass jedes Event an jeden zuständigen Event Listener zugestellt wird und er es auch erfolgreich verarbeitet hat. Hier hilft Spring Modulith, da es den Event-Mechanismus von Spring um eine Persistenzschicht erweitert. Veröffentlichte Events werden dabei von Spring Modulith in derselben Transaktion persistiert, in der sie auch veröffentlicht werden.

Eine erfolgreiche Verarbeitung durch die Event-Listener-Methode wird ebenfalls in der Datenbank persistiert. Hierfür wird die Transaktion der Event-Verarbeitung genutzt. Es kann also nachvollzogen werden, dass jedes Event von jedem zuständigen Event Listener erfolgreich verarbeitet wurde. Ein Retry-Mechanismus wie bei Axon steht mit Spring Modulith bisher nicht zur Verfügung. Die Nachverarbeitung fehlerhafter Event-Listener-Ausführungen muss also explizit implementiert werden.

Eine Abstraktion zum Aufrufen der Fachlogik, wie das Command Gateway in Axon, fehlt im Spring-Ökosystem. Der Aufruf der passenden Methode, z. B. aus dem REST-Controller, muss explizit erfolgen. Hierfür kann Dependency Injection des Fachlogik-API genutzt werden, z. B. wie in Listing 11.

Listing 11

private final FeedService feedService;

@PostMapping
public void publishFeedEntry(@RequestBody PublishFeedEntryBody body) {
  feedService.handle(
    new PublishFeedEntryCommand(
      body.userId(),
      body.category(),
      body.entry()));
}

Event Sourcing

Die Einführung von Events zur Entkopplung der Lese- und Schreibmodelle in CQRS-basierten Anwendungen (unabhängig davon, ob diese mit Axon oder Spring Modulith erstellt wurden) führt letztlich zu der Frage: Bedarf es überhaupt noch einer dedizierten Speicherung des Schreibmodells? Folgt man der Argumentation, dass Events sämtliche Änderungen am Schreibmodell vollständig beschreiben, so lässt sich aus der Summe aller Events in der korrekten Reihenfolge letztlich auch das Schreibmodell selbst erneut wiederherstellen. Das wird Event Sourcing genannt.

Dabei werden im Rahmen der Command-Ausführung die für die betreffende Instanz – also z. B. das Benutzerprofil – zuvor persistierten Events geladen, um damit eine Instanz des Schreibmodells wiederherzustellen. Das Modell selbst ist somit nicht direkt persistent, sondern wird im Bedarfsfall aus den vorausgegangenen Events instanziiert. Änderungen am Modell werden über die Veröffentlichung und Speicherung neuer Events erreicht, die für nachfolgende Command-Ausführungen dann erneut für das Event Sourcing in Betracht gezogen werden (Abb. 2).

neugebauer_scheffler_modulith_axon_2
Abb. 2: Zusammenspiel von CQRS und Event Sourcing

Event Sourcing stellt somit die perfekte Ergänzung für CQRS-basierte Anwendungen dar bzw. wird durch deren forcierte Trennung der Lese- und Schreibmodelle überhaupt erst ermöglicht. Die wesentlichen Vor- und Nachteile von Event Sourcing lassen sich wie folgt zusammenfassen:

  • Die Nutzung von Events zur Wiederherstellung des Schreibmodells fördert die vollständige Speicherung sämtlicher Änderungen innerhalb der Events, da diese ansonsten verloren gingen. Dadurch bleiben, im Vergleich zu einer separaten Speicherung, Schreibmodell und Events ständig synchron.

  • Der Fokus auf Speicherung sämtlicher Daten in Form von Events wirkt sich in aller Regel positiv auf das Event-Design und die Struktur aus. Fachliche Anforderungen, aus denen die Events primär abgeleitet werden, müssen frühzeitig mit technischen Anforderungen abgeglichen werden, um eine geeignete Struktur festzulegen. Das fördert u. a. eine einheitliche Sprache beim Event-Design und in der Abstimmung zwischen Fachexperten und Entwicklern.

  • Dennoch kann der Einsatz von Event Sourcing schnell zu Performanceeinbußen bei der Command-Ausführung führen, da das Laden und Wiederherstellen des Schreibmodells bei steigender Anzahl von Events aufwendiger wird.

Event Sourcing wird aktuell (noch) nicht von Spring Modulith unterstützt, sodass im Folgenden lediglich die Umsetzung mit Axon im Fokus steht.

Axon setzt beim Event Sourcing auf den auch aus dem Domain-Driven Design bekannten Begriff des Aggregate. Dieses bildet die fachliche – und in diesem Fall auch technische – Klammer für sämtliche Command-Ausführungen und daraus resultierende Events. Dementsprechend bedarf es eines eindeutigen Aggregate-Identifiers, um verschiedene Instanzen auseinanderhalten zu können, also z. B. verschiedene Benutzerprofile anhand des Benutzernamens.

Eine mit @Aggregate annotierte Klasse kann folglich zur Laufzeit als Aggregate-Instanz fungieren. Dazu wird sie von Axon instanziiert und sämtliche zuvor veröffentlichten Events werden den darin enthaltenen, mit @EventSourcingHandler annotierten Methoden übergeben, sodass der Programmcode das Schreibmodell rekonstruieren kann. Schließlich werden die bereits bekannten Command Handler – ebenfalls Teil derselben Klasse – aufgerufen, die wiederum über AggregateLifecycle.apply() neue Events, die jeweilige Instanz betreffend, veröffentlichen. Listing 12 zeigt ein Event Sourced Aggregate mit Axon.

Listing 12

@Aggregate
public class FeedAggregate {

  @AggregateIdentifier
  private UUID id;

  private FeedEntry latestEntry;

  @CommandHandler
  public FeedAggregate(PublishFeedEntryCommand command) {
    AggregateLifecycle.apply(
      new FeedEntryPublishedEvent(UUID.randomUUID())
    );
  }

  @EventSourcingHandler
  public void on(FeedEntryPublishedEvent event) {
    this.id = event.feedEntry(); 
  }

  // more command and event sourcing handlers
}

Fazit

Größere Anwendungen bestehen, wissentlich oder nicht, in aller Regel aus mehreren Modulen. Diese müssen miteinander interagieren, damit der fachliche Nutzen der Anwendung erreicht werden kann. Bei der Modularisierung geht es folglich nicht darum, Module vollständig voneinander zu trennen, sondern darum, die Abgrenzung der Module und deren Interaktion kontrollieren zu können.

Dependency Injection hilft zwar, konkrete Modulimplementierungen und deren Initialisierung voneinander zu entkoppeln, verhindert aber nicht, dass Module untereinander ihre Schnittstellen unkontrolliert konsumieren – durch entsprechende Methodenaufrufe. Spring Modulith ermöglicht es, über entsprechende Tests sicherzustellen, dass keine internen, privaten Schnittstellen über Modulgrenzen hinweg aufgerufen werden.

Eine weitergehende Abstraktion der Schnittstellen kann durch den Versand entsprechender DTO-Nachrichten erreicht werden, die entweder Änderungsabsichten (Commands) oder erfolgte Änderungen (Events) repräsentieren. Dafür bedarf es einer geeigneten Komponente zur Nachrichtenübermittlung. In Spring übernimmt dies der Application Context, während Axon eigene Komponenten für den Command- und Event-Versand bereitstellt. Der wesentliche Vorteil dieser Art der Kommunikation ist die Möglichkeit, insbesondere Events in weiteren Modulen zu konsumieren, ohne das versendende Modul anpassen zu müssen.

Die Verarbeitung erfolgter Änderungen in Form von Events kann jedoch temporär zu Fehlern führen, z. B. zu gestörtem E-Mail-Versand, und stellt somit eine starke Kopplung zur Laufzeit dar. Folgerichtig sollten Events persistiert und asynchron verarbeitet werden, was u. a. zu einer geringeren Latenz in der Command-Verarbeitung führt. Eine derartige Entkopplung bedingt zwingend den Umgang mit der daraus resultierenden Eventual Consistency, macht das Gesamtsystem aber deutlich stabiler und skalierbarer. Die strikte asynchrone Trennung von Eventveröffentlichung und -verarbeitung führt letztlich zur Trennung von Schreib- und Lesemodellen – bekannt als CQRS.

CQRS – richtig angewendet – führt folgerichtig zu besserer Modularisierung von Anwendungen und lässt sich sowohl mit Spring Modulith als auch mit Axon umsetzen. Es ermöglicht darüber hinaus Event Sourcing, d. h. die Verwendung der zuvor veröffentlichten Events zur Rekonstruktion des Schreibmodells. Eine entsprechende Unterstützung dafür ist in Spring Modulith bisher jedoch nicht vorgesehen.

 

🔍 Frequently Asked Questions (FAQ)

1. Was ist Spring Modulith?
Spring Modulith ist ein Framework, das Entwicklern hilft, modulare Monolithen zu erstellen, indem es Prinzipien wie CQRS und Event Sourcing integriert, um die Wartbarkeit und Skalierbarkeit zu erhöhen.

2. Was ist Axon Framework?
Axon ist ein Framework, das sich auf die Implementierung von CQRS (Command Query Responsibility Segregation) und Event Sourcing konzentriert, um komplexe Systeme zu modellieren und zu verwalten.

3. Wie unterscheiden sich Spring Modulith und Axon?
Spring Modulith legt den Fokus auf die Strukturierung von Monolithen in Module, während Axon sich auf die Implementierung von CQRS und Event Sourcing konzentriert, um die Komplexität in verteilten Systemen zu managen.

4. Welche Vorteile bietet Spring Modulith?
Es ermöglicht eine klare Strukturierung von Monolithen, fördert die Modularität und erleichtert die Wartung und Erweiterung der Anwendung.

5. Welche Vorteile bietet Axon Framework?
Axon bietet eine robuste Unterstützung für CQRS und Event Sourcing, was besonders in komplexen, verteilten Systemen von Vorteil ist, um Skalierbarkeit und Flexibilität zu gewährleisten.

6. Für welche Szenarien eignet sich Spring Modulith?
Spring Modulith ist ideal für Anwendungen, die als Monolith entwickelt werden sollen, aber dennoch von den Vorteilen der Modularität profitieren möchten.

7. Für welche Szenarien eignet sich Axon Framework?
Axon ist besonders geeignet für Systeme, die komplexe Geschäftslogik enthalten und von den Prinzipien von CQRS und Event Sourcing profitieren können, insbesondere in verteilten Architekturen.

 

The post Modulare Architekturen: Spring Modulith vs. Axon Framework im Vergleich appeared first on MAD Summit.

]]>
API Gateways in Kubernetes https://mad-summit.de/blog/api/api-gateways-in-kubernetes/ Mon, 20 Jan 2025 16:44:33 +0000 https://mad-summit.de/?p=106898 In diesem Artikel wollen wir zeigen, warum API Gateways im Kubernetes-Umfeld mehr sind als nur ein Einstiegspunkt - wir bieten einen Blick auf die Synergien und Potenziale.

The post API Gateways in Kubernetes appeared first on MAD Summit.

]]>
Kubernetes, oder kurz K8s, ist heute der De-facto-Industriestandard für die Bereitstellung und Verwaltung von containerisierten Anwendungen. Denn moderne Applikationen werden meist Cloud-Native und im Stil von Microservices-Architekturen entwickelt. Doch wie lassen sich diese in autonome Services aufgeteilten Anwendungen sicher und effizient für Zugriffe von außerhalb eines Kubernetes-Clusters bereitstellen? Ein API Gateway kann hier Abhilfe schaffen und gleichzeitig Entwicklungsteams entlasten sowie Transparenz schaffen. Wie genau? Das sehen wir uns im Folgenden an.

Bevor wir zum eigentlichen Thema kommen, noch einmal kurz der Hinweis, was ein API Gateway ist und warum es eine zentrale Architekturkomponente darstellt. API Gateway ist ein Architekturpattern [1] und zielt auf die Bereitstellung eines zentralen Einstiegspunkts für externe API-Nutzer ab. Grundlegend hat das erstmal wenig mit Technik zu tun. In der konkreten Ausprägung übernimmt es ähnliche Funktionen wie ein Reverse Proxy. Es reicht Anfragen an die passenden Backend-Services weiter und kümmert sich um querschnittliche Funktionen wie TLS-Terminierung, Authentifizierung, Autorisierung, Load Balancing, Caching und Logging.

Für Entwicklerteams bedeutet das mehr Produktivität und Zufriedenheit, weil wiederkehrende technische Aspekte zentral abgedeckt werden und nicht mehr wiederholt implementiert werden müssen. Ein API Gateway unterstützt zudem die Governance, indem es den Aufbau eines zentralen Policy-Managements fördert und für Transparenz bei der API-Bereitstellung und -Verwendung sorgt.

 

Entwickle Schnittstellen, die einfach funktionieren

Power-Workshops zu API Design & Entwicklung (22. - 26. Juni 2026, München)

Kubernetes Service Management und die Herausforderung mit Load Balancers

Um Applikationen von außerhalb eines Kubernetes-Clusters aufrufen zu können, wird ein Kubernetes Service vom Typ Load Balancer benötigt. In Cloud-Umgebungen wird hierbei eine Load-Balancer-Instanz gestartet – wenn wir nicht aufpassen, wird im schlimmsten Fall eine Load-Balancer-Instanz pro Applikation gestartet. Das ist eine kostspielige und unpraktische Lösung!

Hier setzt das Konzept eines zentralen Reverse Proxys an, der in Kubernetes über einen Load Balancer Service erreichbar gemacht wird und Routing, TLS-Terminierung und Load Balancing übernimmt.

Der Vorteil der in Abbildung 1 dargestellten Architektur ist, dass nur ein Load Balancer für den Proxy benötigt wird. Die in Kubernetes bereitgestellten Applikationen sind über den Proxy erreichbar.

jm_2_25_jax_bernhardt_kubernetes_mn_1
Abb. 1: Reverse-Proxy-Konzept in Kubernetes

Kubernetes Ingress und Gateway API – Kubernetes-eigene Konzepte zur Steuerung des Zugriffs

In Kubernetes wurde das Proxy-Konzept weiterentwickelt. Heute wird häufig ein Ingress Controller zur Verwaltung externer Zugriffe auf Services genutzt. Ein typischer Ingress Controller erlaubt Routing auf Basis von Host- und Pfadinformationen und wird Kubernetes-nativ über das Ingress-Objekt [2] konfiguriert (Listing 1).

Listing 1

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: httpbin
  namespace: demo
  annotations:
    ingress.kubernetes.io/ssl-redirect: "false"
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: httpbin
            port:
              number: 80

Seit kurzem steht außerdem das Gateway API zur Verfügung [3], das über HTTP hinaus Protokolle wie gRPC, TCP und UDP unterstützt und mehr Konfigurationsoptionen bietet (Listing 2).

Listing 2

apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: httpbin
  namespace: demo
spec:
  parentRefs:
  - name: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: httpbin
      port: 80

Beide Spezifikationen haben ihre Berechtigung und werden parallel unterstützt – je nach Anwendungsfall kann Ingress oder das Gateway API die passende Lösung sein. Am Markt gängige Ingress Controller unterstützen neben Ingress in der Regel das Gateway API [4].

Abbildung 2 stellt dar, wie sich ein Ingress Controller in Kubernetes integriert und wie entsprechende Regeln über zum Beispiel Ingress- oder HTTPRoute-Definitionen konfiguriert werden können.

jm_2_25_jax_bernhardt_kubernetes_mn_2
Abb. 2: Ingress Controller in Kubernetes

Wir halten fest: Kubernetes-Applikationen können via Ingress oder Gateway API einfach und effizient für die externe Nutzung verfügbar gemacht werden.

API Gateway vs. Ingress Controller: Ein Vergleich der Funktionen

Dem aufmerksamen Leser wird es aufgefallen sein: Mit API Gateway und Ingress Controller stehen unterschiedliche Lösungsbausteine zur Verfügung, die grundlegend dasselbe Problem adressieren.

Während Ingress oder das Gateway API die grundlegenden Funktionen wie Load Balancing, Routing und SSL/TLS-Terminierung abdecken, bietet ein API Gateway umfassendere Fähigkeiten wie Authentifizierung, Autorisierung, Rate Limiting, Caching und Observability. Tabelle 1 zeigt die Unterschiede.

Capability Ingress Controller API Gateway
Load Balancing Ja Ja
Routing Ja Ja
SSL/TLS-Terminierung Ja Ja
Authentifizierung Nein Ja
Autorisierung Nein Ja
Rate Limiting Nein Ja
Caching Nein Ja
Request-/Response-Validierung Nein Ja
Observability Nein Ja

Tabelle 1: Gegenüberstellung der Capabilities von Ingress Controller und API Gateway

Wie dargestellt, übernimmt ein API Gateway mehr als nur das einfache Routing und kann ein zentraler Baustein für ein umfassendes API-Management werden.

Wer bereits mit Ingress Controllern gearbeitet hat, wird aus eigener Erfahrung wissen, dass die Grenzen technisch verschwimmen. Denn teilwiese implementieren Ingress Controller Funktionalitäten, die in Tabelle 1 einem API Gateway zugeschrieben werden. Wichtig ist daher, stets zu wissen, was das Ziel ist.

Die Qual der Wahl

Architekten und Entwickler müssen anhand von Anforderungen und Use Cases entscheiden, welches Konzept oder welche Technologie die bestehenden Herausforderungen am besten löst. Wie immer gibt es an dieser Stelle keine pauschale Lösung. Geht es ausschließlich um die Erreichbarkeit von Kubernetes-Applikationen außerhalb des Clusters und existieren keine weitergehenden Anforderungen an API-Management oder IT-Sicherheit, wird in vielen Fällen ein Ingress Controller ausreichen.

Viele IT-Systemlandschaften sind heute aber heterogen aufgestellt und basieren auf unterschiedlichen, auch nicht containerbasierten Plattformen. Zudem verteilt sich die Infrastruktur. Der Weg geht von reinen On-Premises-Lösungen hin zu hybriden und Multi-Cloud-Architekturen. APIs existieren ebenfalls bereits, aber häufig ohne jegliche Visibilität und Management.

Um die Gefahr einzudämmen, dass sich die APIs zu einem unkontrolliert wachsenden und undurchsichtigen Dschungel entwickeln, sollten Architekten den Einsatz eines API Gateway in Erwägung ziehen. Und dieses API Gateway sollte die notwendige Flexibilität mitbringen, um den Anforderungen einer verteilten, heterogenen Landschaft gerecht zu werden.

API Gateways und Kubernetes

API Gateways sind heute bereits häufig Bestandteil vieler IT-Systemlandschaften. Viele Unternehmen verwenden heute noch API Gateways, die auf älteren Architekturen basieren, wie etwa das Axway oder Layer 7 API Gateway. Sie stellen uns vor typische Herausforderungen, wie wir sie von Legacy-Applikationen kennen. Es geht also um Themen wie Skalierbarkeit, dynamische Konfiguration, Automatisierbarkeit etc. Zudem sind ältere API Gateways meist ressourcenintensiv, was CPU- und Speichernutzung angeht. Daher stoßen sie in hybriden und Multicloud-Umgebungen häufig an Grenzen. Auch sind sie in der Regel nicht konform mit Cloud-nativen Prinzipien (wie den Twelve-Factor-App-Regeln [5]) und lassen sich in hybriden oder Multi-Cloud-Szenarien schlecht betreiben. Für den Betrieb in dynamischen Umgebungen wie Kubernetes sind diese also nicht geeignet.

Es kann klar gesagt werden: In Zeiten von Containern, Kubernetes und Multi-Cloud ist ein Umdenken nötig und auch zu beobachten. Cloud-native oder sogar -agnostische API Gateways – etwa von Kong, Gravitee.io oder KrakenD – bieten hier Flexibilität und können sogar direkt in Kubernetes betrieben werden. So ein Gateway wird über ein Kubernetes Deployment beschrieben und läuft, wie in Abbildung 3 dargestellt, in Form von Pods im Cluster. Die Erreichbarkeit erfolgt über Ingress- oder HTTPRoute-Definitionen, während Routing- und Policy-Einstellungen über ein Administrations-API gesteuert werden.

jm_2_25_jax_bernhardt_kubernetes_mn_3
Abb. 3: API Gateway mit agnostischer Architektur in Kubernetes

Also alles gut, oder?

Nun ja, bis auf einige Einschränkungen …Aus operativer Sicht ist der in Abbildung 3 dargestellte architektonische Aufbau nicht optimal. Das Gateway ist zwar auf Cloud-Native-Prinzipien aufgebaut, allerdings bedeutet das nicht, dass es auch konform mit den Kubernetes-Mechanismen und Konzepten ist und diese optimal ausnutzt:

  • Betriebliche Komplexität: Die Konfiguration des API Gateway muss irgendwo gespeichert werden. Entweder wird sie als Datei im Docker-Image hinterlegt, was aus Sicht der dynamischen Konfigurierbarkeit problematisch ist. Alternativ kann auch eine Datenbank im Kubernetes-Cluster oder extern bereitgestellt werden. Insgesamt erhöht sich hierdurch die betriebliche Komplexität.

  • Skalierbarkeit: Die Herausforderungen im Bereich der Konfiguration wirken sich auch negativ auf die Skalierbarkeit aus. Vor allem, wenn eine Datenbank verwendet wird, da diese entsprechend mitskaliert werden muss.

  • Sicherheit: Der dargestellte Aufbau birgt Sicherheitsrisiken. Da das Administrations-API außerhalb des Clusters verfügbar ist, um das API Gateway konfigurieren zu können, müssen sich Betriebsteams um eine Absicherung kümmern. Weiterhin muss sichergestellt werden, dass Kubernetes-Applikationen nicht am API Gateway vorbei clusterextern verfügbar gemacht werden. Das kann z. B. durch die Einführung eines Admission Controllers wie Kyverno [6] und die Definition entsprechender Regelwerke verhindert werden, erhöht aber ebenfalls die betriebliche Komplexität.

Kubernetes-native API Gateways als zukunftsfähige Lösung

Aber wie können wir die zuvor beschriebenen Herausforderungen lösen? Die Antwort ist: durch die Verwendung Kubernetes-nativer API Gateways! Solche Gateways integrieren sich optimal in das Kubernetes-Ökosystem. Sie benötigen kein separates Administrations-API, da die Konfiguration nativ über Kubernetes erfolgt. Mit Hilfe von Custom Resource Definitions (CRDs) können Policies und Services direkt in Kubernetes beschrieben werden. Der gesamte Lifecycle des API Gateways wird mit Kubernetes-Bordmitteln abgebildet – eine ideale Lösung für GitOps-Ansätze, was auch der Entwicklereffizienz zugutekommt, da das Kubernetes-Ökosystem nicht verlassen werden muss.

Weiterhin vereinfacht ein Kubernetes-natives API Gateway auch die Handhabung im Betrieb, da Standard-Kubernetes-Mechanismen genutzt werden. Werden Kubernetes-Operatoren verwendet, können betriebliche Abläufe nach der initialen Installation automatisiert und somit effektiv unterstützt werden.

Beispiele für Kubernetes-native API Gateways (Abb. 4) sind neben anderen Kong, Gravitee.io oder Apache APISIX. Was auffällt, ist: Softwarehersteller, die Cloud-native oder -agnostische API Gateways anbieten, bieten häufig auch Kubernetes-native Pendants an.

jm_2_25_jax_bernhardt_kubernetes_mn_4
Abb. 4: Kubernetes-natives API Gateway

API Gateway in Kubernetes – sinnvoll oder nicht?

Abschließend stellt sich die Frage: Ist ein API Gateway für Kubernetes-Applikationen notwendig? Die Antwort lautet: „Es kommt darauf an.“ Die wahre Stärke eines API Gateway zeigt sich besonders dann, wenn das API-Management strategisch angegangen wird und Sicherheit, Transparenz und Kontrolle gefragt sind.

Ein API Gateway kann Unternehmen helfen, die zunehmende Komplexität in heterogenen IT-Systemlandschaften, die sich zunehmend in Richtung Cloud beziehungsweise Multi-Cloud bewegen, zu bewältigen. Gerade in einer Zeit, in der Microservices das bevorzugte Stilmittel sind, wird es wichtiger, Überblick über vorhandene APIs und deren Nutzungsverhalten zu haben, Sicherheitsrichtlinien konsistent durchzusetzen und Kommunikationsverläufe sichtbar zu machen. Kubernetes ist die elementare Plattform, um solche verteilten Applikationsarchitekturen effizient zu betreiben und weiterzuentwickeln. Durch den zunehmenden Einsatz von KI und die Integration von Large Language Models (LLMs) in Applikationen erhöht sich stetig die Anzahl der zu managenden APIs. Das bedeutet, auch die Notwendigkeit für ein konsistentes Management wächst weiter.

Mein Plädoyer daher: Heute schon an morgen denken: Ein API Gateway in Kubernetes könnte der erste Schritt in Richtung eines umfassenden API-Managements sein – und langfristig die Basis für eine stabile und zukunftsfähige IT-Infrastruktur!

 

Stay tuned

Immer auf dem Laufenden bleiben! Alle News & Updates:

[mc4wp-simple-turnstile]

The post API Gateways in Kubernetes appeared first on MAD Summit.

]]>
DDD: Die Tücken der Terminologie https://mad-summit.de/blog/domain-driven-design/ddd-die-tuecken-der-terminologie/ Mon, 28 Oct 2024 11:30:55 +0000 https://mad-summit.de/?p=86411 Zwanzig Jahre nach der Veröffentlichung von Eric Evans Buch „Domain-Driven Design: Tackling Complexity in the Heart of Software“ ist Domain-Driven Design ein beliebter und bekannter Ansatz in der professionellen Softwareentwicklung. Trotzdem haben Teams in der Praxis oft mit der Komplexität von Sprache und Kommunikation zu kämpfen, und das von Evans vorgeschlagene Konzept der Ubiquitous Language ist nicht einfach in die Tat umzusetzen. Warum ist das so?

The post DDD: Die Tücken der Terminologie appeared first on MAD Summit.

]]>
Domain-Driven Design (DDD) ist ein methodischer Ansatz für das Softwaredesign, der das Ziel hat, die Komplexität der Software beherrschbar zu machen. Für die Softwareentwicklung spielen verschiedene Arten von Komplexität eine Rolle. DDD legt den Fokus vor allem auf Domain Complexity. Diese liegt in der Aufgabe oder dem fachlichen Problem begründet, das die Software lösen soll, und ist zum Beispiel von technischer Komplexität zu unterscheiden, die aus der Wahl der eingesetzten Technologien resultiert.

Zwei verschiedene Erkenntnispfade sind für DDD von zentraler Bedeutung. Der erste beruft sich auf die lang bestehende und oft bewährte Praxis, Modelle für praktische und wissenschaftliche Zwecke zu verwenden. So ist beispielsweise eine Karte ein Modell, das zur Navigation verwendet wird. Ein Modell hat erklärende und vorhersagende Funktionen und dient oft auch einem praktischen Zweck. Modelle lassen sich iterativ und inkrementell verbessern.

Der zweite ist ein Pfad der Reflexion über Sprache und Kommunikation und verbindet DDD mit Erkenntnissen zu praktischen Problemen der Kommunikation und des Sprachgebrauchs. Wir bewegen uns hier auf den Gebieten der Kommunikationspsychologie, Linguistik, Informationstheorie und Philosophie. Ein Ausgangspunkt ist die Idee, dass jede Kommunikation mit Transaktionskosten verbunden ist und eine gewisse Wahrscheinlichkeit hat, „noise“ oder Missverständnisse zu produzieren. Das Modell der vier Seiten einer Nachricht oder Sprachspiele wie „Stille Post“ erklären verschiedene Aspekte von Missverständnissen.

Wir haben es also inhaltlich mit zwei unterschiedlichen Denkansätzen zu tun, die Evans beide zu berücksichtigen versucht, wenn er DDD als effektiven Ansatz zur Bewältigung von Komplexität in der Softwareentwicklung vorschlägt. Beide sind in DDD eng miteinander verwoben. Evans legt allerdings den Schwerpunkt seines Buchs auf die Erstellung eines Modells der Domäne. Reflexion über Kommunikation und Sprache findet durch sein Konzept der Ubiquitous Language Eingang, spielt insgesamt aber eine untergeordnete Rolle.

Das Domänenmodell

Evans beginnt sein Buch mit einer Vision des Domänenmodells und dessen Bedeutung für das Softwaredesign. Seine Definition des Modells lautet: „A rigorously organized and selective abstraction of expert knowledge“ [1]. Dessen Bedeutung fasst er mit mehreren Kernaussagen zusammen:

  • „The model and the heart of design shape each other.“

  • „The model is the backbone of a language used by all team members.“

  • „The model is distilled knowledge.“

  • „A domain model can be the core of a common language for a software project.“ [1]

Im Fall einer komplexen Domäne soll das Modell helfen, das Design der Software zu verbessern und die Komplexität der Domäne beherrschbar zu machen. Das Softwaredesign soll im Zusammenspiel mit einem Modell stattfinden. Die Kommunikation im Designprozess soll sich auf das Modell berufen können. Das Modell soll eine effektive Kommunikation unterstützen und es ermöglichen, eine gemeinsame Sprache zu finden.

 

Schaffe Struktur, ermögliche den Wandel!

Power-Workshops zu Software Design & Patterns (22. - 26. Juni 2026, München)

 

Das Argument der Sprachbarrieren

Um für die Nutzung von DDD zu motivieren, argumentiert Evans in einem ersten Schritt, dass angesichts einer komplexen Fachlichkeit Sprachbarrieren im Team vorprogrammiert seien. Sein Argument des „Linguistic Divide“ lässt sich kurz so zusammenfassen: Softwareentwicklung ist ein komplexer Prozess, an dem verschiedene Experten einer Organisation beteiligt sind. Der Erfolg hängt in hohem Maß von einer effektiven Zusammenarbeit ab. Doch leider gibt es in vielen Organisationen Sprachbarrieren, die eine effektive Zusammenarbeit gefährden, da sie das gegenseitige Verständnis erschweren.

In einer Organisation findet Arbeitsteilung statt. In der Regel gibt es eine Aufteilung in zwei Gruppen von Spezialisten, nämlich in Experten mit Fachwissen auf der einen und Experten mit technischem Wissen auf der anderen Seite. Spezialwissen geht oft mit Fachausdrücken und einer besonderen Terminologie einher. Menschen aus anderen Gruppen sind mit diesen In-Group-Dialekten nicht vertraut. Infolgedessen besteht zwischen diesen Gruppen eine Sprachbarriere, die viel Raum für Missverständnisse lässt.

Argumente gegen Übersetzungen

Ein zweiter Schritt in Evans’ Argumentation stützt sich auf ein Konzept von Übersetzung, das eine Kommunikationsform zwischen Experten beschreiben soll. Es dient der Unterscheidung zwischen zwei Möglichkeiten, wie Experten in einer Organisation kommunizieren können:

  • per Übersetzung zwischen ihren fragmentierten, spezialisierten Fachsprachen oder

  • in einer gemeinsamen Sprache, die sie zusammen etablieren und praktizieren

Diese beiden Formen der Kommunikation hält Evans nicht für gleich effektiv, um ein gegenseitiges Verständnis zu erzielen.

Typische Beispiele für Übersetzungen im Alltagsverständnis sind die literarischer Texte oder die Arbeit von Dolmetschern und Übersetzungen in Untertiteln. Nehmen wir das Beispiel einer deutschen Übersetzung von „À la recherche du temps perdu“ von Marcel Proust. Da ich der französischen Sprache nicht mächtig genug bin, kann ich den von Proust selbst verfassten französischen Originaltext nicht lesen. Ich kann beschließen, eine Übersetzung des Buches zu kaufen, die von einem Literaturübersetzer angefertigt wurde, der über die entsprechende sprachliche Kompetenz verfügt. Oder ich kann beschließen, mein Französisch zu vertiefen. Aber ich kann nicht davon ausgehen, von heute auf morgen Proust im französischen Wortlaut selbst lesen zu können. Eine gute Übersetzung, die dem ursprünglichen Sinn des Textes so nahe zu kommen versucht wie nur möglich, hilft mir, den Originaltext sinngemäß zu verstehen. Doch die Lektüre von Übersetzungen wird mich nicht mit der Zeit befähigen, die von Proust selbst verfassten französischen Originaltexte zu lesen und zu verstehen. Und klar, wir alle kennen schlechte Übersetzungen in Untertiteln von Filmen und Serien.

Auch Experten, die sich einer Sprachbarriere gegenübersehen, werden laut Evans versuchen, einander zu „übersetzen“. Ein Spezialist sagt etwas. Der andere versucht, zu verstehen, was der Spezialist gesagt hat, indem er den Wortbeitrag für sich „übersetzt“. Der Austausch basiert jedoch gerade nicht auf der sprachlichen Kompetenz des Übersetzers, sondern ist in hohem Maße auf Annahmen, Rückfragen und raten angewiesen. Für eine gute Übersetzung fehlt den Beteiligten wichtiges Hintergrundwissen, das sich nur schwer explizieren lässt. Niemand hat eine klare Vorstellung davon, was kommuniziert werden muss, um ein gegenseitiges Verständnis zu erreichen. Überall lauern „unknown Unknowns“.

Die Beispiele für Übersetzung, die zu unserem Alltagsbegriffs passen, halten wir insgesamt für unproblematisch. Im Großen und Ganzen können wir auf die sprachliche Kompetenz von professionellen Übersetzern und Dolmetschern vertrauen. Schlechte Übersetzungen von Untertiteln stören uns hin und wieder vielleicht ein wenig, aber viele Leute finden sie auch amüsant. Exakte Übersetzungen sind nicht immer möglich, aber bei der alltäglichen Kommunikation kommt es nicht auf eine große Genauigkeit an.

Im Gegensatz dazu stuft Evans die Übersetzungen zwischen Experten im Designprozess von Software als ein ernst zu nehmendes Problem ein. Solche Übersetzungen beruhen in der Regel nicht in gleichem Maße auf sprachlicher Kompetenz wie in den genannten Fällen. Gleichzeitig erfordert die Arbeit eine hohe Genauigkeit bei der Spezifikation. Mit Missverständnissen können Risiken und Kosten verbunden sein.

Im klassischen Ansatz wird versucht, dieses Problem zu lösen, indem kompetente Übersetzer gefunden werden. Business Analysts oder Requirements Engineers sollen zwischen den beiden Expertengruppen vermitteln. Evans hält diese Entscheidung für bedenklich. Weil es zu wenige fähige Übersetzer gebe, die sowohl das fachliche als auch das technische Wissen haben, sieht er hier den eklatanten Nachteil eines „Information Bottleneck“. Die Übersetzungen sind oft ungenau und ziehen deshalb Missverständnisse nach sich. Wie wir sehen werden, können sich Übersetzungsfehler sogar ansammeln, wenn im Prozess gezielt Übersetzer eingesetzt werden.

Evans führt seine Argumente nicht im Detail aus, was einigen Raum für Interpretationen offenlässt. Ein wesentlicher Aspekt scheint jedoch zu sein, dass Übersetzungen einer gemeinsamen Sprache und einem geteilten Verständnis von Fachlichkeit, Design und Software entgegenwirken.

 

 

Zum Beispiel schreibt er an einer Stelle von „translation muddles model concepts“ und etwas später: „The effort of translation prevents the interplay of knowledge and ideas that lead to deep model insights“ [1]. Zugleich aber bilden für ihn die Begriffe des Modells das Rückgrat einer gemeinsamen Sprache. Eine vollständige Version eines seiner Argumente könnte also lauten:

  1. „Translation muddles model concepts“,

  2. „Model concepts are the backbone of a common language“,

  3. [therefore, translation hinders the creation of a common language] (Rekonstruktion des Autors)

Doch wie lassen sich dann die Begriffe des Modells, die das Rückgrat einer gemeinsamen Sprache bilden, klären und definieren, wenn man „lost in translation“ ist und noch keine gemeinsame Sprache gefunden hat?

Eine andere Art der Übersetzung, auf die Evans nicht so stark eingeht, ist die von Spezifikationen in Quellcode. Der Gedanke an eine solche Übersetzung schwingt sicherlich in seiner Beobachtung mit, dass die „Terminology of day-to-day discussions is disconnected from the terminology embedded in the code“. Solange Spezifikation und Quellcode in verschiedenen Sprachen formuliert werden, braucht es eine Übersetzung. Gegen diese und ihre Fehleranfälligkeit kann Evans also nicht argumentieren. Sie lässt sich kaum eliminieren. Die Idee, dass der Quellcode die Terminologie und Konzepte der Fachsprache nutzen sollte, ist dabei logisch zu unterscheiden von der Idee, dass der Austausch auf Basis einer gemeinsamen Sprache geschehen sollte, deren Terminologie durch ein Modell der fachlichen Domäne geklärt wird.

Zwar argumentiert Evans gegen Übersetzungen von Experten, weil diese zu verworrenen Begrifflichkeiten beitrügen; doch Übersetzungen sind nicht der einzige Grund für Sprachverwirrung. Evans führt Vagheit als weiteren Grund dafür an. Vagheit stellt jedoch kein kommunikatives Versagen dar, sondern ist ein Merkmal natürlicher Sprachen.

Indirekte Kommunikation

In der Literatur zu DDD, aber auch in der Agile-Community finden sich Argumente gegen indirekte Kommunikation und es werden Praktiken favorisiert, die indirektem Austausch oder dessen Folgen entgegenwirken sollen („Let the developer talk with the user“, User Stories, Demo …). Indirekte Kommunikation kann durch die Komplexität der Domäne bedingt sein, weil nur viele Experten gemeinsam über das nötige Wissen zur Domäne verfügen. Sie verfestigt sich jedoch am stärksten auf der Organisationsebene. Ein Modell wird indirekte Kommunikation in Organisationen nicht beseitigen. Denn es ist leicht, ein theoretisches Modell der Domäne zu erstellen, das in der Kommunikation zwischen den Experten, mit den Stakeholdern und den Nutzern in der Praxis nicht genutzt wird.

Indirekte Kommunikation fördert Übersetzungen. Bestimmte Strukturen innerhalb von Organisationen fördern indirekte Kommunikation. In der Folge können sich Fehler in der Kommunikation leicht anhäufen. In einem Unternehmen könnten zum Beispiel Fachexperten mit Business Analysts, Business Analysts mit Softwarearchitekten und Softwarearchitekten mit Entwicklern kommunizieren. In einer solchen Umgebung gibt es drei Ebenen der Übersetzung a2bb2c und c2d wie Abbildung 1 zeigt (schwarze Pfeile). Nehmen wir an, die verschiedenen Ebenen sind stark voneinander getrennt und übersetzen unidirektional und unabhängig, und sie haben eine konstante Fehlerquote von 0,2 pro Ebene, d. h., in jeder fünften Transaktion tritt ein Fehler auf, z. B. ein Missverständnis, und feste, einheitliche Transaktionskosten pro Ebene c. In einem solchen Fall haben sie eine Gesamtfehlerquote von 1-0,83, also etwa 50 Prozent, und Gesamttransaktionskosten von 3c.

nitsch_ddd_1_1.tif_fmt1
Abb. 1: Fehlerquoten in Übersetzungen

Um die Gesamtfehlerrate zu verringern, könnte man eine Rückwärtskommunikation pro Schicht zulassen und damit die Transaktionskosten erhöhen (orange Pfeile). Nimmt man zum Beispiel an, dass zwei zusätzliche Transaktionen erforderlich sind, um die Fehlerrate zu halbieren, und führt daher einen Multiplikator von 3 pro Schicht für die Transaktionskosten c pro Schicht ein, erhält man 9c an Gesamttransaktionskosten für drei Schichten und die Gesamtfehlerquote wäre 1-0,93, also etwa 27 Prozent. Im Gegensatz dazu könnte man bei nur einer Übersetzungsschicht standardmäßig eine Fehlerquote von 0,2 und Kosten von c haben und eine Fehlerquote von nur 0,1 bei Kosten von 3c erreichen (blauer Pfeil).

Ob sich Kommunikationsfehler wirksam reduzieren lassen, hängt natürlich stark von der Natur der Fehlerraten und Transaktionskosten pro Ebene ab. Zum Beispiel könnte in einem Netzwerk mit mehreren Ebenen die Lösung einiger Missverständnisse eine rekursive Rückwärtsauflösung erfordern, sodass die Lösung eines Missverständnisses zwischen c2d von der Rückwärtskommunikation b2c und so weiter abhängt und somit zu einem größeren Anstieg der Transaktionskosten führen könnte. Andererseits mag eine direkte Kommunikation zum Beispiel aufgrund der Größe einer Organisation nicht immer praktikabel sein, sodass die Einführung mehrerer Ebenen erforderlich ist. Wenn jedoch die direkte Kommunikation eine Option ist und der Erwartungswert für die Fehlerquote und die Transaktionskosten für eine direkte Übersetzung a2d nicht höher ist als der Erwartungswert für die Gesamtfehlerquote und die Transaktionskosten für die ursprünglichen Schichten a2bb2cc2d, dann scheint die Eliminierung empfehlenswert zu sein.

Eine gemeinsame Sprache

Ein gewisses Maß an Übersetzung mag zu Beginn jeder Zusammenarbeit zwischen Spezialisten notwendig sein. Langfristig soll das Team jedoch nicht auf Basis von Übersetzungen kommunizieren, sondern eine gemeinsame Sprache etablieren und praktizieren.

Es gibt ein einfaches Argument, das wir zur Unterstützung seines Vorschlags anführen können. Wie wir aus praktischer Erfahrung wissen, ist die Wahrscheinlichkeit von Missverständnissen zwischen zwei Personen, die unterschiedliche Sprachen sprechen, größer als zwischen zwei Personen, die eine gemeinsame Sprache, z. B. Englisch, beherrschen. Das Gleiche dürfte auch für die Fachsprachen der Spezialisten gelten.

Doch was macht eine gemeinsame Sprache aus? Eine gemeinsame Sprache besteht aus gemeinsamen

  • Regeln,
  • Begriffen,
  • Bedeutungen,
  • Gegenständen/Artefakten.
nitsch_ddd_1_2
Abb. 2: Das Vokabular der Ubiquitous Language

Die gemeinsame Domänensprache wird von Evans Ubiquitous Language genannt. Diese Sprache sollte sowohl von Fachleuten als auch von technischen Experten verstanden werden. Das Vokabular der Ubiquitous Language soll die Terminologie des Domänenmodells enthalten. Namen von Klassen und prominenten Operationen, einige Patterns, Prinzipien der High-Level-Architektur sowie Schlüsselkonzepte der Domäne sollen ebenfalls enthalten sein [1]. Domänenkonzepte, die nicht ausreichend geklärt werden können oder die für den Zweck der Anwendung nicht relevant sind, sind nicht Teil der Sprache (Abb. 2).

Die Dinge werden jedoch komplizierter, wenn man beginnt, über die Details der Ubiquitous Language nachzudenken. Es folgt ein kurzer Ausblick auf zwei Probleme, die wir im nächsten Teil der Artikelserie näher beleuchten werden.

Allgegenwärtigkeit

Die gemeinsame Sprache soll im Team allgegenwärtig sein. Ein Problem, das sich aus der geforderten Allgegenwärtigkeit der Sprache ergibt, hat mit der Beziehung zwischen der Ubiquitous Language und dem Quellcode zu tun. Eine Domänensprache ist laut Evans nur dann allgegenwärtig, wenn sie auch in den Code eingebettet ist.

Es ist an sich einfach, Klassen auf der Grundlage der Domänensprache zu benennen und auf diese Weise Domänensprache in den Quellcode einzubetten. Die Einbettung garantiert jedoch nicht, dass die Domänensprache eine gemeinsame Sprache darstellt. Damit Klassennamen tatsächlich Teil einer gemeinsamen Sprache sein können und jeder effektiv über sie sprechen kann, müsste jeder wissen, auf welche Klassen sie sich beziehen. Aber Klassen finden sich nur im Quellcode. Fachexperten kennen in der Regel den Quellcode nicht. Sie werden in den meisten Fällen nicht wissen, worauf sich die „Namen von Klassen“ beziehen. Wenn man Fachexperten nicht beibringt, den Quellcode zu lesen, wie können dann Namen von Klassen Teil einer gemeinsamen Sprache sein?

Sprache-Modell-Bindung

Ein weiteres Problem ist, wie die Bindung von Sprache und Modell sichergestellt werden kann. Da sich Sprache und Modell weiterentwickeln können, ist im Einzelfall nicht klar, ob sie nicht Gefahr laufen, sich getrennt zu entwickeln. Da die Ubiquitous Language in den Quellcode eingebettet sein soll, muss insbesondere die Bindung zwischen dem Quellcode und dem Domänenmodell bestehen bleiben. Auch der Quellcode kann mit der Zeit größeren Veränderungen unterworfen sein.

Wie findet man heraus, ob Sprache und Modell noch zusammenpassen? Eine einfache Methode, das zu prüfen, wäre wünschenswert. Andernfalls wird man nicht in der Lage sein, eine Bindung langfristig aufrechtzuerhalten. Automatisierte Tests könnten ein Weg sein, eine Bindung zwischen Quellcode und Modell sicherzustellen. Die Tests prüfen die Kriterien für die Akzeptabilität des Quellcodes aus der Perspektive des Modells.

Das Idealbild von Evans ist die bidirektionale Bindung zwischen Domänenmodell und Quellcode. Änderungen am Quellcode sollen Änderungen am Modell nach sich ziehen können. Aus organisatorischer Sicht scheint das nur möglich zu sein, wenn der Softwareentwurfs- und -entwicklungsprozess nicht vollständig von oben nach unten diktiert wird, sondern Rückkopplungsschleifen zulässt.

Fazit

Domain-Driven Design ist also nicht auf die leichte Schulter zu nehmen. Es wird oft als eine Reihe von Methoden und Techniken für die Modellierung behandelt und damit betont, dass es sich um einen Ansatz der modellbasierten Softwareentwicklung handelt. Obwohl es nicht falsch ist, DDD auf diese Weise zu charakterisieren, ist die Vorstellung, es sei im Kern ein Ansatz der modellbasierten Softwareentwicklung, etwas unglücklich, da es der Problemdarstellung von Evans nicht gerecht wird.

Eine starke Fokussierung auf Modellierung, ohne über Sprache und Kommunikation zu reflektieren, überschätzt die Bedeutung von Modellen. Denn Modelle entstehen nicht aus dem Nichts. Ein Modell ist ein Artefakt, dessen Erstellung das Ergebnis einer gemeinsamen Anstrengung des Teams ist. Idealerweise beschreibt es einen gemeinsamen Wissensstand und hat eine klar definierte Terminologie. Sprache, Begriffe und Konzepte müssen also geklärt werden, um ein Modell überhaupt erstellen zu können. Modelle können zwar helfen, die Komplexität der realen Welt zu bewältigen, aber ihre Verwendung trägt nicht per se dazu bei, die Komplexität von Sprache und Kommunikation zu bewältigen.

DDD ist kein Selbstläufer und erfordert eine intensive Auseinandersetzung mit der Methode, um sie zu erlernen und zu erkennen, wo die Fallstricke bei der Umsetzung liegen. Mit einem Workshop zu Domain Storytelling und Event Storming wird es jedenfalls nicht getan sein.

Angesichts der Bekanntheit und Popularität von DDD rate ich daher zu nüchterner Vorsicht. DDD kann eine gute Wahl sein, wenn die Fachlichkeit recht komplex ist. Regelmäßige Kommunikation und kontinuierliches Lernen sind in einem solchen Fall möglicherweise nicht effektiv genug, wenn dem Team ein gemeinsames, tieferes fachliches und methodisches Verständnis fehlt. Die Verwendung des DDD-Ansatzes könnte dann hilfreich sein, um Fachlichkeit und Methodik zu klären. Wenn die Komplexität der Fachlichkeit jedoch gering ist, ist der DDD-Ansatz aufgrund seines eher hohen Aufwands wahrscheinlich keine gute Wahl. Es ist also in jedem Fall wichtig, zunächst zu einer Einschätzung der fachlichen Komplexität zu kommen und dann reflektiert zu entscheiden, ob sich der Ansatz lohnt.

 

Stay tuned

Immer auf dem Laufenden bleiben! Alle News & Updates:

[mc4wp-simple-turnstile]

 

 

The post DDD: Die Tücken der Terminologie appeared first on MAD Summit.

]]>
21 Jahre Domain-Driven Design https://mad-summit.de/blog/domain-driven-design/21-jahre-domain-driven-design/ Tue, 03 Sep 2024 14:53:10 +0000 https://mad-summit.de/?p=86298 21 Jahre sind vergangenen, seit Eric Evans 2003 den Begriff Domain-Driven Design aus der Taufe gehoben hat – das ist doch mal ein Grund zum Feiern! Zum Feiern und zu einer (mehr oder weniger) kleinen Retrospektive und Bestandsaufnahme. Was böte sich da mehr an, als Expertinnen und Experten zu Wort kommen zu lassen, die sich intensiv mit der Thematik beschäftigt und jede Menge aus Theorie und Praxis zu berichten haben? Wir haben – da 21 Experten den Rahmen vielleicht doch ein wenig gesprengt hätten – mit neun Fachleuten gesprochen, die uns ihre Sicht auf das Thema DDD darstellen: 21 Jahre DDD – 9 Expertinnen und Experten!

The post 21 Jahre Domain-Driven Design appeared first on MAD Summit.

]]>

Java Magazin: 21 Jahre DDD, Zeit für ein zwischenzeitliches Resümee: Ist DDD in der „breiten Masse“ angekommen, oder bedarf es da noch des einen oder anderen Wegweisers?

Tobias Goeschel: Diese Frage ist nicht ganz einfach zu beantworten. Ganz sicher in der breiten Masse angekommen ist die Information, dass DDD bei der Planung und Umsetzung von Geschäftssoftwareprojekten hilfreich und für Microservices-Architekturen nahezu unverzichtbar ist. Also, das Bewusstsein ist da. Aber bei der Umsetzung gibt es noch viel Nachholbedarf: Derzeit sehe ich den weitaus größten Anteil der Unternehmen, gerade im Enterprise-Umfeld, entweder im Zustand „Das sollten wir unbedingt mal machen“ oder davon überzeugt, dass ein einzelner Workshop mit Erstellung eines Modells bereits einen fertigen, belastbaren Architekturentwurf liefert. Dass effektives DDD eine Umstellung des gesamten Vorgehens bei der Softwareentwicklung bedeutet, mit iterativer, interdisziplinärer Zusammenarbeit zwischen Entwicklungsteam und Fachbereich, ist für viele nicht greifbar.

Wenn DDD wirklich gut gemacht wird, werden Architekturentscheidungen häufig hinterfragt, schwierige Kontexte immer wieder neu diskutiert und durchaus auch mal ganze Module komplett neu programmiert. So etwas ist in klassischen Unternehmen politisch oft schwierig durchzusetzen oder wird durch gelebte Firmenkultur verhindert. Hinzu kommt, dass für die erfolgreiche Entwicklung mit DDD eine Vielzahl von anderen Skills, z. B. aus den Bereichen Software Crafting oder DevOps, benötigt werden. Die Einstiegshürde ist daher immer noch vergleichsweise hoch.

Am Ende also: jein. Wir sind noch lange nicht am Ende der Reise angekommen.

Stefan Hildebrandt: Es gibt mittlerweile recht viele Unternehmen, die auf DDD setzen, aber auch noch viele, die es nicht nutzen, und andere, die das Thema bisher noch nicht als Option für sich wahrgenommen haben. Auch die Arten der Adaptierung sind sehr unterschiedlich: Einige schneiden Systeme, Microservices oder Module nach Bounded Contexts, die im Vorfeld sauber erarbeitet wurden, verwenden nur noch Value-Objekte und leben ihre Fachlichkeit in ihrem System und ziehen großen Nutzen aus DDD. Andere packen Klassen aus unterschiedlichen Bereichen in Pakete mit den Namen domainports und adapter, nutzen nur Strings an allen externen Schnittstellen und nennen das dann hexagonale Architektur. Das ist dann vermutlich ein Rückschritt zum vorherigen Vorgehen. Hier ist sicher noch viel Aufklärungsarbeit und noch mehr praktische Erfahrung notwendig, damit alle einen Nutzen aus den Konzepten und dem bedarfsgerechten Einsatz der Werkzeuge von DDD ziehen können.

Stefan Hofer: Jedenfalls ist DDD über seinen ursprünglichen Anwendungsbereich hinausgewachsen: Es geht nicht mehr nur um Geschäftsanwendungen, die eine Organisation für sich selbst entwickelt. Mittlerweile wird DDD in der Entwicklung von Softwareprodukten für Unternehmen und private Nutzer eingesetzt. In diesen Bereichen werden wir noch viel Weiterentwicklung sehen. An (Fach-)Hochschulen ist DDD ebenfalls angekommen, das freut mich besonders.

Jörn Koch: DDD ist als Thema in den Köpfen der breiten Masse angekommen und wird nach meinem Eindruck vor allem als die Möglichkeit gesehen, Monolithen (aka „Big Ball of Mud“) zu vermeiden und stattdessen eine Landschaft von Standalone Systems aufzubauen.

Allerdings ist die breite Masse (noch) nicht in der Lage, DDD erfolgreich einzusetzen. Einige Missverständnisse, die das verhindern, sind typisch, z. B. dass eine dezentrale Datenhaltung nach DDD zu massiver Datenredundanz und zur Explosion der Datenmengen führen müsse. Solche Missverständnisse führen leider oft dazu, dass DDD nur inkonsequent eingesetzt wird (z. B. in Kombination mit einer zentralen Datenhaltung) und die mit DDD verknüpften Zielvorstellungen (z. B. zu einem guten Schnitt von Standalone Systems zu kommen) nicht erfüllt werden.

Neben den reinen DDD-Konzepten sind aus meiner Sicht pragmatische Methoden notwendig, die schrittweise zu DDD-konformen Ergebnissen hinleiten – trotz eines noch nicht „DDD-kompatiblen“ Mindsets bei den Beteiligten. Ein Beispiel sind szenariobasierte Methoden, in denen anhand von Beispielprozessen die fachlichen Grundlagen für einen DDD-Entwurf ermittelt werden.

Carola Lilienthal: Mein Eindruck ist, dass DDD in der breiten Masse als mögliche sinnvolle Methode angekommen ist. Das mache ich daran fest, dass es inzwischen auf allen Softwareentwicklungskonferenzen Vorträge zu DDD gibt. Wer sich also ein bisschen in unserer Disziplin umschaut, kommt am DDD nicht mehr vorbei. Der Einsatz von DDD in Projekten ist im Gegensatz dazu noch nicht wirklich verbreitet. Das liegt sicherlich daran, dass es schon sehr viel Software gibt, die anders entwickelt wurde, und auch daran, dass DDD bisher an den wenigsten Unis gelehrt wird. Es gibt also noch viel zu tun, um DDD für alle Softwareentwicklungsteams zugänglich zu machen.

Michael Plöd: Ich tue mich immer schwer, „die breite Masse“ zu definieren. Dafür ist die IT-Landschaft schlichtweg zu heterogen. Allerdings denke ich, dass Domain-Driven Design inzwischen ein Momentum erreicht hat, das definitiv nennenswert ist und das nun auch seit vielen Jahren eine gewisse Konstanz aufweist. Allerdings beobachte ich, dass Domain-Driven Design primär im Bereich der Softwareentwicklung und -architektur bekannt ist. Leider ist DDD in vielen Fachbereichen und bei zahlreichen Domain Experts noch kaum bis gar nicht bekannt. Hier denke ich, dass wir als Community ansetzen sollten um das Thema verstärkt in Richtung des Business zu tragen.

Nicole Rauch: Das hängt natürlich sehr davon ab, was man unter „angekommen“ versteht. Hat es die breite Masse schon mal gehört? Ja, definitiv. Weiß die Mehrheit, was das im Großen und Ganzen bedeutet? Da muss man vielleicht schon differenzieren. Ich glaube, dass viele ein gutes Verständnis der grundlegenden taktischen Patterns wie Entities und Value Objects haben oder auch, dass man seine Applikationen fachlich schneiden sollte, aber wenn es dann mehr in Richtung der konkreten Umsetzung dieser vielleicht etwas diffusen Vorstellungen geht, zum Beispiel dass die Domäne technikfrei sein muss (und dass das z. B. auch technische Annotationen betrifft) oder dass man die Domänensprache bis hinunter zur Datenbank benutzen muss (und ja, auch wenn es deutsche Begriffe sind!), da kann man schon manchmal in verwunderte Gesichter blicken.

Wenn man dann darauf schaut, wer DDD tatsächlich in der täglichen Arbeit einsetzt, wird es wirklich dünn.

Von daher würde ich sagen, die Theorie ist so weit schon einigermaßen angekommen, aber die praktische Anwendung bei weitem noch nicht.

Henning Schwentner: So dazwischen. Mittlerweile haben die meisten Entwicklerinnen und Entwickler die Abkürzung DDD zumindest schon mal gehört (auch dank des Microservices-Hypes); viele haben sich auch in der Theorie damit beschäftigt. In der Praxis wird DDD aber leider noch weniger breit eingesetzt, als das sinnvoll wäre. Oft höre ich da das Argument: „Das sind ja alles tolle Ideen, die beim Bau einer Software auf der grünen Wiese gut funktionieren, aber wir haben hier ja schon ein Altsystem.“ Als Wegweiser empfehle ich in solchen Fällen das gerade erscheinende Buch „Domain-Driven Transformation“ [1] von Carola Lilienthal und mir, das zeigt, wie man DDD auch nachträglich in ein existierendes Legacy-System einführt und dieses dadurch wieder handhabbar macht.

Eberhard Wolff: DDD ist schon lange in der breiten Masse angekommen – aber leider werden die meisten Ideen, wenn sie populär werden, verwässert oder falsch verstanden. Das hat es bei DDD gleich mehrfach gegeben: Am Anfang wurde DDD als ein Ansatz wahrgenommen, um objektorientierte Systeme auf Klasseneben zu designen. Mittlerweile ist den meisten klar, dass DDD auch Ansätze enthält, um eine grobgranulare Architektur zu definieren. Aber es gibt immer noch Missverständnisse: Zu oft wird ein klassisches Design genommen und die Elemente werden zu Bounded Context umdeklariert. So ändern sich zwar die Bezeichnungen, aber inhaltlich bleibt alles beim Alten.

JM: Hast du eine lustige oder interessante DDD-Geschichte, die du gerne mit der Community teilen willst?

Goeschel: Vor einigen Jahren habe ich in Helsinki für eine gemischte Gruppe aus 20 Personen ein Event-Storming-Training gehalten, das ich zuvor bereits etliche Male in Deutschland mit Kund:innen durchgeführt und eingespielt hatte. In meiner Beispieldomäne, einem Kinobetrieb mit Einkauf, Programmplanung, Filmauswahl und Ticketing, gab es einen Teilprozess, der das Kinoticket per E-Mail verschicken sollte, nachdem die Buchung angewiesen, aber bevor die Zahlung tatsächlich eingegangen war – ein Verfahren, das bei uns durchaus üblich ist; in der Regel gibt es ein Mahnwesen und Inkasso, das Säumnisse im Nachgang ahndet. In Finnland löste diese Idee ein wahre Empörungswelle aus: Ich bekam erklärt, dass die finnische Seele zwar freundlich und offen, aber „von Grund auf misstrauisch“ sei. So gäbe es keinesfalls – jemals – eine Transaktion, bei der die Ware vor dem Erhalt des Geldes ausgehändigt würde. Ebenso schwierig war es übrigens für mich, zu akzeptieren, dass mein eigenes Bild von der Domäne nicht korrekt war, was eine ganze Reihe von Änderungen für das Training nach sich zog.

Am Ende war es aber eine sehr erhellende Erfahrung: Nicht nur das reine Geschäft, sondern auch Landeskultur, Bräuche, Religion und ähnliche Einflüsse haben großen Anteil daran, wie Geschäftsprozesse modelliert und am Ende in Software gegossen werden. Auch deswegen empfiehlt es sich sogar für „altbekannte“ Domänen, die Vorstellungen in den Köpfen sichtbar zu machen und zu prüfen, ob sich die Annahmen der Beteiligten überschneiden.

Ironischerweise gab es übrigens beim anschließenden Besuch in der Bar um die Ecke dann auch praktisches Anschauungsmaterial: Jedes Getränk musste sofort und in bar bezahlt werden – „Deckel schreiben“ ist in (zumindest diesem Teil von) Finnland folgerichtig ein undenkbares Konzept.

Schaffe Struktur, ermögliche den Wandel!

Power-Workshops zu Software Design & Patterns (22. - 26. Juni 2026, München)

Hildebrandt: Auch bei DDD kann man in die „Big Design Up Front“-Falle tappen! Ich habe beobachtet, wie ein Projektteam über einige Monate ein sehr umfassendes und detailliertes Modell der Domänen und Prozesse des Kunden erstellt hat. Das Vorgehen war dem Team nicht vorgegeben und die Option DDD war besprochen, aber vom Kunden nicht explizit beauftragt worden und die Modellierung ging weit über den Kontext des zu entwickelnden Systems hinaus, was den Beteiligten erst viel später klar wurde. Hier wäre ein etwas agileres und iteratives Vorgehen sinnvoll gewesen, um nicht nach wenigen Wochen in einer total gestressten Situation zu landen, aus der man sich erst nach Jahren wieder befreien konnte.

Hofer: Mich begeistern die „Aha-Momente“ in Collaborative-Modeling-Workshops (Event Storming, Domain Storytelling). Einmal meinte eine Teilnehmerin: „Ich arbeite seit 30 Jahren in der Abteilung, aber so habe ich noch nie über unsere Prozesse nachgedacht!“

Koch: In einem Projekt bei einem Logistiker warnten die Fachexperten vor komplexen „Kundenaufträgen aus der Hölle“, die teilweise aufgeteilt werden mussten, um mit der Auftragserfassung des Altsystems überhaupt erfasst werden zu können – was wiederum ihre Abwicklung und Abrechnung erschwerte. Nachdem wir zwei der kompliziertesten dieser Kundenaufträge genauer analysiert und nach DDD modelliert hatten, hatten alle diese „Höllenaufträge“ jeden Schrecken verloren: Dabei hatten wir nach DDD lediglich die Fachsprache softwaretechnisch nachmodelliert. Das war bereits die ganze Magie – und wir konnten viel schneller zum nächsten Thema übergehen als gedacht.

Lilienthal: Den größten Schock erleben Entwicklungsteams beim Umstieg auf DDD, wenn ihnen klar wird, dass sie sich vom Prinzip der Wiederverwendbarkeit verabschieden müssen. Bei DDD baut man kontextspezifische Domänenmodelle, die keine Klassen aus anderen Domänenmodellen wiederverwenden, nur weil dort schon eine Klasse Kunde oder Produkt oder Vertrag existiert. Die großen Augen mit vielen Fragezeichen zu redundanter Datenhaltung und Konsistenz und „Wie soll das denn gehen?“ sind immer wieder eine große Herausforderung in der DDD-Beratung und nach einiger Zeit lachen wir (Berater:innen und Kund:innen) dann gemeinsam über diese aufregende Anfangszeit.

Plöd: Mit einer „lustigen“ Geschichte kann ich nicht dienen, eher mit einer Geschichte, die eine traurige Ausgangssituation hatte: Fachbereich und Entwickler weigerten sich, direkt miteinander zu kommunizieren. Mit viel Mühe gelang es mir, beide Seiten zu einem gemeinsamen Event Storming in einen Raum zu bringen. Der Workshop begann damit, dass die Bereichsleiterin der Fachseite meinte „Ist das wieder so eine agile Post-it-Kleberei?“. Während des Workshops entstand mühsam, aber langsam ein spannender Kommunikationsfluss zwischen den beiden Abteilungen und man hörte immer häufiger Sätze wie „Was, das braucht ihr gar nicht?“ oder wie „Also, das können wir euch schnell bereitstellen“. In der Retrospektive meinte die Fachseite, dass sie das erste Mal das Gefühl hatten, von den Entwicklern wirklich gehört zu werden … Zwei Jahre später wollte die Fachabteilung unbedingt in das gleiche Großraumbüro wie das Entwicklungsteam ziehen. Direkte Kommunikation ohne Barrieren ist ungemein wichtig und sorgt für bessere Software.

Rauch: Als ich vor langer Zeit zum ersten Mal von DDD gehört hatte, war ich sehr begeistert und konnte auch meine Teamkollegen damit anstecken. Wir waren gerade dabei, eine große Altanwendung um ein kleines neues Modul zu ergänzen und haben im Zuge dessen beschlossen, DDD einmal auszuprobieren. Das lief auch wunderbar, der technikfreie Domänencode gefiel uns allen sehr gut, und durch die Verwendung von Repositories konnten wir die Tests auch sehr schön als kleine handliche Unit-Tests gestalten – die gewünschten Testdaten wurden durch verschiedene konkrete Testimplementierungen des Repository-Interface zugefüttert und fertig. Wir benötigten keine Datenbank mehr und konnten die Testlaufzeiten minimal halten – das war ein Novum für das Unternehmen, in dem die Ausführung aller Tests einer Applikation ansonsten oft Stunden dauerte. Wir hatten es auch geschafft, Specification by Example einzuführen, und die Fachabteilung war tatsächlich mit an Bord und erstellte fachliche Tests für uns – wir fühlten uns wie auf einer Wolke des Glücks. Leider hatten wir dabei völlig außer Acht gelassen, dass die Echtdaten, mit denen unser neuer Applikationsteil zu tun haben würde, deutlich komplizierter waren, als wir uns das bei der Implementierung unserer Test-Repositories so vorstellten. Und so schepperte es erstmal ganz schön, als wir die Applikation zum ersten Mal an eine Datenbank anbanden. Eigentlich kein Problem, wenn die Zeit bis zum geplanten Release nicht so unglaublich knapp gewesen wäre …

Es ist damals noch alles gut gegangen, aber daraus habe ich gelernt, dass es zwar sehr schön und hilfreich ist, dass man das Domain Layer für sich allein entwickeln kann, dass man aber trotzdem das drumherum befindliche Application Layer und auch die realen Daten nicht vergessen darf und den Domänencode früh und oft mit der realen Außenwelt integrieren muss.

Schwentner: Die DDD-Community ist ein Haufen von sehr freundlichen, offenen und lernbegierigen Leuten, von denen einige einen ziemlich verschrobenen Humor haben. Sucht in den Sozialen Netzen nach „DDD Borat“ und Ihr werdet sehen, was ich meine …

Wolff: Ich finde Eric Evans, den Autor des ursprünglichen DDD-Buchs, sehr beeindruckend. Ich hatte vor ca. 15 Jahren das Vergnügen, an einem Workshop mit ihm teilzunehmen und er begann mit Strategic Design – damals gingen mir gleich mehrere Lichter auf. Ebenso ist sein Paper zu „Getting Started with DDD when Surrounded by Legacy Systems“ [2] sehr beeindruckend. Beim Lesen habe ich mich mehrfach beim intensiven Kopfnicken wiedergefunden. Auch heute verkauft sich sein DDD-Buch sehr gut. Es ist seit 21 Jahren immer noch in der ersten Edition und hat eine ganze Designschule begründet. Das ist ein krasser Erfolg. Und dazu ist Eric nicht nur ausgesprochen intelligent, sondern auch eine sehr nette Person und hat andere zum Mitmachen bei DDD eingeladen – das finde ich super und darum ist das Thema heute so populär.

JM: Wo Licht ist, ist (wohl) auch Schatten: Kannst du auch über negative Erfahrungen mit DDD berichten?

Goeschel: Ich hatte es ja eingangs schon gesagt: In manchen Umfeldern ist DDD politisch brisant und deshalb gibt es durchaus manchmal Reibungsverluste. Mir fällt spontan ein Event-Storming-Workshop ein, in dem keine der 15 teilnehmenden Personen bereit war, auch nur einen einzigen Klebezettel zu beschriften – aus Angst, es könnte hinterher möglicherweise Ärger geben, wenn etwas falsch aufgeschrieben würde. Damit ist natürlich der Sinn und Zweck der Übung hinfällig. Gelöst habe ich das übrigens, indem ich alle Zettel selbst beschrieben und mir die Inhalte soufflieren lassen habe. Ein ziemlich unschönes Erlebnis, auch wenn am Ende ein Modell entstand, mit dem weitergearbeitet werden konnte.

Hildebrandt: Oft ist es in der IT über viele Teams schon schwierig, die gleichen Dinge gleich zu benennen, aber eine größere Herausforderung stellt die Verwendung einer gemeinsamen Sprache (Ubiquitous Language) in allen Fachbereichen und bei anderen nicht direkt an der Umsetzung Beteiligten in der Kommunikation zur Software dar, da viele Abteilungen ihren eigenen „Dialekt“ haben oder nicht täglich in der Sprache unterwegs sind. Hier sollten wenigstens die direkt Beteiligten überzeugt werden, damit Absprachen, Oberflächen und Dokumente konsistent sind. In der Folge kann es dann auch zu größeren Refactorings kommen, wenn sich der Sprachgebrauch im Unternehmen ändert, z. B. eine Onlinebestellung mit dem Einkauf vor Ort verschmolzen und in der Folge nur noch von Einkauf gesprochen wird. Das in der Domäne nachzuziehen, kann schon einen größeren Aufwand nach sich ziehen, bis hin zu Downtimes für DB-Migrationen.

Ein Punkt, der mir häufig auffällt, ist, dass dogmatisch eine der Standardarchitekturen (Hexagonal, Onion oder Clean) im Unternehmen ausgewählt und in der vollen Ausprägung verwendet wird, die im Kontext der konkreten Applikation aber keinen Nutzen bringt, da z. B. kaum Fachlogik in den Entitäten der Domäne existiert, sondern sich das Ganze in dem komplexen Mapping in Adaptern abspielt und nur zwei Kommunikationspartner existieren. In einem solchen Fall werden dann die Daten beim Eingang ins Domänenmodell transformiert, für die Persistenz in das DB-Model, dann wieder zurück ins Domänenmodell und schlussendlich ins Ausgangsmodell. In Summe muss dort viel Code erstellt und gewartet werden. Außerdem müssen – auch wenn in den Architekturstilen Tests einfach zu erstellen sind – viel mehr Tests erstellt werden. Auch bei der Massendatenverarbeitung kann es zu massivem Aufwand bei der Laufzeit kommen, was dann Aufwand bei der Implementierung nach sich zieht. So werden aus kleinen, kompakten Systemen richtig aufwendig zu pflegende Systeme.

Hier wäre es hilfreich, die Elemente der Architekturstile zu kennen und diese bei Bedarf einzusetzen, wie es in anderen Kontexten üblich ist. Ein stark typisiertes Modell („Ein Name ist ein Name und kein String“) ist im Quellcode – wenigstens für Java-Entwickler – immer eine gute Wahl! Ports und Adapter sind bei „externen“ Schnittstellen auch eine wichtige Option, bei „internen“ Schnittstellen – innerhalb eines SCS ggf. auch zum Frontend – erzeugen sie möglicherweise mehr Aufwand, machen den Code weniger lesbar und fehleranfälliger. Daher sollten sie nicht ohne Grund eingesetzt werden. Das Gleiche gilt für die Entkopplung der Datenhaltung über ein eigenes Modell mit Mapping. Dieses ist bei viel Fachlogik in den Domänenklassen für eine technologiefreie Modellierung und Tests sehr hilfreich, weniger komplexe Domänenmodelle lassen sich aber auch mit JPA inkl. guter Typisierung erstellen und bei Tests gut handhaben.

Am Ende ist es wichtiger, die Fachlichkeit verstanden und gut umgesetzt zu haben, sodass Anwender des Systems zufrieden sind und Anforderungen in einer guten Qualität und verlässlicher Geschwindigkeit für den Auftraggeber planbar umgesetzt werden können, als sich an alle Elemente in einer Beispielarchitektur zu halten. Dafür ist der fachliche Teil wichtig, aber auch ein inkrementeller Ansatz, da auch bei DDD das Lernen nicht vor der Umsetzung abgeschlossen ist.

Hofer: Wenn die Voraussetzungen nicht stimmen, bringt die beste Methodik nichts: Fachleute, die keine Zeit für Collaborative-Modeling-Workshops eingeräumt bekommen, strategisches Design mit der Vorgabe, keinesfalls die bestehende Aufgabenverteilung der Teams zu ändern oder eine Ubiquitous Language, die nicht vom Datenmodell der IT-Unternehmensarchitektur abweichen darf.

Koch: Viele Teams und Fachexperten tun sich schwer, in separaten Subdomänen zu denken oder Fachliches von Technischem zu trennen – es wird oft alles als ein großes Ganzes gesehen. DDD erscheint dann paradoxerweise kompliziert und die Fachlichkeit zu komplex.

Da DDD auf einem guten Verständnis der Fachlichkeit basiert, wird häufig zu viel analysiert, bevor überhaupt mit der Umsetzung begonnen wird. Das ist dann sehr un-agil.

In manchen Projekten existieren sehr konkrete Vorstellungen davon, wie das Zielsystem auszusehen hat (Architektur, UI, …). Der Blick auf die Fachdomäne fühlt sich dann manchmal „abstrakt“ an (weit weg von der Software) und wie ein Schritt zurück.

Einige DDD-Konzepte sind auch nach 21 Jahren noch immer schwer greifbar (z. B. die „Subdomäne“). Es fehlt eine gute, allgemein anerkannte Definition, stattdessen existieren unterschiedliche Interpretationen nebeneinander, die eine Diskussion über DDD erschweren.

Lilienthal: Einmal wurde ich von einem Kunden zu einem DDD-Workshop gebeten, weil die vorhandene IT-Landschaft zu Microservices umgebaut werden sollte. Als ich dort war, zeigte mir der externe Berater die IT-Landschaft und sagte: „Sehen Sie mal: Hier ist der Kunde und da ist der Kunde – eigentlich überall und wir haben dadurch ganz viele Doubletten. Das wollen wir mit Microservices zusammenlegen, sodass der Kunde nur noch einmal existiert und im ganzen System immer konsistent ist!“ Nun war ich echt in einer Zwickmühle: Einerseits wollte ich den externen Berater, der eine Service-orientierte Architektur mit einem Kundenservice als Microservices nach DDD verkauft hatte, nicht blamieren. Andererseits musste ich dem Kunden reinen Wein einschenken, dass seine IT-Landschaft eigentlich genau das tut, was DDD will: Lokale Domänenmodelle pro Bounded Context und deshalb auch den Kunden mehrfach in der IT-Landschaft jeweils spezifisch zu dem Kontext, wo er gebraucht wird. Ich habe dann vorsichtig Hinweise gegeben, bis dem externen Berater ein Licht aufging und wir haben gemeinsam die Kurve gekriegt. Zum Glück …

Plöd: Negative Erfahrungen entstehen immer dann, wenn bestimmte Methodiken oder Patterns aus dem DDD-Umfeld primär aus reinem Selbstzweck eingesetzt werden, damit man „DDD-kompatibel“ (ein fürchterlicher Begriff!!) ist. Man findet das ganz häufig im Umfeld des Tactical Designs, wo dann z. B. durch hexagonale Architekturen ein Overkill an Stellen entsteht, wo das Architekturmuster keinen Mehrwert liefert. Gleiches gilt für den Einsatz der Methoden der kollaborativen Modellierung, wenn strikt vorgeschrieben ist, welche Methodik wann genau zum Einsatz kommen muss. Ich denke, das sollten die Teams für sich entscheiden und bewerten. DDD ist keine Religion und das Buch von Eric Evans ist, auch wenn es noch heute absolut lesenswert ist, nicht die heilige Bibel.

Rauch: Als negative Erfahrung mit DDD selbst würde ich es jetzt nicht unbedingt bezeichnen, aber ich denke, dass es wichtig ist, sich darüber klar zu sein, dass der Einsatz von DDD mit erheblichem Aufwand und damit auch mit Kosten verbunden ist. Es ist z. B. wichtig, den Code immer wieder an neue Erkenntnisse anzupassen, und sei es auch nur bei so „trivialen“ Dingen wie dem Finden eines besseren Begriffs für einen bestimmten Sachverhalt. Hier ist es erforderlich, nicht nur das Glossar der Ubiquitous Language, sondern auch den gesamten Code bis hinunter zur Datenbank anzupassen. Auch neue Erkenntnisse im Hinblick auf die Modellierung einer bestimmten Fachlichkeit, die sogenannten „Modelling Breakthroughs“, müssen ständig in den Code einfließen. Diese wiederkehrenden Änderungen sind ein ganz normaler Prozess in DDD, sie sorgen dafür, dass das Domänenmodell „geschmeidig“ und damit leicht anpassbar und erweiterbar ist. Auf lange Sicht bringt diese Vorgehensweise also einen enormen Mehrwert, doch wenn das Unternehmen nicht bereit ist, die Kosten zu tragen, oder wenn ein Team absehbar nicht in der Lage ist, diese Arbeit zu investieren, dann ist DDD vermutlich nicht die beste Wahl. Es ist wichtig, sich darüber schon im Vorfeld klar zu sein, um später nicht enttäuscht zu werden.

Schwentner: Manche Projekte tragen das „Blaue Buch“ wie eine heilige Schrift vor sich her, nach dem Motto: „Wir machen alles genau, wie es hier drinsteht.“ DDD hat sich in den letzten zwei Jahrzehnten weiterentwickelt und es ist auch kein Wundermittel, das Lahme zum Gehen bringt. Aber es hilft dabei, sich auf das Wesentliche zu fokussieren: unsere Anwender verstehen und dadurch in die Lage kommen, großartige Software zu bauen. Und damit können dann durchaus lahme Systeme wieder zum Laufen gebracht werden.

Wolff: Eines der großen Probleme in der Softwareentwicklung ist das Benennen von Dingen. Domain-Driven Design löst das sehr gut: DDD bedeutet, dass die Domäne das Design treibt. Aber trotzdem gibt es sogar dabei Missverständnisse: Wenn der Fachbereich oder die Benutzer:innen nicht an der Entwicklung beteiligen sind, kann man kaum sicherstellen, dass Techniker:innen die Domäne verstehen – wie soll sie dann das Design treiben? Leider ist aber zu oft mindestens den Techniker:innen unklar, welche geschäftliche Bedeutung ein Projekt hat. Damit geht die Essenz von DDD verloren: Die Orientierung an der Fachlichkeit. Das ist viel wichtiger als diese oder jene Technik oder wie Klassen in einem System benannt sind. Leider geht aber genau das verloren.

JM: Bitte vervollständige den folgenden Satz: Für die nächsten 21 Jahre DDD wünsche ich mir …

Goeschel: … dass der aktuelle Trend zu immer systemischeren und ganzheitlichen Denkweisen sich fortwährend und flächendeckend durchsetzt. In den letzten zwei bis drei Jahren hat sich in der DDD-Community bspw. mit Team Topologies, Wardley Maps und Data Mesh eine ganze Reihe neuer Werkzeuge etabliert, die neben der reinen Programmierung auch Daten, Strategie und Zusammenarbeitsmodelle in den Fokus nehmen. DDD ist damit längst nicht mehr nur eine Geschmacksrichtung von Softwarearchitektur, sondern Grundlage für die Gestaltung ganzer Unternehmen. Für die großen Umwälzungen in Richtung Digitalisierung, die wir in den nächsten Jahren zu erwarten haben, wird der kluge Einsatz dieser Mittel entscheidend sein.

Stay tuned

Immer auf dem Laufenden bleiben! Alle News & Updates:

[mc4wp-simple-turnstile]

Hildebrandt: … einen reflektierten Umgang mit den Möglichkeiten und Werkzeugen, die DDD bietet, um es an den Stellen einzusetzen, an denen ein Mehrwert entsteht, und auf Elemente zu verzichten, wenn sie in der Software nur Ballast sind.

Hofer: … dass mit der Popularität nicht so viele Missverständnisse und überzogene Heilsversprechen einhergehen, wie das z. B. bei Agilität und Microservices der Fall war. Und um mit etwas Positivem zu enden: Zum 15-jährigen Jubiläum von DDD rief Eric Evans mit dem Motto „DDD is not over yet“ zur Weiterentwicklung auf. Ich wünsche mir viele neue, gute Ideen!

Koch: … mehr Diskussion und Experimentierfreude im Bereich praktischer Methoden, die die Anwendung von DDD unterstützen.

Lilienthal: … dass immer mehr Softwareentwicklungsteams die Qualität ihrer Legacy-Software mit DDD zu verbessern lernen und dass neue Systeme nur noch mit dem DDD-Blick auf die Fachlichkeit und die Anwender:innen gebaut werden.

Plöd: … weiterhin eine so offene und umtriebige Community, die das Thema immer weiterentwickelt, und ein weiteres Zusammenwachsen von Business und IT.

Rauch: … zwei Dinge: Zum einen beobachte ich einen immer stärker werdenden Technikwahn, und es werden Frameworks und Libraries benutzt, ohne dass richtig verstanden wird, ob man sie wirklich braucht und welche Nachteile man sich dadurch einhandelt. Oft wird der Code dadurch unnötig kompliziert, was die Nachvollziehbarkeit und Testbarkeit erschwert. Hier wünsche ich mir mehr Rückbesinnung auf das, was tatsächlich technisch notwendig ist, um so hin zu klarem, gut verständlichem Domänencode zu kommen.

Zum anderen finde ich es immer wieder erschreckend, wie viel Technik in den Entwicklungsprozess an und für sich gedrückt wird, sowohl für das Management der Anforderungen als auch des Codes mit starren Reviewtools und Ähnlichem.

Dabei möchte Domain-Driven Design (genau wie agile Vorgehensweisen übrigens auch) meiner Meinung nach hauptsächlich eins erreichen: Dass die Entwickler und die Domänenexperten endlich mal direkt und ungefiltert miteinander sprechen und so das gemeinsame Verständnis bestmöglich voranbringen. Denn um Alberto Brandolini zu zitieren: „Es ist nicht das Wissen des Fachexperten, sondern das Verständnis des Entwicklers, das in der Produktionsumgebung bereitgestellt wird.“ Ich wünsche mir, dass wir das in 21 Jahren verstanden haben werden.

Schwentner: a) … dass noch mehr Programmiererinnen und Programmierer verstehen, dass Softwareentwicklung kein Selbstzweck ist, sondern wir Software bauen, um unseren Anwendern das Leben leichter zu machen.

b) … sie sich in Theorie und Praxis in DDD ausbilden, sei es durch das Lesen von Büchern, in Trainings oder und gerade durch Ausprobieren im eigenen Projekt.

c) … weitere Werkzeuge in die Werkzeugkiste „Collaborative Modeling“ hinzukommen und Event Storming und Domain Storytelling ergänzen.

Wolff: Am Ende weist DDD jedoch auf ein fundamentales Problem bei der Softwareentwicklung hin: Die Domäne zu verstehen, um das Richtige zu bauen. Das wird voraussichtlich auch in 21 Jahren noch die wichtigste Herausforderung in der Softwareentwicklung sein und begleitet mich schon meine gesamte Karriere. Daran werden neue, produktivere Werkzeuge und Technologien nichts ändern. Aber ich finde es schwierig, über einen so langen Zeitraum Dinge vorherzusehen oder sich zu wünschen. Zudem sind die Umstände zu diesem Zeitpunkt deutlich anders: Ich bin dann vermutlich im Ruhestand. Dank der Untätigkeit bezüglich der Klimakatastrophe wird die Welt 2043 sicher auch sehr anders aussehen.

JM: Vielen Dank!

Die Fragen stellte Marius Nied.

The post 21 Jahre Domain-Driven Design appeared first on MAD Summit.

]]>
Aftermovie 2024 https://mad-summit.de/blog/modulare-software-architektur-microservices/aftermovie-2024/ Tue, 23 Jul 2024 07:10:25 +0000 https://mad-summit.de/?p=86135 Wir blicken zurück auf eine tolle Zeit bei der letzten Edition des MAD Summits. Schwelgen Sie mit uns in Erinnerung und schauen Sie sich das Aftermovie an.

The post Aftermovie 2024 appeared first on MAD Summit.

]]>
Erst im Juni 2024 fand unsere letzte Ausgabe des MAD Summit statt, und sie war ein voller Erfolg! Dies können Sie in unserem brandneuen Recap-Video sehen.

 

Hier unsere Highlights und Gründe, sich gleich die nächste Edition abzuspeichern:

  • Hervorragende Stimmung – Sich austauschende Teilnehmende, wohin man blickt.
  • Komplettpaket – Umfassende köstliche Verpflegung & sinnvolle Goodies.
  • Langzeit Nutzen – Praktische Code-Vorlagen, Aha-Momente und andere Erkenntnisse, die zur sofortigen Veränderung in der Arbeitsweise anregen.
  • Unterhaltsame Night Sessions mit Worst Practices in Sachen Web Security: Aus den Fehlern anderer lernen.
  • Spannendes Live-Modulieren einer Domäne eines mutigen Teilnehmers – direkte Einbindung eines realen Beispiels und Domain Storytelling Schritt-für-Schritt erklärt & diskutiert.

Jetzt vormerken: Weiter geht’s im November. Vom 25. – 27.11. werden unsere erfahrenen Fachexpert:innen in Berlin vielseitige Workshops und Night Sessions anbieten, die sich den Tiefen von modularer Software-Architektur, Microservices, Monolithen, Design Patterns sowie Künstlicher Intelligenz widmen.

The post Aftermovie 2024 appeared first on MAD Summit.

]]>
API-Design-Reviews durchführen https://mad-summit.de/blog/api/api-design-reviews/ Wed, 15 May 2024 07:43:01 +0000 https://mad-summit.de/?p=86037 Thilo Frotscher zeigt in diesem Artikel, wieso es äußerst wichtig ist, API-Design-Reviews durchzuführen, bevor Schnittstellen in Betrieb gehen.

The post API-Design-Reviews durchführen appeared first on MAD Summit.

]]>
Das Thema Systemintegration beschäftigt Entwickler:innen bereits seit sehr langer Zeit. Und es ist ein Thema, das bleiben wird. Manches ist über die Jahre gleichgeblieben, etwa Basistechnologien wie HTTP, die Unzuverlässigkeit des Netzwerks oder die Fallacies of Distributed Computing [1]. Andere Aspekte verändern sich: Trends kommen und verschwinden wieder (SOAP), eine API Economy entsteht und der Grad der Vernetzung nimmt immer rasanter zu.

Umso wichtiger ist es, dass Schnittstellen ein gutes Design aufweisen. Aktuell werden überwiegend HTTP APIs eingesetzt. Wer regelmäßig mit diesen arbeitet, wird feststellen, dass zahlreiche APIs nicht sehr gelungen sind, was ihre Betreiber über kurz oder lang vor einige Herausforderungen stellen wird. Aus diesem Grund ist es sehr wichtig, eine API-Design-Review durchzuführen, bevor eine Schnittstelle in Betrieb geht.

Wie lauten die Gründe für schlechtes API-Design? Oftmals ist ein solches sicherlich durch fehlende Erfahrung begründet. In vielen Fällen wird Entwicklungsteams jedoch auch schlicht nicht genügend Zeit eingeräumt, um gewissenhaft am API-Design zu feilen. Denn auf Ebene des Projektmanagements herrscht nicht selten die Ansicht vor, eine HTTP-basierte Schnittstelle müsste mit Hilfe moderner Tools und Frameworks von erfahrenen Entwickler:innen doch im Handumdrehen zu implementieren sein. Das ist im Grunde genommen auch richtig. In der ersten Iteration ist das Design dann jedoch in aller Regel noch nicht sehr durchdacht. Ein gutes API-Design benötigt Zeit – und idealerweise ein Review durch einen erfahrenen API-Experten.

Weshalb ist gutes API-Design eigentlich so wichtig? Einer der wichtigsten Gründe hierfür ist sicherlich, dass Schnittstellen, die zum Zwecke der Systemintegration entwickelt werden, in aller Regel eine sehr lange Nutzungsdauer haben. Sie müssen also über einen längeren Zeitraum betrieben, gewartet und oftmals auch weiterentwickelt werden. Es kommen neue Anforderungen hinzu, etwa bezüglich der Daten, die über die Schnittstellen ausgetauscht werden, bezüglich der Anwendungsfälle oder der Endgeräte, die angebunden werden sollen. Zudem steigt mit der Zeit typischerweise die Anzahl der Nutzer oder Clients. Ist ein API einmal in Betrieb genommen und wird es aktiv von Clients genutzt, ist es nicht mehr beliebig veränderbar. Wer seine Nutzer nicht verärgern möchte, muss daher genau darauf achten, dass bei Änderungen der Schnittstelle die Kompatibilität bisheriger Clients erhalten bleibt. Genau deswegen muss das API-Design bereits bei der ersten Inbetriebnahme der Schnittstelle eine hohe Qualität aufweisen. Dann lässt sich das mit überschaubarem Aufwand bewerkstelligen. Im Falle eines weniger gelungenen Designs werden erste Herausforderungen und Aufwände schon bald sehr deutlich in Erscheinung treten.

 

Entwickle Schnittstellen, die einfach funktionieren

Power-Workshops zu API Design & Entwicklung (22. - 26. Juni 2026, München)

Es ist wichtig zu betrachten, in welchem Umfeld ein API eingesetzt wird. Die Einsatzgebiete HTTP-basierter APIs lassen sich grob in fünf Kategorien einteilen.

  1. Backends für webbasierte Anwendungen

  2. Kommunikation innerhalb von Microservices-Architekturen

  3. Integration von Systemen im gleichen Unternehmen

  4. Integration von Systemen einer begrenzten Zahl bekannter Unternehmen (Lieferanten, Geschäftspartner)

  5. öffentliche APIs

Diese Einsatzgebiete unterscheiden sich zum Teil sehr deutlich hinsichtlich der Anzahl der API-Clients und der Bekanntheit oder Nähe zu ihren Entwicklungsteams. Backends für webbasierte Anwendungen haben in der Regel nur einen einzigen Client, und zwar das jeweilige Frontend. Dessen Entwickler:innen sind in aller Regel gut bekannt und gehören dem gleichen Team an. Notwendige API-Änderungen können „auf Zuruf“ vereinbart werden, ohne Rücksicht auf andere Clients nehmen zu müssen. Das API-Design kann genau auf die Use Cases der Anwendung zugeschnitten werden. Völlig anders verhält es sich mit öffentlichen APIs. Diese haben potenziell eine sehr große Anzahl von Clients, die zum Zeitpunkt der Schnittstellenentwicklung typischerweise noch nicht bekannt sind. Gewünschte Operationen, Datenfelder oder angestrebte Use Cases müssen daher ein Stück weit erahnt werden, oder das API-Design muss so generisch und erweiterbar ausgelegt sein, dass es beliebige Einsatzzwecke zulässt. Nicht kompatible Änderungen an öffentlichen APIs sind in aller Regel nur sehr schwierig möglich, sobald die Schnittstelle einmal in Betrieb ist und verwendet wird. Hinzu kommt, dass öffentliche APIs für viele Unternehmen ein Produkt darstellen, das erheblichen Einfluss auf Umsatz und Erfolg haben kann. Manchmal ist das API sogar die zentrale Dienstleistung eines Unternehmens – oder wird im Laufe der Zeit dazu. Das erste API-Design sollte also unbedingt gelingen. Die anderen drei Kategorien bilden unterschiedliche Grauzonen zwischen diesen Extremen. Je nachdem, in welchem Umfeld also ein konkretes API eingesetzt werden soll, ist die Qualität seines Designs also unterschiedlich wichtig. Und das sollte als Entscheidungshilfe dienen, ob überhaupt ein API-Review durchgeführt werden muss, bevor die Schnittstelle in Betrieb geht. Für Backends webbasierter Anwendungen ist das beispielsweise in der Regel nicht notwendig. Doch bereits in Microservice-Anwendungen kann es sehr sinnvoll sein.

Eine API-Design-Review beginnt in der Regel mit einigen grundsätzlichen Fragen. Was ist der Zweck des API? Wer soll es nutzen (Zielpublikum)? Und in welchem der genannten Einsatzszenarien? Die Antworten auf diese Fragen beeinflussen ganz wesentlich, wie das API gestaltet sein sollte. Handelt es sich beispielsweise um ein öffentliches API, dann müssen wir davon ausgehen, dass unternehmensfremde Nutzer möglicherweise manche Abkürzungen oder Bezeichnungen nicht kennen, die im Unternehmen des API-Betreibers geläufig sind. Es muss also darüber nachgedacht werden, ob einzelne Datenstrukturen nach außen gezielt in eine allgemein verständliche Sprache übersetzt werden sollten. Kennt man die künftigen Nutzer des API, so können gewünschte Datenstrukturen und Operationen mit diesen abgestimmt und ein API-Design gemeinsam iterativ erarbeitet werden. Sind die künftigen Nutzer (noch) nicht bekannt, muss das API-Design möglichst generisch sein und grundsätzlich beliebige Anwendungsfälle ermöglichen.

Eine weitere Frage, die sehr früh zu klären ist, ist die nach der angestrebten Strategie für die Versionierung. Soll das API explizite Versionsnummern haben? Falls ja, bedingt das einige Folgefragen, die allesamt nicht leicht zu beantworten sind. Wie wird die jeweilige Version sichtbar gemacht (URL, Headerattribut, Content-Type)? Wie viele Versionen sollen (maximal) gleichzeitig betrieben werden? Und für wie lange werden ältere Versionen weiter unterstützt? Eine Alternative zu expliziten API-Versionen könnte lauten, einfach keine zu haben. Es wäre dann immer nur die jeweils neueste Version des API in Betrieb, die zu beliebigen Zeitpunkten einfach in Betrieb genommen wird. Natürlich kann das nur gelingen, wenn jede neue API-Version rückwärtskompatibel zur vorherigen Version ist, also keine „breaking changes“ mit sich bringt. Ein solches Verfahren ist sehr viel bequemer für den API-Betreiber, bedingt jedoch bereits in der allerersten Version ein API-Design, das leicht erweiterbar ist.

Spätestens an dieser Stelle sollte klar geworden sein, weshalb es so wichtig ist, sich eingehende Gedanken über ein API-Design zu machen und dieses eben auch einer Review zu unterziehen, bevor die erste Version in Betrieb geht. Beim Review werden dann unterschiedliche Aspekte betrachtet, etwa die Themen Konsistenz, Verständlichkeit, Benutzbarkeit, Einhaltung von Standards bzw. Best Practices und Sicherheit. Je nachdem, ob es sich um ein internes oder öffentliches API handelt, wo es zum Einsatz kommt und wie viele Nutzer es hat, sind die einzelnen Aspekte des Reviews natürlich unterschiedlich wichtig.

 

 

Konsistenz

Einer der wichtigsten Punkte ist Konsistenz, und zwar sowohl innerhalb eines API als auch über alle APIs eines Projekts oder sogar eines ganzen Unternehmens hinweg. Denn wenn alle APIs, alle Operationen, Verhaltensweisen und Datenmodelle, eben alles was ein API-Design ausmacht, dem gleichen Ansatz folgen, dann sind diese APIs leichter zu verstehen und zu verwenden. Wer einmal gelernt hat, ein API des Betreibers zu verwenden, dem gelingt der Übergang zum nächsten API wie von selbst, weil dieses eben dem gleichen Design folgt und das gleiche Verhalten aufweist. Das macht bei unternehmensfremden API-Nutzern wie Kunden oder Lieferanten einen guten Eindruck, erleichtert die unternehmensübergreifende Systemintegration und sorgt für eine geringe Last an Supportanfragen. Aber auch unternehmensintern hat Konsistenz im API-Design gewaltige Vorteile. Entwickler:innen können dadurch viel leichter zwischen Projekten wechseln, ohne jedes Mal einen neuen API-Ansatz erlernen zu müssen. Nach Daten zu suchen oder zu filtern, sollte immer gleich funktionieren, unabhängig davon ob man nach Personen, Produkten, Bestellungen oder Lieferanten sucht. Gleiches gilt für das Abfragen, Aktualisieren und Löschen von Daten. Auch Datenformate (Datum, Uhrzeit, metrische Einheiten und Nachkommastellen) sollten konsistent verwendet werden, ebenso Bezeichnungen, Attributnamen und Formate.

Verständlichkeit

Ein weiterer Aspekt des API-Reviews ist die Verständlichkeit der Schnittstelle. Das betrifft unter anderem die Datenstrukturen, ihre Attributnamen, Semantik, Datenformate, Abkürzungen und Codes. Auch die Benennung und Struktur der URL-Pfade zu einzelnen API-Endpunkten sollten betrachtet werden. Sind diese nachvollziehbar und erwartbar? Oftmals existiert in der Fachlichkeit eine natürliche Hierarchie, die idealerweise auch im API-Design zu finden ist. Betrachtet man beispielsweise die Fachdomäne eines fiktiven Karten- oder Adressdiensts, so gibt es auf der Erde mehrere Kontinente, sie enthalten Länder und diese wiederum Städte. Die Städte haben mehrere Straßen usw. Wird diese Struktur von einem API und ihren URL-Pfaden nachgebildet, erhöht das ganz wesentlich ihre Verständlichkeit. Entwickler:innen von API-Clients können dann aufgrund ihres Hintergrundwissens gewisse Strukturen des API bereits erahnen, bevor sie die Dokumentation studieren. Ein API-Reviewer versucht sich daher in die Lage von Cliententwicklern zu versetzen. Was könnten sie erreichen wollen (z. B. eine neue Straße anlegen)? Wie würden sie annehmen, dass das mit dem API funktioniert? Und ist es so tatsächlich möglich oder sieht das API einen gänzlich anderen, eher nicht erwartbaren Weg vor, um eine neue Straße zu erzeugen? Zur Verständlichkeit eines API trägt natürlich in hohem Maße auch dessen Dokumentation bei. Eine API-Spezifikation im OpenAPI-Format ist das Mindeste, was ein API-Betreiber anbieten sollte. In aller Regel ist das jedoch nicht ausreichend. So sind weitere Informationen notwendig, etwa über die Bedeutung von Attributen in Datenformaten oder die Verwendung von Suchfiltern und Operationen. Die Dokumentation muss so geschrieben sein, dass sie für die Nutzer des API leicht verständlich ist. Im Falle öffentlicher APIs sollte die Dokumentation frei von unternehmens- oder brancheninternen Fachbegriffen und Abkürzungen sein.

Benutzbarkeit

Auch eine Betrachtung der Benutzbarkeit ist wichtiger Teil jedes API-Reviews. So gibt es eine ganze Reihe von Operationen, die in fast allen APIs früher oder später benötigt werden und deshalb idealerweise von vornherein Bestandteil des API-Designs sein sollten. Hierzu zählen das schon genannte Suchen und Filtern („Finde alle Bestellungen aus dem Jahr 2022 mit einem Bestellwert von mindestens 1 000 €“). Mindestens genauso wichtig für API-Clients ist die Möglichkeit, ein Limit für die Anzahl der zurückgelieferten Resultate zu spezifizieren und gegebenenfalls seitenweise durch eine Ergebnismenge zu navigieren (Pagination). Denn nur selten wird es sinnvoll oder effizient sein, Tausende oder gar Zehntausende Datensätze auf einmal zum Client zu transportieren. Eine weitere sehr sinnvolle Operation für API-Clients ist die Möglichkeit, anzugeben, in welcher Detailtiefe die Resultate geliefert werden sollen. Bei einer Suche nach Kunden könnten beispielsweise in einem Fall sämtliche bekannten Informationen über diese Kunden angefordert werden (Adresse, Bestellhistorie etc.), in anderen Fällen wären vielleicht nur die Namen und Kundennummern ausreichend. All diese Operationen sollten, wie bereits dargelegt, überall gleich funktionieren, also konsistent implementiert sein. Sie mögen generisch erscheinen, aber genau das ermöglicht es dem API-Betreiber, eine Schnittstelle bereitzustellen, die nicht auf spezifische Use Cases ausgelegt ist, sondern die es jedem einzelnen Nutzer erlaubt, seine eigenen Anwendungsfälle mit Hilfe dieser generischen Operationen umzusetzen. Zur Bewertung der Benutzbarkeit einer Schnittstelle zählt darüber hinaus auch eine Betrachtung der Datenstrukturen und die Frage, wie viele einzelne Requests ein Client senden müsste, um typische Anwendungsfälle umzusetzen. Ist die Anzahl der Requests zu hoch, weist das auf Verbesserungspotenzial im API-Design hin.

Einhaltung von Standards und Best Practices

Ein API-Reviewer sollte auch ein Auge auf die Einhaltung von Standards und Best Practices werfen. Nur selten ergibt es Sinn, von diesen abzuweichen und eigene proprietäre Lösungen zu implementieren. In aller Regel erschwert es nur die Integration des API durch seine Nutzer. Im Falle öffentlicher APIs kann es überdies ein Hinderungsgrund für ihre Verbreitung und damit für ihren Erfolg sein. Zu betrachten ist unter anderem die (korrekte) Verwendung des HTTP-Protokolls, seiner Request-Typen (GETPUTPOSTPATCHDELETE) und Statuscodes. Aber auch für die Benennung von URL-Pfaden oder Queryparametern sollten allgemein bekannte Best Practices eingehalten werden. Im Bereich der Sicherheit sind unbedingt bewährte Standards wie OAuth, OpenID Connect oder JWT zu verwenden.

Idealerweise existiert im Unternehmen eine API-Design-Richtlinie, die einen Designstandard für alle APIs festlegt. Es gibt viele gute (und weniger gute) Wege, HTTP-basierte APIs zu entwerfen, aber Unternehmen sollten sich für ein einheitliches Vorgehen entscheiden. Richtlinien definieren das Aussehen und die Funktionsweise von API-Designs. Wenn sie gut gemacht sind, stimmen sie mit den gängigen Praktiken in der Außenwelt überein, was die Nutzung der APIs noch einfacher gestaltet. Doch selbst wenn solche Richtlinien existieren, gibt es immer noch genügend Möglichkeiten, ungewollte Inkonsistenzen einzubauen. Eben diese aufzudecken, ist eine der Aufgaben der API-Reviews.

Sicherheit

Natürlich ist auch Sicherheit ein sehr wichtiges Thema, und daher ist es unbedingt empfehlenswert, einen Sicherheitsexperten mit einer separaten Prüfung zu beauftragen. Doch einige sicherheitsrelevante Aspekte kann auch der API-Reviewer bereits betrachten. Das betrifft beispielsweise die ausgetauschten Datenstrukturen und die darauf möglichen API-Operationen. Welche Daten und Attribute sind lesbar, welche sollten besser nicht ausgeliefert werden? Welche Daten sind manipulierbar und durch wen? Eine sinnvolle Strategie kann es sein, zunächst nur wenige Daten verfügbar zu machen, eventuell sogar nur die minimale Menge sinnvoll verwendbarer Attribute. Ein API-Betreiber kann dann abwarten, welche weiteren Attribute von API-Nutzern angefragt werden und jeweils im Einzelfall entscheiden, ob diese zugänglich sein sollten. So entsteht schrittweise eine Schnittstelle, die wirklich nur diejenigen Daten ausliefert, die benötigt werden. Auch im Sinne der Rückwärtskompatibilität ist ein solches Vorgehen auf jeden Fall sinnvoller, als nachträglich Attribute aus der Schnittstelle zu entfernen. Für den Zugriff und das Referenzieren einzelner Ressourcen oder Entitäten muss das API auch IDs ausliefern. Aus Sicherheitsgründen ist es hier sehr ratsam, über die Schnittstelle keine internen IDs (wie etwa Primary Keys der Datenbank) bekannt zu machen. Stattdessen sollten alle Ressourcen auch externe IDs haben, die nicht abzählbar und schlecht zu erraten sind. Beispielsweise UUIDs bieten sich hierfür an. Diese müssen dann am Übergang zwischen API und internem System in interne IDs übersetzt werden. Weitere Hinweise zu typischen Sicherheitslücken liefert das OWASP API Security Project [2].

Zusammenfassung

Öffentliche APIs können erheblich zum Umsatz und Erfolg eines Unternehmens beitragen. Sie sollten daher als Produkt verstanden werden. Für manche Unternehmen ist ihr API gar die zentrale Dienstleistung. APIs sollte daher die gleiche Pflege und Gewissenhaftigkeit zuteilwerden, wie jedem anderen Produkt. Das gilt auch für das Design. Doch auch bei internen, nicht öffentlichen APIs ist das Design ein entscheidender Faktor für Erfolg oder Misserfolg der Schnittstelle. Schlechtes Design hat mittelfristig Konsequenzen, etwa Mehraufwände, Supportanfragen, schlechte Erweiterbarkeit und Benutzbarkeit. Eine API-Design-Review durch einen erfahrenen API-Experten ist daher ein wichtiger Bestandteil der API-Entwicklung. Die Review sollte selbstverständlich durchgeführt werden, bevor das API erstmals in Betrieb geht und von Clients genutzt wird. Wichtige Aspekte einer Review sind Konsistenz, Verständlichkeit, Benutzbarkeit, die Einhaltung von Standards und Best Practices und natürlich auch Sicherheit. Diese Aspekte können unterschiedlich gewichtet sein, je nachdem, in welchem Umfeld das API eingesetzt wird.

 

Stay tuned

Immer auf dem Laufenden bleiben! Alle News & Updates:

[mc4wp-simple-turnstile]

 

Links & Literatur

[1] Fallacies of Distributed Computing: https://blogs.oracle.com/developers/post/fallacies-of-distributed-systems

[2] OWASP API Security Project: https://owasp.org/www-project-api-security/

The post API-Design-Reviews durchführen appeared first on MAD Summit.

]]>