Die vorgestellten Entwurfsmuster haben Lösungsansätze für den Feinentwurf aufgezeigt, d.h. sie betreffen konkrete Entwurfsentscheidungen für die objektorientierte Programmierung im Inneren einer Komponente. Architekturmuster haben im Unterschied zu den Entwurfsmustern den Anspruch, die grundlegende Struktur und Interaktion zwischen den Komponenten eines Anwendungssystems zu bestimmen. Demzufolge treffen wir Architekturmuster im Entwicklungsprozess in der vorausgehenden Phase des Grobentwurfs an. Grundsätzlich betrachten Architekturmuster auf einer abstrakten Ebene das Zusammenspiel von Komponenten, ohne dabei vorauszusetzen, wie diese Komponenten konkret implementiert werden, z.B. in welcher Programmiersprache. Auf dieser Ebene bleiben Architekturmuster per Definition abstrakt. Das gemeinsame Ziel sämtlicher Architekturmuster ist es, entsprechend dem Prinzip der Modularisierung Abhängigkeiten zwischen den Komponenten bewusst so zu gestalten, dass eine möglichst geringe Kopplung erreicht wird.

Als Ausgangspunkt wird meistens ein sogenanntes "monolithisches System" angenommen, für das scheinbar keine durchdachte Struktur vorliegt und das uns dem Begriff Monolith entsprechend als ein einzelner großer Baustein erscheint. Ein monolithisches System ist typischerweise bereits seit vielen Jahren im produktiven Einsatz. In der Vergangenheit wurde es kontinuierlich erweitert, bis es seine heutige Komplexität erreicht hat. Es läuft überraschend stabil, aber es ist nicht mehr (oder kaum noch) wartbar, erweiterbar und skalierbar. Unter den Entwicklern wird es als sogenanntes Legacy-System bezeichnet, dessen präskriptive Architektur längst erodiert ist. Wenn wir genau in den Monolithen hineinschauen, werden wir nichtsdestotrotz irgendeine Struktur erkennen, aber die Abhängigkeiten der Module oder Klassen sind so stark miteinander verwoben, dass sich keine Komponenten herauslösen lassen. Das Refactoring des Systems erscheint daher dringend notwendig.

Zusammenfassend ergeben sich folgende Nachteile eines monolithische Systems [KB18]:

Die Schichtenarchitektur (auch Schichtenmodell) ist ein einfaches Muster zur Strukturierung eines Softwaresystems, bei dem die einzelnen Aspekte (z.B. Komponenten oder Klassen) eines Systems jeweils unterschiedlichen Schichten (engl. Tier oder Layer) zugeordnet werden. Es gilt das grundsätzliche Prinzip, dass aus höheren Schichten auf tiefere Schichten zugegriffen werden darf, aber nicht von unten nach oben. Die Abhängigkeiten sind also von oben nach unten gerichtet. Bei einer strikten (oder geschlossenen) Schichtenarchitektur sind ausschließlich Zugriffe auf die nächsttiefere Schicht zulässig, d.h. es dürfen keine Schichten übersprungen werden. Im Gegensatz dazu darf in einer nicht-strikten (oder offenen) Schichtenarchitektur eine beliebige tiefere Schicht aufgerufen werden. Der Kontrollfluss und die Daten müssen dadurch nicht durch zwischenliegende Schichten durchgeschleust werden, was im verteilten System zu einer verbesserten Performance führen kann. Als Nachteil steht diesem Vorteil gegenüber, dass sich der Grad der Kopplung im System erhöht, wenn die tieferen Schichten potentiell von diversen höheren Schichten eingebunden werden und nicht nur von der nächsthöheren Schicht.

Die bekannteste Schichtenarchitektur besteht aus 3 Schichten:

