Haben Sie schon einmal davon gehört, dass Architekten keine Spaghetti mögen? Als Software-Architekten sind wir dafür verantwortlich, Systeme zu konzipieren und zu entwerfen, die die Geschäftsmodelle dieser Ära mit ihren rasanten Veränderungen unterstützen können. Vor diesem Hintergrund müssen wir Wege finden, unsere Applikationsarchitektur so weiterzuentwickeln, dass sie den Geschäftskonzepten und -prozessen entspricht. Andernfalls entsteht die strukturell unausgereifte und deshalb so verhasste „Spaghetti-Architektur“.

In diesem Blogbeitrag stelle ich einige der Best Practices vor, die Sie befolgen sollten, um eine strukturierte und skalierbare Applikationsarchitektur zu erreichen und Spaghetti-Systeme zu vermeiden. Dieser Artikel basiert auf einem aktuellen TechTalk zum Thema: Web and Mobile Architecture with Architecture Dashboard. Darin erhalten Sie weiterführende Details zum Thema.

Fangen wir an!

Warum ist Applikationsarchitektur so wichtig?

Die Applikationsarchitektur umfasst alle Softwaremodule und -komponenten, interne und externe Systeme sowie die Interaktionen zwischen ihnen, die eine Applikation ausmachen. Eine gut strukturierte Applikationsarchitektur stellt sicher, dass Ihre Applikationen mit Ihrem Unternehmen wachsen und die intendierten Geschäfts- und Benutzeranforderungen erfüllen. Gleichzeitig stellt sie sicher, dass alle Konzepte korrekt isoliert sind und vernünftige Abhängigkeiten untereinander bestehen.

Angenommen, Sie ignorieren die Architektur von Applikationen, während Sie Änderungen vornehmen und neue Anforderungen zu Ihrem Software-Projekt hinzufügen. Genau dies führt früher oder später zu einer Spaghetti-Architektur – einem Labyrinth aus unüberschaubarer Synchronisation und unüberschaubaren Abhängigkeiten zwischen verschiedenen Teilen Ihrer Applikation.

 

Beispiel einer Spaghetti-Architektur.
Beispiel einer Spaghetti-Architektur.

 

Eine Spaghetti-Architektur führt zu mehreren Problemen. Die wichtigsten sind:

  • Schlechte Dienstabstraktion: Durch die nicht korrekte Isolierung und Abstraktion von Diensten rund um Kerngeschäftskonzepte werden Geschäftsregeln über verschiedene Systeme verteilt, was die Wiederverwendbarkeit von Code sehr unstrukturiert oder sogar unmöglich macht.
  • Unkontrollierbare Abhängigkeiten: Wenn Komponenten nicht korrekt voneinander isoliert sind, hat die Aktualisierung oder Ersetzung eines Systems einen Schneeballeffekt – d.h. Änderungen in einem Teil wirken sich auf alle seine Abhängigkeiten aus.
  • Unflexible Legacy-Systeme: Die schnelle Anpassung eines Legacy-Systems an geschäftliche Änderungen wird schwierig. Wenn das System komplex und unflexibel ist, können Änderungen viel Zeit in Anspruch nehmen. Und wenn die Technologie veraltet ist, kann die Anhäufung von Core-Information und Abhängigkeiten der Systeme im Laufe der Zeit jegliche Neuerungen hemmen.

Soviel zu den Gründen, warum Architekten keine Spaghetti-Systeme mögen. Wie aber lassen sie sich vermeiden?

Best Practices zum Aufbau einer skalierbaren Applikationsarchitektur

Der Schlüssel zum Aufbau einer skalierbaren und zuverlässigen Applikationsarchitektur liegt darin, Ihre Architektur auf fest definierte Grundsätze und etablierte Fundamente zu stützen. So können Sie rasantes Wachstum und massive Skalierbarkeit unterstützen. Zugleich vermeiden Sie Albträume bei der Bereitstellung und höhere Code-Wartungskosten. Dies ermöglicht Ihnen, mit den geschäftlichen Anforderungen Schritt zu halten.

Um dies zu erreichen, beginnen Sie mit einem Architekturdesign, mit dem Sie:

  • Konsens zwischen allen Akteuren fördern
  • Unterstützung planen
  • den Wandel erleichtern
  • Komplexität verwalten
  • Risiken reduzieren
  • technischen Schulden minimieren – das Endziel jeder gut durchdachten Architektur.

