Systemdesign

Ports and Adapters – eine Architektur für moderne Applikationen

Seite: 3/4

Anbieter zum Thema

Das Vorgehen von Ports and Adapters

1. Definieren der Ports

Anstatt der üblichen Schichten wird die Anwendung in die Namen gebenden Ports und Adapter eingeteilt. Adapter sind Komponenten nach dem klassischen Adapter-Pattern der Gang of Four. Das Wort „Port“ wurde gewählt, um an die Ports eines Computers zu erinnern. An einen solchen Port kann ein beliebiges Gerät angeschlossen werden. Dazu muss es lediglich das Protokoll des Anschlusses verstehen.

Für jedes Gerät gibt es einen Adapter, der zwischen der API und den Signalen übersetzt, die das Gerät benötigt. Ein passendes Beispiel hierfür sind die USB-Anschlüsse an einem Rechner. Von Abschussrampen, die Schaumstoffpfeile verschießen bis zu Tastaturen und Mäusen kann man dank einheitlicher Schnittstelle alles anschließen und betreiben.

Dieses Bildnis aus Anschlüssen und Adaptern lässt sich leicht auf Teile von Anwendungen übertragen:

  • Die Benutzeroberfläche (GUI - Graphical User Interface) ist ein Beispiel für einen Adapter, der die Kommunikation zwischen Nutzer und Anwendung ermöglicht.
  • Eine Datenbank ist ein Adapter, der die Datenhaltung verwaltet.

Eine Anwendung nach dem „Ports and Adapters“-Muster lässt sich generell graphisch wie folgt darstellen (Schaubild 2):

Schaubild 2. Hexagon: Die Anwendung ist das zentrale Bauteil und bietet Ports für eine Vielzahl von Adaptern.
Schaubild 2. Hexagon: Die Anwendung ist das zentrale Bauteil und bietet Ports für eine Vielzahl von Adaptern.
(Bild: method park)

Ein Sechseck wird verwendet, um zu verdeutlichen, dass eine Innen- und Außenasymmetrie besteht und dass verschiedene, in ihrer Funktion ähnliche Ports gibt. Es soll zudem darstellen, dass es eine Anzahl unterschiedlicher Ports vorhanden ist.

Dabei geht es nicht um die Zahl Sechs im Speziellen. Vielmehr erhalten Entwickler beim Entwurf Platz, um die verschiedenen Ports und Adapter einzuzeichnen. Der alternative Name des Musters ("Hexagonale Architektur") leitet sich von dieser Darstellung ab.

Bei Ports werden primäre und sekundäre Ports unterschieden. Primäre Ports sind solche, die die Anwendung anbietet und von außen aufgerufen werden. Die eigentliche Anwendungslogik ist beispielsweise ein primärer Port. Die sekundären Ports werden von der Anwendung selbst aufgerufen. Der Port für die Datenhaltung ist ein solcher sekundärer Port.

2. Vorgehen

Beim Entwurf von Anwendungen nach dem "Ports and Adapters"-Muster beginnt man üblicherweise mit der Definition der einzelnen Ports. Eine "klassisch aufgebaute" Anwendung mit Datenhaltung, Geschäftslogik und Benutzeroberfläche ließe sich wie folgt darstellen: (Schaubild 3)

Schaubild 3. Vereinfachte Darstellung: Eine Anwendung mit zwei Ports.
Schaubild 3. Vereinfachte Darstellung: Eine Anwendung mit zwei Ports.
(Bild: method park)

Die Anwendung besitzt zunächst in dieser einfachen Ausprägung zwei Ports: zum einen den Benutzer- und zum anderen den Datenhaltungsport.

  • Aber welche Adapter soll es geben? Und wie lassen sie sich identifizieren?
  • Der Benutzer soll die Anwendung durch eine HTML-Oberfläche steuern können.
  • Anderen Clients bzw. Tests sollen die Möglichkeit haben unsere Anwendung mittels einer REST-Schnittstelle zu steuern.
  • Die Daten der Anwendung sollen in einer Postgres-Datenbank gespeichert werden können.
  • Zu Testzwecken soll es möglich sein die Anwendung ohne eine Datenbank zu betreiben.

Daraus ergeben sich die in der Abbildung dargestellten vier Adapter (Schaubild 4):

Schaubild 4. Systemaufbau im Überblick: Eine klassische Anwendung mit UI und Datenbank im Stil der hexagonalen Architektur.
Schaubild 4. Systemaufbau im Überblick: Eine klassische Anwendung mit UI und Datenbank im Stil der hexagonalen Architektur.
(Bild: method park)

Im Anschluss an die Definition der Adapter und Ports werden deren Schnittstellen beschrieben. Dies kann entweder in UML geschehen oder direkt durch die Erstellung von Interfaces, wie man sie aus C# oder Java kennt.

