Softwarearchitekturmuster
In Teil eins dieser Blog-Post-Serie haben wir uns mit der Rolle von Softwarearchitekturen im Kontext funktionaler und nicht-funktionaler Anforderungen beschäftigt. Als Nächstes lassen Sie uns einige grundlegende Muster für Softwarearchitekturen erörtern.
Softwarearchitekturmuster sind bekannte und bewährte Lösungen für nicht-funktionale Probleme, die in verschiedenen Softwaresystemen mit potenziell sehr unterschiedlichen funktonalen Anforderungen auftreten können. Sie sind dabei unabhängig von der verwendeten Programmiersprache oder Implementierung. Softwarearchitekturmuster sind nicht zu verwechseln mit Softwareentwurfsmustern, wie z. B. den bekannten Mustern, die von den Gang of Four definiert wurden und sprachtypabhängig sind (d. h. sie eignen sich nur für objektorientierte Sprachen). Softwareentwurfsmuster befassen sich speziell damit, wie eine einzelne Softwarekomponente zu implementieren ist, wie ihr Programmcode zu organisieren ist und wie ganz allgemein, das Schreiben von Programmcode verbessert werden kann. Softwareentwurfsmuster berücksichtigen dabei keine nicht-funktionalen Anforderungen.
Mit Hilfe von Softwarearchitekturmustern lassen sich dagegen bestimmte Architekturziele erreichen, z. B. die gleichzeitige Bedienung von Millionen von Kunden, ohne dass das Softwaresystem spürbar langsamer wird. Ein anderes Architekturziel ist die Garantie, dass empfangene Daten unter keinen Umständen verloren gehen, selbst bei Naturkatastrophen nicht. Wiederum andere Ziele können sich auf niedrige Kosten, Energieeffizienz oder einen geringen Speicherbedarf beziehen. Mit Softwarearchitekturmustern können sie eine für ihre Software spezifische Lösung entwerfen, um solche Architekturziele einzuhalten. Softwarearchitekturmuster finden in der Regel beim Entwurf großer Systeme Verwendung, die möglicherweise mehrere, in verschiedenen Sprachen geschriebene Dienste umfassen. Sie sind aber grundsätzlich auch für die Erstellung kleiner Systeme von Bedeutung.
Sehen wir uns einige Beispiele für Softwarearchitekturmuster an, die in heutigen Softwaresystemen zu finden sind und die man gewissermaßen als Übergang von traditionellen zu modernen Architekturmustern verstehen kann (wobei durchaus ein jeweils gültiger Anwendungsfall existiert):
Monolithische Architektur
Ein Architekturmuster, bei dem alle Software-Komponenten eng miteinander gekoppelt sind, auf einem einzigen Server laufen und Teil derselben Codebasis sind. In der Regel laufen alle Komponenten auch innerhalb eines einzigen Prozesses und teilen sich denselben Speicher.
Eine unvollständige Liste zu Vorteilen dieser Architektur:
- Komponenten können effizient in synchroner oder asynchroner Weise miteinander kommunizieren, indem durch die Verwendung von Datenstrukturen im selben Adressspeicher, der Kommunikationsaufwand sehr geringgehalten werden kann.
- leichter Einstieg in die Entwicklung und geringe Komplexität, um die Anwendung in Betrieb zu nehmen
Eine unvollständige Liste zu Nachteilen dieser Architektur:
- typischerweise eher geeignet für Geschäftslogiken mit geringer Komplexität (obwohl es offenbar einige Gegenbeispiele gibt, z. B. Stackoverflow, Shopify)
- mangelnde Flexibilität
-
- Programmcode-Änderungen in einer einzelnen Komponente erfordern eine Neueinrichtung des gesamten Softwaresystems mit all seinen Komponenten.
- Eine Änderung der Technologie in einer einzelnen Komponente kann zu Änderungen in allen anderen Komponenten führen.
- Entwicklung, Wartung und Verwaltbarkeit werden schwieriger, je mehr sich das Softwaresystem weiterentwickelt und je komplexer die Geschäftslogik wird.
-
- Schwache Skalierungsmöglichkeiten. Muss eine Komponente aufgrund gestiegener Anforderungen skaliert werden, erfordert dies die Skalierung aller Komponenten, z. B. durch Replikation der gesamten Anwendung.
- Der Koordinationsaufwand für Entwicklungsteams steigt mit der Teamgröße aufgrund des Arbeitens an ein und derselben Codebasis.
Mehrschichtige Architektur
Eine Client-Server-Architektur, die typischerweise als 3-Schichten-Architektur realisiert wird und die Darstellungslogik, Verarbeitungslogik und Datenverwaltungslogik durch physikalisch isolierte Komponenten trennt. Sie kann als ein erster Schritt in Richtung Microservice-Architektur betrachtet werden. Die Vor- und Nachteile sind insofern eine Mischung aus denen der monolithischen und Microservice-Architektur. Im einfachsten Fall bleibt die Verarbeitungslogik allerdings ein Monolith, die Darstellungslogik wird vom Endbenutzergerät übernommen, z.B. Handy, und die Datenverwaltungslogik wird durch eine SQL-Datenbank moduliert.
Microservice-Architektur
Der Grundgedanke der Microservice-Architektur besteht darin, verteilte, möglichst kleine Komponenten mir geringer Komplexität zu betreiben, die nur lose gekoppelt sind und über leichtgewichtige Protokolle miteinander kommunizieren können.
Jede Microservice-Komponente kann eigene, architektonische Merkmale mit unterschiedlichen nicht-funktionalen Anforderungen besitzen und wird in einer eigenen Codebasis gepflegt.
Eine unvollständige Liste zu Vorteilen dieser Architektur:
- sehr flexibel
-
- Code- oder Technologieänderungen in einer Komponente wirken sich nur auf die einzelne Komponente aus.
- Komponenten können in der Regel unabhängig voneinander in Betrieb genommen werden.
- Die Verantwortlichkeiten im Entwicklerteam können auf die Komponenten aufgeteilt werden.
-
- gut skalierbar, da nur einzelne Komponenten nach Bedarf repliziert werden müssen
Eine unvollständige Liste zu Nachteilen dieser Architektur:
- Die Kommunikationskosten hängen von der Anzahl und Größe der Nachrichten ab und können hoch sein.
- komplex und herausfordernd
-
- initialer Softwareentwurf und Aussagen zur Leistung des Entwurfs
- Datenkonsistenz und Zustandssynchronisation über alle Komponenten hinweg
- Protokollierung, Fehlersuche und Rückverfolgung über mehrere Komponenten hinweg
- erstmalige Inbetriebnahme, Wartung und Überwachung
-
- Erfordert performante Hardware, die ein hohes Maß an Parallelität ermöglicht. Potenziell ist eine verteilte Umgebung mit schneller Netzwerkanbindung nötigt.
Ereignisgesteuerte Architektur
Die ereignisgesteuerte Architektur kann eine Microservice- oder eine monolithische Architektur sein. Das Architekturmuster konzentriert sich speziell darauf, wie Komponenten zusammenarbeiten. Die ereignisgesteuerte Architektur gibt vor, dass ihre Komponenten entkoppelt sind und durch asynchron ausgelöste und empfangene Ereignisse interagieren. Es gibt keine direkten Abhängigkeiten zwischen den Komponenten. Stattdessen gibt es zwei Haupttypen von Komponenten: Produzenten und Konsumenten von Ereignissen. Ein Ereignis stellt eine mitteilungswürdige Änderung des Systemzustands dar und erfordert eine Aktion von einer oder mehreren Komponenten. Ein Ereignis ist ein abstraktes Konzept, das frei definiert werden kann, z. B. kann ein Ereignis eine Benutzerinteraktion wie ein Tastenklick sein; es kann datengesteuert sein, z. B. durch eine Änderung im Datenspeichers. Ein Ereignis kann auch etwas gänzlich anderes sein, z. B. die Beendigung eines langlaufenden Prozesses. Die Weiterleitung von Ereignissen erfolgt über einen Nachrichtenkanal oder -Bus (z.B. über einen Nachrichten-Broker wie RabbitMQ oder einen Service-Bus wie MassTransit). Eine solche Komponente kümmert sich darum Ereignisse von den Produzenten zu erhalten, zu validieren, zu speichern und an die Konsumenten zu liefern.
Eine unvollständige Liste zu Vorteilen dieser Architektur:
- flexibel und modular aufgrund der inhärenten, entkoppelten Ereignisbehandlung. Zu einem gewissen Maße trifft dies auch auf Monolithen zu, da Modul-Threads als Konsumenten und Produzenten fungieren können.
- skalierbar, da Konsumenten oder Produzenten individuell repliziert werden können
- flexible Behandlung von Ereignissen. Die Verarbeitung kann aufgeschoben werden und bei Bedarf erfolgen oder wenn ausreichend Betriebsmittel verfügbar sind
- fehlertolerant, da ausfallende Komponenten ersetzt werden können, ohne dass Ereignisse verloren gehen
Eine unvollständige Liste zu Nachteilen dieser Architektur:
- Daten- und Arbeitsabläufe können komplex und schwer zu verstehen sein.
- Protokollierung, Fehlersuche und Rückverfolgung über mehreren Komponenten kann eine Herausforderung sein.
- Der betriebliche Aufwand für die Inbetriebnahme, Wartung und Überwachung kann hoch sein und externe Komponenten wie Nachrichten-Broker mit einbeziehen.
- Ereignisse können in unerwarteter Weise mit Verzögerungen verarbeitet werden.
Event-Sourcing-Architektur
Die Event-Sourcing-Architektur ist eine Variante der ereignisgesteuerten Architektur, bei der alle Ereignisse als eine Sequenz von Änderungen des Systemzustands gespeichert werden. Der aktuelle Zustand des Systems wird durch die Anwendung aller Ereignisse in der Sequenz festgelegt. Die Konsumenten können die Ereignisse in ihrer zeitlichen Abfolge aus der Sequenz lesen und in ihre eigene, spezielle Darstellung projizieren, um eigene, spezifische Funktionalitäten bereitzustellen. Auf diese Weise können verschiedene spezialisierte Darstellungen dazu beitragen, unterschiedliche Anwendungsfälle effizient zu unterstützen.
Eine unvollständige Liste zu Vorteilen dieser Architektur:
- Robustheit: Eine Ereignissequenz stellt historische Daten dar, die dazu verwendet werden können, den Systemzustand auf einen beliebigen früheren Zeitpunkt zurückzusetzen.
- Rückverfolgbarkeit: Probleme lassen sich leicht reproduzieren, da die genaue Reihenfolge der Ereignisse, die zu einem Problem geführt haben, als Ereignissequenz verfügbar ist.
- Flexibilität: Weitere Anwendungsfälle können durch Erstellen dedizierter Konsumenten umgesetzt werden.
Eine unvollständige Liste zu Nachteilen dieser Architektur:
- Hohe Komplexität: Die Logik, das System in einen früheren Zustand zurückzuführen oder die Verwaltung der verschiedenen Konsumenten mit ihren spezifischen Datenprojektionen, kann schwierig sein.
- Hohe Speicherkosten: Die Speicherung aller Ereignisse im Zeitverlauf und all der verschiedener Datenprojektionen kann hohe Speicherkosten verursachen.
- Kompatibilitätsprobleme: Eine Änderung der Daten oder des Ereignisschemas impliziert komplexe Entscheidungen in Bezug auf bestehende Ereignissequenzen. Die Konvertierung historischer Ereignisse und Daten kann kostspielig sein und die Kompatibilität beeinträchtigen. Die Versionierung von Ereignissen ist nicht trivial.
Cloud-native Architektur
Im Gegensatz zu Anwendungen, die einer monolithischen Architektur folgen und später angepasst werden, um in der Cloud betrieben werden zu können, zielt eine Cloud-Native-Architektur von Anfang an darauf ab, Anwendungen für die Cloud zu entwickeln und nutzt hierzu explizit die Funktionen und Vorteile einer Cloud-Umgebung aus. Lesen Sie hierzu auch unsere Blog-Artikelserie speziell zum Thema Cloud-Native. Die Cloud-Native-Architektur führt die Idee der Microservice-Architektur auf die nächste Stufe. Cloud-native Komponenten werden als eine Reihe von Microservices realisiert, die in Containern betrieben werden und jeweils eine für die Komponente kohärente und spezifische Ausführungsumgebung bieten.
Im Gegensatz zu herkömmlichen Architekturen, die für hardwarebasierte Serverumgebungen konzipiert sind, ermöglicht es eine Cloud-native Architektur, mit praktisch unbegrenzten Betriebsmitteln zu arbeiten. Die von Anwendungen benötigten Betriebsmittel werden von Cloud-Anbietern auf Abruf und nach Bedarf bereitgestellt. Je nach Art der Bereitstellung, wird dies als Infrastructure as a Service (IaaS), Platform as a Service (PaaS) oder Software as a Service (SaaS) bezeichnet. Cloud-Native bedeutet jedoch nicht, dass entsprechende Anwendungen in einer öffentlichen Cloud ausgeführt werden müssen. Je nachdem welche nicht-funktionalen Anforderungen erforderlich sind, kann dies allerdings eine gute Option sein. Die Architektur ermöglicht aber ebenso die Nutzung eines privaten Cloud-Anbieters oder die Einrichtung und Konfiguration einer Softwareplattform wie Kubernetes auf eigenen Servern, um Cloud-native Komponenten auf dedizierter Hardware auszuführen und zu verwalten.
Eine unvollständige Liste zu Vorteilen dieser Architektur, insbesondere in Kombination mit öffentlichen oder privaten Cloud-Anbietern:
- Pay-As-You-Go: Anstatt für Einrichtung, Wartung und Betrieb eigener Server (auch im Leerlauf) zu bezahlen, zahlen Sie nur für wirklich genutzte und reservierte Betriebsmittel.
-
- Übergang von Investitionsausgaben (CapEx) zu Betriebsausgaben (OpEx)
- niedrige Einstiegskosten für den Aufbau eigener Dienste
-
- ermöglicht die Nutzung der von Cloud-Anbietern bereitgestellten, ausgereiften Werkzeuge, Funktionen und Diensten mit hoher Verfügbarkeit, z. B. Datenbanken, Load Balancer, Diagnose-Tools usw.
- äußerst flexibel, da von Natur aus skalierbar, zuverlässig und verfügbar
- Ausfallsicherheit: Cloud-Anbieter ermöglichen es, Dienste weltweit zu verteilen und so die Verfügbarkeit zu erhöhen.
- Lokalität: Dienste können in der Nähe Ihrer Kunden betrieben werden, um schnellere Antwortzeiten zu ermöglichen.
- Sicherheit: Das Modell der geteilten Verantwortung verringert die Sicherheitsrisiken für Cloud-Kunden.
- Hybride Setups sind möglich, bei denen Cloud-native Komponenten lokal auf eigener Hardware und entfernt in einer öffentlichen oder privaten Cloud betrieben werden.
Eine unvollständige Liste zu Nachteilen dieser Architektur, insbesondere in Kombination mit öffentlichen oder privaten Cloud-Anbietern:
- Pay-As-You-Go: Schwer verständliches Kostenmodel, und die Kosten können sich mit steigendem Betriebsmittelbedarf und genutzten Cloud-Funktionen erheblich erhöhen
- komplexes Softwarearchitekturmuster mit zusätzlichem Fokus auf den Kosten
- Die Hardware ist entweder außerhalb der eigenen Kontrolle (im Falle von Cloud-Anbietern) oder verursacht übermäßige Kosten für den Betrieb und Wartung der eigener Cloud-Plattformen.
- Bei lokal betriebenen Cloud-Plattformen kann die Hardware über- oder unterdimensioniert sein, und eine Skalierung der Hardware entsprechend dem Betriebsmittelbedarf der Software ist schwierig.
Die Quintessenz
Sämtliche Anforderungen an ein Softwaresystem zu erfassen, sie im Detail zu verstehen, die Auswahl des geeignetsten Softwarearchitekturmusters und ggfs. dessen Anpassung auf die spezifischen Bedürfnisse des eigenen Softwaresystems kann eine große Herausforderung sein. Entsprechend kann die Aufgabe, eine Softwarearchitektur zu entwerfen, gleichzeitig entmutigen und faszinieren. In jedem Fall muss sie gut durchdacht sein, denn es liegt in der Natur jeder Softwarearchitektur, dass es sehr zeitaufwendig, arbeitsintensiv und äußerst kostspielig ist, sie später wieder zu ändern, nachdem sie einmal implementiert wurde.
Wir bei der CID haben jahrzehntelange Erfahrung in der Planung und Gestaltung von Softwarearchitekturen und da die CID eine eigene, private Cloud in großem Maßstab betreibt, verfügen wir insbesondere über langjährige Kenntnisse und Erfahrungen mit allen Arten von Cloud-Themen. Ob es sich um einen digitalen Cloud-Transformationsprozess handelt, z.B. um einen sperrigen Monolithen in die Cloud zu hieven, oder um die Entwicklung von Cloud-nativen Komponenten von Grund auf, in der CID finden Sie immer einen zuverlässigen Partner.
</br />
Autor © 2024: Dr. Tom Crecelius – www.linkedin.com/in/tom-crecelius-65a16949/