Wie erstellen Sie ein zukunftssicheres Architekturdesign? Hier möchte ich Ihnen das Architecture Canvas vorstellen – ein Framework zur Unterstützung und Beschleunigung des Architekturdesigns, dem wir bei OutSystems folgen.

Was ist das Architecture Canvas?

Das Architecture Canvas ist ein mehrschichtiges Framework, das die Abstraktion wiederverwendbarer Dienste und Komponenten fördert. Gleichzeitig bewahrt es unabhängige Lebenszyklen und minimiert die Auswirkungen von Änderungen. Dadurch lässt sich Ihre Architektur leichter warten und weiterentwickeln.

So sieht das Ganze aus:

Drei Schichten des Architecture Canvas

Von unten nach oben:

  • Foundation-Schicht: In dieser Schicht implementieren Sie alle wiederverwendbaren nicht-funktionalen Anforderungen, wie z. B. Dienste zur Verbindung mit externen Systemen oder zur Erweiterung Ihres Frameworks durch Bibliotheken von wiederverwendbaren UI-Mustern und Themen. Hier finden sich alle Arten von nicht-funktionalen Anforderungen.
  • Core-Schicht:Aufbauend auf der Foundation-Schicht implementieren Sie Ihre Kerngeschäftsdienste, einschließlich Diensten rund um Geschäftskonzepte, Geschäftsregeln, Geschäftseinheiten, Geschäftstransaktionen und Geschäftswidgets. Diese Dienste sollten systemunabhängig sein und auf den Foundation-Diensten basieren, um alle benötigten Integrationsdetails zu abstrahieren. In diesen beiden unteren Schichten isolieren Sie alle wiederverwendbaren Dienste oder Komponenten.
  • Endbenutzer-Schicht: Auf der oberen Schicht unterstützen Sie die Interaktionen Ihrer Anwender durch Benutzerschnittstellen und Prozesse. Dabei nutzen Sie die Core- und Foundation-Dienste zur Unterstützung der User Journey. Beachten Sie, dass ein Modul auf dieser Schicht niemals Dienste für andere Module zur Verfügung stellen sollte, um die vollständige Unabhängigkeit des Lebenszyklus zu gewährleisten.

Validierung der Struktur Ihrer Architektur

Um sicherzustellen, dass Ihre Architektur solide (und keine Monolith- oder Spaghetti-Architektur) ist, gibt es eine Reihe von Richtlinien und Empfehlungen. Die folgenden Regeln beziehen sich auf starke Referenzen über Module oder Applikationen hinweg (z.B. für Aktionen oder Blöcke). Lassen Sie lose Referenzen wie Service-Aktionen oder Bildschirmziele also außer Acht.

1. Keine Aufwärtsreferenzen über die 3 Schichten hinweg:

Angesichts der strukturierten Schichtung liegt es auf der Hand, dass die geschäftsunabhängigen Basisdienste nicht von Kerngeschäftskonzepten oder wiederverwendbare Dienste nicht von Endbenutzerschnittstellen abhängen sollten. Darüber hinaus bilden Aufwärtsreferenzen oft ein Cluster, in dem zwei direkt oder indirekt verbundene Module eine zirkuläre Abhängigkeit aufweisen. Sehen Sie sich das folgende Beispiel an:

Keine Aufwärtsreferenzen über die 3 Schichten hinweg

Modul B kann Modul A indirekt erreichen und Modul A kann Modul B indirekt erreichen. Wir haben also ein Cluster von voneinander abhängigen Modulen. Wenn Sie ein weiteres Endbenutzermodul (EU2) haben, das den Kerndienst B legitim konsumiert, wird es von dem gesamten Cluster abhängig. Dadurch erhält dessen Runtime nicht nur einen unnötig großen Speicherbedarf, sondern es wird auch von Änderungen in Modulen beeinflusst, die es nicht einmal kennen sollte.

2. Keine Seitenreferenzen unter Endbenutzern:

Die Endbenutzer-Module sollten niemals wiederverwendbare Dienste bereitstellen, um sicherzustellen, dass sie korrekt isoliert sind. So können Endbenutzer unterschiedliche Lebenszyklen haben. Siehe das Beispiel unten:

