… oder: Wie sich Softwarearchitektur von Lasagne zu Zwiebeln entwickelt hat.
Softwarearchitektur ist ein entscheidender Faktor für den Erfolg jeder Softwareanwendung. Eine gut durchdachte Architektur ermöglicht es, komplexe Systeme effizient zu entwickeln, zu testen und zu betreiben. Doch wie hat sie sich im Laufe der Zeit entwickelt? Welche Herausforderungen und Trends prägen die Architektur von heute und in Zukunft? In diesem Artikel werden wir einen Blick auf die Geschichte und die aktuelle Entwicklung der Softwarearchitektur werfen.
Layered Architecture
Beginnen wir mit einem Modell, das sicherlich jeder schon einmal in dieser oder ähnlicher Form gesehen hat: Layered Architecture. Sie ist, wie eine Lasagne, in Schichten aufgebaut. Weitere Namen dafür sind n-Tier-Architektur, Schichtenarchitektur, -modell oder -muster.
Die Schichten dienen dabei als logische Trennung des Codes. Dabei werden ähnliche oder verwandte Komponenten in der jeweils selben Schicht zusammengefasst. Es gilt, dass die Schichten – anders als im Big Ball of Mud – isoliert voneinander sind. Das bedeutet, dass die einzelnen Layer verändert werden können, ohne dass das eine Auswirkung auf andere Layer hat.
Bei dieser Architekturform wird auch zwischen offener und geschlossener Schichtenarchitektur unterschieden. Die geschlossene Variante erlaubt einer Schicht dabei nur das Aufrufen der Schicht direkt unter ihr. So wird gewährleistet, dass die Schichten gegeneinander isoliert bleiben und im Falle einer Änderung nur aneinander liegende Schichten angepasst werden müssen. Bei der offenen Variante darf jede Schicht eine beliebige unter ihr liegende Schicht aufrufen. Das hat den Vorteil, dass diese Systeme häufig performanter sind. Dem gegenüber steht ein höherer Grad an Kopplung.
Zusätzlich gibt es noch eine Sonderform der Layered Architecture, bei der eine Schicht auch mit der darüber liegenden Schicht reden darf, wie z.B. beim ISO/OSI-Modell.
Auch wenn es sich hierbei um eine sehr weit verbreitete Architekturform handelt, kommen doch einige Nachteile mit. Häufig muss durch die Abhängigkeiten auch die Domänenlogik angepasst werden, wenn sich an der Infrastruktur etwas ändert, weil der Domänencode benachbart zur Datenzugriffschicht (DAL, Data Abstract Layer) ist. Anwendungen im geschäftlichen Umfeld leben durchaus einige Jahre bis Jahrzehnte. Da ist es typisch, dass häufiger etwas angepasst werden muss.
Um dieser Problematik zu entgehen, kamen EntwicklerInnen bald auf die Idee, DAL von Domäne zu trennen…
Von Schichten zu Sechsecken …
So kamen Interfaces und das Dependency-Inversion-Prinzip ins Spiel. Robert C. Martin beschrieb das bereits 1996. In seinem Blog ist das Prinzip auch noch einmal nachzulesen.
Wikipedia beschreibt das Dependency-Inversion-Prinzip wie folgt:
Module höherer Ebenen sollten nicht von Modulen niedrigerer Ebenen abhängen.
Beide sollten von Abstraktionen abhängen.
Abstraktionen sollten nicht von Details abhängen.
Details sollten von Abstraktionen abhängen.
Am Ende geht es darum, die Abhängigkeit der Module umzukehren. Das Modul auf der höheren Ebene soll die Schnittstelle definieren, mit der gearbeitet wird. Die Module auf niedrigerer Ebene sollen die Schnittstellen implementieren. Das Adapter Pattern der „Gang of Four“ tut beispielsweise genau das. Das wird gleich noch wichtig. Jedenfalls könnten wir nun unsere Datenbank austauschen, z. B. von SQLite zu PostgreSQL oder MongoDB, ohne die Anwendung anpassen zu müssen. Dieses Architekturmuster wird auch heute noch gerne für einfache CRUD-Anwendungen oder Rapid Prototyping genutzt und ist dafür gut geeignet.
Wenn das Prinzip der Abhängigkeitsumkehr auf alle Schnittstellen angewendet wird, wie z. B. User Interface, APIs, Logger, Mailer, Datenbank und was es sonst noch an externen Systemen gibt, lösen wir uns bereits von der Darstellung mit Schichten.
Plötzlich ist es in der Darstellung nicht mehr wichtig, ob ein Modul oben oder unten angeordnet wird. In den 1990er Jahren visualisierte Alistair Cockburn diese Art der Architektur als Hexagon; die hexagonale Architektur war geboren.
… und zu Kreisen
Tatsächlich können aber auch mehr oder weniger als sechs Systeme mit der Domäne interagieren. Innerhalb eines Jahrzehnts – wir schreiben das Jahr 2005 – setzte sich der Begriff Ports & Adapters für diesen Architekturstil durch. Hier sind wir jetzt wieder bei dem Adapter Pattern, welches eben benutzt wird, um die Module, die nicht zur Domäne gehören, anzubinden. Das Wort „Ports“ wurde gewählt, weil das Prinzip an die Ports bei einem Computer erinnern: An einen Port kann ein beliebiges Gerät angeschlossen werden, solange es das Protokoll des Anschlusses spricht.
Um jetzt endlich zur Zwiebel zu gelangen, prägte Jeffrey Palermo 2008 den Begriff der Onion Architecture, bei der die bekannten Konzepte wieder aufgegriffen wurden. Die Darstellung erfolgt dabei als mehrschichtiges, rundes Gebilde, welches an die Schichten einer Zwiebel erinnert. An der Art und Weise, wie Systeme angebunden werden, hat sich nicht viel geändert.
Allerdings wird hier noch ein wenig feiner zwischen den einzelnen Services unterschieden. Den Kern der Architektur stellt das Domänenmodell dar. Es ist frei von allen Abhängigkeiten und beschreibt die fachlichen Bauteile der Anwendung. Im zweiten Ring befinden sich die Domänen-Services, welche die fachliche Logik beinhaltet. Diese Services sind nur vom Domänenmodell abhängig und bilden die komplette Fachlichkeit der Anwendung. Umschlossen wird die Fachlichkeit von der Anwendungsschicht, die dafür verantwortlich ist, die Verarbeitung von Anfragen zu koordinieren und die Geschäftsregeln umzusetzen. In der äußeren Schicht sitzen die Kommunikationsschnittstellen zur Außenwelt, Infrastruktur und Tests.
Vorteile der Onion Architecture
Die Vorteile einer solchen Architektur liegen dabei auf der Hand:
- Anwendungen können sich auf einen einzigen Aspekt der Fachlichkeit konzentrieren. Mehrere dieser Anwendungen können dann wie in einem Baukasten zusammengesteckt werden. Höre ich da etwa Microservices?
- Die Entwicklung kann parallelisiert werden und mehrere Teams können zeitgleich an den unterschiedlichen Anwendungen entwickeln.
- Die Adapter sind austauschbar und damit die angeschlossenen Systeme.
- Läuft so eine Anwendung in der Cloud, kann sie auf einfache Art und Weise automatisch skalieren.
Der Nachteil allerdings ist, dass sich die Komplexität der Anwendung durch ihre Isolierung und die Abstraktionen deutlich erhöht. Auch die Kommunikation zwischen mehreren Services, die zusammen eine Anwendung bilden, darf nicht unterschätzt werden. Hier ergeben sich schnell Probleme, wie die Daten untereinander konsistent gehalten werden können. Lösungen dafür versprechen z. B. Saga Pattern.
Softwarearchitektur im Wandel
Wie man sieht, unterliegt Softwarearchitektur einem stetigen Wandel. Aber selbst im Zeitalter von Cloud-Native wird die monolithische Layered Architecture nicht verschwinden. Für kleine, einfache Anwendungen oder Prototypen hat sie ihre Daseinsberechtigung. Durch die Verwendung moderner Architekturansätze können Entwicklerinnen und Entwickler flexiblere und skalierbarere Anwendungen erstellen, die besser auf die sich schnell verändernden Anforderungen der heutigen Welt reagieren können.
Die Investition in eine solide Architektur lohnt sich also nicht nur für den reibungslosen Betrieb der Software, sondern auch für den Erfolg des Unternehmens.
Schreibe einen Kommentar