Wie die folgende Abbildung zeigt, können diese 3 Schichten in einem verteilten System, das aus Client und Server besteht, je nach Anwendung unterschiedlich zugeordnet werden. Ein Thin Client ist ausschließlich für die Präsentation der Daten zuständig. Ein Fat Client dagegen übernimmt neben der Präsentation auch die eigentliche Verarbeitung der Daten, d.h. insbesondere Validierung und Transformation, und u.U. sogar die Datenhaltung.

Die unterschiedlichen Varianten der 3-Schichtenarchitektur sollen folgend kurz erläutert werden, wobei erst in späteren Kapiteln auf konkrete Frameworks zur Implementierung eingegangen wird. Wir stellen uns dazu eine Web-Anwendung vor, deren Client-Komponenten in JavaScript implementiert sind und innerhalb eines Web-Browsers des Anwenders ausgeführt werden. Die Server-Komponenten seien in Java implementiert. Grundsätzlich sind auch andere Anwendungstypen, z.B. eine Desktop-Anwendung oder eine mobile Anwendung, und andere Programmiersprachen vorstellbar.

  1. In einer Web-Anwendung finden Verarbeitung und Datenhaltung i.d.R. auf dem Server statt. Viele Frameworks sind darauf ausgerichtet, eine serverseitige Template-Engine einzusetzen, um HTML-Templates mit Daten aus den verbundenen Datenbanken zu befüllen und HTML-Dokumente an den Client auszuliefern. Im Java-Umfeld zählen zu den aktuell verbreiteten Template-Engines u.a. Mustache, Thymeleaf, FreeMarker sowie die mittlerweile veralteten JavaServer Pages (JSP). Wenn eine serverseitige Template-Engine eingesetzt wird, erstreckt sich die Präsentationsschicht über Client und Server.
  2. Alternativ dazu kann eine Web-Anwendung so gebaut sein, dass der Server anstatt ganze HTML-Dokumente nur strukturierte Daten im JSON- oder XML-Format an den Client ausliefert. Diese Daten werden durch den Client empfangen, verarbeitet und in das Document Object Model (DOM) des Browsers dynamisch eingearbeitet. In diesem Fall sind Präsentations- und Verarbeitungsschicht über die Schnittstelle zwischen Client und Server klar voneinander getrennt.
  3. Um die Bedienbarkeit für den Anwender durch schnellere Rückmeldungen zu verbessern, bietet es sich an, einfache Validierungen der Anwendereingaben bereits im Client durchzuführen. Andernfalls wäre für jede Validierung eine Anfrage an den Server erforderlich, deren Antwortzeit von der verfügbaren Datenübertragungsrate abhängig ist. Auf diese Weise wird ein Teil der Anwendungslogik in den Client verschoben, so dass die Verarbeitungsschicht sich auf Client und Server aufteilt.
  4. Es kann auch sämtliche Anwendungslogik einer Web-Anwendung im Client implementiert werden. Lediglich die Datenhaltung wird noch auf einem Server bereitgestellt, um die Daten zwischen mehreren Clients über eine zentrale Stelle austauschen zu können. Jeder Client benötigt in dieser Architektur einen persönlichen Zugang zur Datenhaltungsschicht. Ein entsprechender Dienst, der sich auf reine Datenhaltung und vorausgehende Authentifizierung beschränkt, ist z.B. Google Firebase.
  5. Wenn die Daten ausschließlich zentral auf einem Server gehalten werden, kann die Anwendung nicht ohne aktive Verbindung zum Server zwecks Datenaustausch genutzt werden. Um dem Anwender zu ermöglichen, die Anwendung auch offline zu nutzen, werden Daten auf dem Client zwischengespeichert und bei aktiver Verbindung zum Server periodisch synchronisiert. Bei dieser Architektur erstreckt sich die Datenhaltungsschicht also auf Client und Server gleichermaßen. Die Herausforderung liegt in der Synchronisation der Daten, wenn diese sowohl auf dem Client als auch auf dem Server (indirekt von einem anderen Client) verändert worden sind. Diese Architektur wird häufig für mobile Anwendungen eingesetzt, da diese hinsichtlich der Verbindung zum Server zwischen online und offline schwanken.