Keine Seitenreferenzen unter Endbenutzern

Wenn Endbenutzer 1 Endbenutzer 2 konsumiert, verhindert dies nicht nur dessen Unabhängigkeit von EU2, sondern auch von der übrigen darunter liegenden Hierarchie.

3. Vermeiden Sie zirkuläre Referenzen zwischen Core- und Foundation-Modulen:

Wenn Sie die beiden vorherigen Regeln befolgen, müssen Sie sich keine Sorgen über Zyklen zwischen Endbenutzer-Modulen machen. Ein Zyklus ist immer unerwünscht, da er eine erwartete Auswirkung darauf hat, wie der Code verwaltet wird. Ein Zyklus zwischen Modulen zeigt an, dass die Konzepte nicht korrekt abstrahiert sind.

Im folgenden Beispiel sollte eines von zwei Dingen geschehen: 1) Entweder sind A und B so stark miteinander verbunden, dass sie Teil desselben Moduls sein sollten (z. B. Auftrag und Auftragsposition), oder 2) eine der Abhängigkeiten sollte dadurch gebrochen werden, dass die Logik an die richtige Stelle gesetzt wird, entsprechend der erwarteten Beziehung zwischen den Konzepten. Zum Beispiel hängen Verträge von Kunden ab. Kunden sollten dagegen ohne Referenz zu Verträgen existieren können.

Vermeiden Sie zirkuläre Referenzen zwischen Core- und Foundation-Modulen

4. Zusätzliche Empfehlungen:

 

  • Kernmodule sollten keine Frontend-Bildschirme haben: Wenn Sie einen Dienst implementieren, möchten Sie vielleicht Bildschirme hinzuzufügen, um Unit-Tests durchzuführen. Als Entwickler sollten Sie sich jedoch von Testbildschirmen trennen, sobald Sie Ihren Code testen. Wenn die Testbildschirme nützlich sind, um Regressionstests oder BDD-Tests zu unterstützen, sollten sie in ein Endbenutzer-Testmodul verschoben werden. Eine der größten Gefahren dabei, Testbildschirme auf Ihren Kernmodulen zu lassen, besteht darin, dass es sich bei diesen Testbildschirmen in der Regel um anonyme Bildschirme für Testzwecke handelt. Sollten diese in Produktion gelangen, stellen sie eine Sicherheitslücke dar.
  • Alle Entitäten sollten als schreibgeschützt verfügbar gemacht werden: Diese Empfehlung stellt sicher, dass Sie Consumern nicht erlauben, die CRUD-Aktionen zum direkten Erstellen, Aktualisieren oder Löschen von Datensätzen in der Datenbank zu verwenden. Denn der Kerndienst sollte die Geschäftstransaktionen abstrahieren und sich um Validierungen, Normalisierungen und Nebenwirkungen wie Audits oder die Integration mit einem anderen System kümmern. Implementieren Sie alle Ihre Geschäftstransaktionen als öffentliche Aktionen, um Consumern sichere und korrekt abstrahierte Dienste zur Verfügung zu stellen.
  • Vermeiden Sie Geschäftslogik auf der Foundation-Schicht: Manchmal werden Geschäftsregeln in dieser Schicht implementiert, doch sie sollte vom Geschäft unabhängig sein. So kann sie in jedem Bereich der Applikation wiederverwendet werden.
  • Fügen Sie keine Kerngeschäftseinheiten auf der Foundation-Schicht hinzu: Auch hier gilt, dass Foundation-Module keine geschäftsbezogenen Einheiten enthalten sollten, um geschäftsunabhängig zu sein. Sie können jedoch nicht-geschäftsbezogene Einheiten enthalten, um nicht-funktionale Anforderungen Ihrer Applikationen zu unterstützen. Wenn Sie z. B. einen generischen Dienst zur Prüfung all Ihrer Transaktionen erstellen müssen, können Sie eine Audit-Einheit erstellen. Das Audit selbst ist nicht das Geschäft Ihres Unternehmens; das Geschäft Ihres Unternehmens ist der Verkauf eines Produkts oder die Provisionierung eines neuen Kunden oder die Änderung eines Vertrags.

Die Komposition einer Applikation mit dem Architecture Canvas