Nach dem Entwurf der einzelnen Ports beginnt man mit der Implementierung. Entscheidender Vorteil: Die Implementierung der Adapter kann ab diesem Zeitpunkt parallel erfolgen, denn die Subsysteme der Anwendung kommunizieren nur über die zuvor definierten Ports.

3. Vorteile

1. Klare Einteilung der Komponenten: Der Einsatz des Patterns führt zu einer eindeutigen Einteilung der unterschiedlichen Komponenten. Alle Objekte, die beispielsweise für die Darstellung der Anwendung gebraucht werden, existieren lediglich im entsprechenden GUI-Adapter, während die für die Kommunikation mit der Datenbank notwendigen Objekte nur dem Datenbankadapter bekannt sind.

2. Eine Anwendung in mehreren Ausprägungen: Ähnlich wie in einem Baukastenprinzip lassen sich in der Hexagonalen Architektur verschiedene Varianten einer Anwendung zusammenfügen. So wäre es denkbar den Postgres-Datenbank-Adapter durch einen Android-SQLite-Adapter und den HTML-UI-Adapter durch einen nativen Android-UI-Adapter zu ersetzen. Mit dem Austausch von lediglich zwei Komponenten wandelt man die Anwendung zu einer nativen Android-Anwendung, ohne dass die eigentliche Geschäftslogik geändert werden muss.

3. Parallele Entwicklung: Die klare Einteilung in Komponenten und in wohldefinierten Schnittstellen ermöglicht es verschiedenen Teams oder Einzelpersonen, parallel an der jeweiligen Implementierung zu arbeiten. Zum Zeitpunkt des Entwicklungsstarts ist es einfach das Verhalten noch fehlender Komponenten mit "Test-Adaptern" zu simulieren. So muss man nicht auf die Fertigstellung beispielsweise der Datenhaltungsschicht warten.

4. Austauschbarkeit der Adapter: Sollte es notwendig sein, Funktionalität auszutauschen, beispielsweise das Datenbanksystem zu ersetzen, darf sich aus Anwendungssicht nicht zwangsweise auch das Protokoll zwischen der Anwendung und der Datenhaltungskomponente ändern. Durch den Einsatz von "Ports and Adapters" kann man dieses Risiko minimieren. Adapter sind leicht austauschbar. Daher ist ein Wechsel zwischen Datenbanken nichts weiter als ein Wechsel des entsprechenden Adapters.

5. Einfache Erweiterbarkeit und Skalierbarkeit: Durch das konsequente Entwickeln gegen Ports lässt sich die grundlegende Adapterstruktur leicht ändern. So wäre es denkbar, die einzelnen Adapter des Systems als Microservices zu realisieren. Ohne größeren Aufwand ist es so möglich, eine Anwendung von einem lokalem zu einem verteilten System zu migrieren.

6. Keine Festlegung auf eine Zulieferform: Da die Hexagonale Architektur keinerlei Annahmen über die Darstellung der Daten und die Entgegennahme der Benutzereingaben trifft, ist man folglich an dieser Stelle auch nicht festgelegt. Hat man seine Anwendung ursprünglich als schichtenbasierte Webanwendung entworfen, so ist es mitunter schwer, ihre Funktionalität einer mobilen Anwendung zur Verfügung zu stellen. Im Falle der Hexagonalen Architektur stellt die mobile Anwendung nur einen weiteren Adapter für die Entgegennahme der Benutzereingaben dar.

7. Keine Festlegung auf eine Architekturform innerhalb der einzelnen Komponenten: Innerhalb der Hexagonalen Architektur werden keinerlei Annahmen über den Entwurf der einzelnen Adapter getroffen. Um beispielsweise eine webbasierte Oberfläche als Adapter für die Benutzerinteraktion zu entwickeln, kann man sich problemlos eines Musters für die Oberflächenentwicklung wie etwa MVC (Model-View-Controller), bedienen.

4. Nachteile

Wie jede Architekturentscheidung bringt auch die Hexagonale Architektur Nachteile mit sich:

1. Höhere Komplexität: Durch den Einsatz jeder Abstraktion erhöht sich zwangsläufig die Komplexität der betroffenen Anwendung. Anders als bei einer monolithischen Anwendung befinden sich die einzelnen Komponenten des Gesamtsystems mitunter nicht mehr an einem Ort. Möglicherweise sind sie über mehrere Projekte und Subprojekte verteilt, was dazu führt, dass sich neue Kollegen zunächst mit der Orientierung etwas schwerer tun.

2. Orchestrierung der Anwendung notwendig: Um die einzelnen Teile der Anwendung zusammenzufügen, bedarf es einer Komponente für diese Aufgabe. Diese kann ihrer Aufgabe manuell oder per Dependency Injection nachkommen. Letzteres wiederum erhöht die Komplexität zusätzlich.

(ID:44687448)