Bevor wir die Zusammenstellung der Applikation durchgehen, eine kurze Anmerkung: In diesem Zusammenhang hat „Applikation“ nicht die gleiche Bedeutung, wie im geschäftlichen Kontext üblich.

Bei OutSystems verwenden wir den Begriff „Applikation“ für einen Satz von Modulen, die in Service Studio – der Entwicklungsumgebung von OutSystems – definiert wurden und die die minimale Bereitstellungseinheit für LifeTime bilden – OutSystems Konsole zur Verwaltung all Ihrer Umgebungen, Applikationen, IT-Benutzer, Sicherheit und des App-Lebenszyklus. Was Sie von der Entwicklung zur QA und von der QA zur Produktion befördern, sind jedoch keine einzelnen Module, sondern Applikationen.

Um festzustellen, welcher Schicht die Applikation entspricht, sollten Sie nach der obersten Schicht der Module innerhalb der Applikation suchen. Wenn die oberste Schicht z. B. ein Endbenutzer-Modul ist, dann handelt es sich um eine Endbenutzer-Applikation.

Die Komposition einer Applikation mit dem Architecture Canvas

Sobald eine Applikation eingerichtet ist, sollten Sie eine Reihe von Regeln befolgen, um für eine korrekte Architektur zu sorgen.

Regel #1: Beginnen Sie mit den Richtlinien des Architecture Canvas für Module

Schichten Sie Ihre Module korrekt gemäß den oben definierten Richtlinien.

Regel #2: Isolieren Sie gemeinsame Dienste

Wenn die Module richtig eingesetzt sind, können Sie sich den Applikationen widmen. Verwenden Sie hier die gleichen Prinzipien. Wenn Sie ein Modul auf „End-user Application 2“ haben, das ein Modul auf „End-user Application 1“ konsumiert, sollten Sie die gemeinsame Core-Applikation isolieren, um Abhängigkeiten zu vermeiden. Auf diese Weise unterstützen Sie beide Applikationen. Sobald Sie etwas teilen möchten, müssen Sie die gemeinsamen Dienste in gemeinsamen Applikationen isolieren.

Wie Sie gemeinsame Dienste isolieren

Regel #3: Keine Vermischung von Verantwortlichkeiten

Wenn eine Applikation mehr einen Besitzer hat, führt dies zu einer komplexen Bereitstellungsverwaltung, da die Verantwortlichkeit für Änderungen uneindeutig wird. Klare Zuständigkeiten sind von zentraler Bedeutung. Wenn sich die Applikation keinem einzigen Besitzer zuzuweisen lässt, sollten die Verantwortlichkeiten klar aufgeteilt werden.

Wie Sie Verantwortlichkeiten trennen

Regel #4: Keine Vermischung von Projektsponsoren

Genau wie Besitzer haben auch Sponsoren unterschiedliche Ansprüche und Grundlagen. Zum Beispiel: Stellen Sie sich ein Portal vor, das die Simulation verschiedener Versicherungssparten ermöglicht. Wenn alle Sparten unter der gleichen Applikation laufen, kann eine Änderung in einer von ihnen (z. B. im Automobilbereich) nicht unabhängig von den anderen erfolgen. Das bedeutet, dass die langsamste Sparte den Release-Zyklus diktiert.

Durch das Erstellen von separaten Applikationen pro Geschäftssparte kann jede Sparte das Tempo der jeweiligen Lieferung selbst bestimmen. Sobald dies geklärt ist, sollten Sie herausfinden und isolieren, was von den verschiedenen Sponsoren gemeinsam genutzt wird und was gemeinsam geregelt werden sollte.

Wie Sie Sponsoren voneinander trennen

Möchten Sie das Architecture Canvas in Aktion sehen?

Dann lade ich Sie ein, meinen kürzlich stattgefundenen Tech Talk Tech Talk Web and Mobile Architecture with Architecture Dashboard anzusehen. In der Session zeige ich anhand eines Praxisbeispiels, wie man eine Architektur nach dem Canvas-Prinzip entwirft, und stelle einige Best Practices vor, mit denen Sie eine Spaghetti-Architektur vermeiden. Außerdem stelle ich das Architecture Dashboard vor – ein Tool, das Ihnen bei der Evaluation Ihrer Ergebnisse hilft.