Eine Sicht auf Berge mit einer Bank zum Hinsetzen.

Sichtbar bessere Architektur­dokumentationen

26. September 2024

|

Dominik

Ich hatte vor einigen Wochen angekündigt, eine kleine Reihe mit Tipps und Tricks zu Architekturdokumentation zu starten. Mit diesem Artikel erscheint nun endlich der zweite Teil in dieser Reihe und diesmal soll es um Sichten in der Softwarearchitektur gehen.

Ich habe das Glück, dass ich in der Vergangenheit und auch aktuell an und mit vielen Architekturdokumentationen in Projekten arbeiten darf und dabei fallen mir immer wieder Unterschiede im Verständnis und auch Verbesserungsmöglichkeiten in der Verwendung auf. Ich möchte daher hier auf ein paar ausgewählte Aspekte von Sichten in der Softwarearchitektur eingehen, auch Stellung beziehen, was ich ungünstig finde und basierend darauf Tipps geben, wie ich über bestimmte Dinge nachdenke und was für mich gut funktioniert.

Anfangen will ich aber mit einer ganz kurzen Wiederholung: Was sind eigentlich Sichten in der Softwarearchitektur, wofür brauchen wir sie und was machen wir damit?

Alle Softwaresysteme, mit denen wir es in unserer täglichen Arbeit zu tun haben, haben eine substantielle Größe und sind dementsprechend komplex. Sie sind so komplex, dass es nicht möglich ist, eine Darstellung zu finden, die alle Aspekte des Systems hinreichend erfasst, so dass man noch sinnvoll damit arbeiten kann.

Nick Rosanzki und Eoin Woods drücken das in ihrem Buch „Software Systems Architecture“ so aus:

Principle: It is not possible to capture the functional features and quality properties of a complex system in a single comprehensible model that is understandable by and of value to all stakeholders. 

Nick Rosanzki, Eoin Woods

Das heißt nicht, dass es nicht immer wieder Versuche gibt das zu tun, siehe unten. (Bitte macht das so nicht.)

Ein riesiges Architekturdiagramm auf dem man aufgrund der Menge der Elemente und Relationen faktisch nichts mehr erkennen kann

Was machen wir also? Wir wenden unsere auch sonst in der Softwarearchitektur so beliebten Prinzipien von Abstraktion und Divide and Conquer an und teilen die Beschreibung des Systems auf, indem wir viele unterschiedliche Darstellungen erstellen, in denen wir uns auf bestimmte Aspekte konzentrieren und ganz absichtlich andere Aspekte, sowie tiefergehende Details weglassen, weil die uns gerade nicht interessieren. Damit reduzieren wir die Komplexität in der Darstellung. Wir machen damit also, was andere Professionen quasi schon seit Jahrhunderten in der gleichen Weise tun, siehe beispielsweise Gebäudearchitektur (die Pläne der Statik, Elektrik, Wasserinstallation, usw. trennen) oder Musikkomposition (die die Noten für die unterschiedlichen Instrumente nicht in eine einzige Partitur packen). Es scheint also eine gute Idee zu sein.

Es gibt mittlerweile einige Vorschläge aus unterschiedlichen Büchern, Ansätzen oder Dokumentationstemplates, welche Sichten man verwenden sollte, um die Architektur seines eigenen Softwaresystems zu beschreiben. Sicherlich sind Euch schon die Sichten von arc42 oder das C4 Model über den Weg gelaufen, die neben anderen (4+1 Views, SEI Views and Beyond, etc.) heute zu den bekanntesten zählen. Und wie auch schon beim letzten Artikel kann ich auch hier wieder sagen, dass ich es super finde, dass es solche Ideen und Vorlagen gibt, die wir als Startpunkte verwenden können; ich würde mir aber wünschen, dass wir hier nicht stehenbleiben, sondern versuchen, das Beste für unseren Kontext, das heißt unser spezifisches System und unser Team herauszuholen. Dafür findet Ihr folgend einige Tipps und Hinweise.

Vier Sichten sind nicht alles

Vorlagen und Sichtenkataloge sind ein zweischneidiges Schwert: Wir bekommen einen guten Startpunkt und eine gute Anleitung, es schleicht sich aber auch gerne der Gedanke ein „Ich habe zu allen Sichten ein Diagramm gemacht, ich bin fertig.“ Ich finde das aus zwei Gründen gefährlich:

Erstens, ist keines der Modelle umfassend und deckt alle Aspekte ab, die man für die meisten Systeme fast immer erfassen sollte. Ein gutes Beispiel dafür sind Daten. Unsere Dokumentationen umfassen eigentlich immer eine Darstellung zu wichtigen Datenmodellen, Datenspeicherung, Datenzugriff, Datenaustausch, etc.

Zweitens ist nun einmal nicht jedes Softwaresystem gleich und Schema F wird häufig den Spezifika nicht gerecht (siehe vorangegangener Artikel). Und ich möchte hier auch Gernot Starkes Tipp aus seinem Buch (mit vollstem Respekt) widersprechen, keine weiteren Sichten zu erstellen, weil man damit alles Grundlegende abgedeckt hat. Meine Erfahrung aus ganz unterschiedlichen Projekten bringt mich zu einer anderen Erkenntnis. Ich würde stattdessen raten: Denkt darüber nach, was Euer System ausmacht und erstellt geeignete Sichten, um das möglichst gut auszudrücken. Das hat auch mit meinem nächsten Punkt zu tun.

Eine Bank mit schöner Aussicht auf einen Bergsee

Sichten sind überall

Ich möchte hier zwei Dinge auseinanderziehen, die häufig nicht so richtig gut unterschieden werden und deswegen die Diskussion und das Verständnis verwässern: Ähnlich wie bei Klassen und Objekten in der Objektorientierten Programmierung, müssen wir unterscheiden zwischen Sichttypen (manchmal auch Perspektiven genannt) und konkreten Sichten. Sichttypen definieren abstrakt, welche Aspekte beschrieben werden und welche Elemente und Relationen wir dafür verwenden, um dann basierend darauf (beliebig viele) konkrete Sichten zu instanziieren, um unterschiedliche Sachverhalte auszudrücken.

Das ist wichtig, weil wir Sichten eben nicht nur zur Beschreibung des Gesamtsystems, sondern gerade auch bei der Beschreibung von querschnittlichen Architekturkonzepten verwenden können und auch sollten:

Wenn wir uns also beispielsweise ein Logging-Konzept überlegen, bei dem wir einen zentralen Log-Server betreiben, an den die Logs aus unterschiedlichen Quellen mit Log Forwardern geschickt werden, der die Logs dann verarbeitet, aufbereitet, speichert und zur Analyse visualisiert zur Durchsuchung bereitstellt, dann wäre es zur Erklärung sicherlich hilfreich, auf strukturelle (Welche Komponenten sind daran beteiligt und wie integrieren die sich in das restliche System?), Verhaltens- (Wie läuft der Log-Transport, -Verarbeitung, -Speicherung und -Zugriff ab?) und Verteilungsaspekte der Lösung einzugehen. Und dafür machen wir dann eben einfach Baustein-, Verhaltens- und Verteilungssichten, speziell für dieses Konzept. Und analog machen wir das dann eben mit den anderen Architekturkonzepten, die wir für unser System entwickelt haben.

Natürlich verbietet kein Template so etwas zu tun, es wird aber auch nicht explizit so erklärt und folglich sehen wir das in Architekturdokumentationen auch quasi nie so gemacht. Es ist ein immer wiederkehrendes Muster, dass hier viel zu wenig Tiefe in der Beschreibung von Architekturkonzepten steckt. Dabei spielt doch gerade hier die Musik bei der Erfüllung unserer Qualitätsanforderungen.

Deswegen meine Empfehlung:

Bringt mehr Tiefe in die Beschreibung Eurer Architekturkonzepte, indem Ihr auch hier Architektursichten zur Beschreibung verwendet! 

Worüber reden wir hier gerade?

In Sichten auf der Ebene der Softwarearchitektur arbeiten wir häufig mit Elementen, die es so nicht als Konstrukte im Code gibt und deren Abbildung in die Technik auch nicht evident ist. George Fairbanks nennt das in seinem BuchModel-Code-Gap„:

„Your architecture models and your source code will not show the same things. […]. Your architecture models include some abstract concepts, like components, that your programming language does not, but could. […] You can relate that architectural idea to code, but it is not a straightforward structural correspondence. There is a gap between architecture models and code.“

Es ist nützlich, in dieser Weise auf der Ebene der Architektur zu arbeiten, weil wir ja gerade von den ganzen Details und Spezifika abstrahieren wollen um uns den Prozess des Lösungsdesigns zu erleichtern. Trotzdem dürfen wir auch nicht die Model-Code-Gap zu groß werden lassen und die Bodenhaftung verlieren. Das heißt, wenn wir in Sichten mit bestimmten Elementen arbeiten, sollten wir eigentlich immer wissen, was wir mit diesen Elementen meinen.

Wenn ich also beispielsweise mein System in „Subsysteme“ und darunter in „Services“ zerlege, wäre es vorteilhaft zu wissen, was deren Eigenschaften sind und was später im Code daraus werden soll. Ein Service könnte dann beispielsweise ein Objekt, ein eigener Prozess oder eine ganz eigenständige Applikation werden. Das sollte ich mir selbst klar machen, und dann natürlich auch den anderen erklären, die sich meine Sichten anschauen.

Während sich das trivial anhört, sieht man das Gegenteil leider häufig: Es werden Elemente verwendet, deren Bedeutung völlig ungeklärt ist (bspw. „Bausteine“) oder die sogar eine bestimmte Assoziation hervorrufen, aber dennoch anders abgebildet werden (bspw. „Anwendung“). Das hat leider auch damit zu tun, dass die Namen vieler Sichttypen nicht gerade so gewählt sind, dass sie die Bedeutung der zugehörigen Elemente sehr gut transportieren: Was beschreibt beispielsweise eine Logical View? Die inflationäre Verwendung des Begriffes „Komponente“ trägt ihr Übriges dazu bei.

Ich finde hier Stefan Zörners Praxistipp „Begriffe innerhalb des Projektes verbindlich erklären“ aus seinem Buch sehr gut. Die Idee ist, einfach einmal aufzuschreiben, was die Elemente bedeuten und wie sie sich auf die Technik abbilden, die Begriffe ins Glossar aufzunehmen und die Begriffe dann konsistent zu verwenden. Zur Beschreibung der Abbildung in die Technik kann man übrigens auch wieder hervorragend eine eigene Sicht verwenden.

Laufzeit und Entwicklungszeit (das leidige Thema)

Ein absoluter Dauerbrenner ist die Unterscheidung von Laufzeit und Entwicklungszeit, bzw. der Mangel an bewusster Unterscheidung bei der Erstellung von Sichten in Projekten. Das ist schade, denn Darstellungen, die unbewusst beides vermischen, werden häufig unpräzise, was zu Missverständnissen oder Fehlern führen kann.

Zur Erläuterung: In Laufzeit-Sichten beschreiben wir, wie sich das System verhält, während es ausgeführt wird, in Entwicklungszeitsichten beschreiben wir, wie das System aus technischen Elementen der Programmiersprache aufgebaut ist.

Zur Veranschaulichung des Unterschieds: gibt es einen Service, der mehrfach instanziiert wird, müssen wir in einer Laufzeitsicht mehrere Boxen zeichnen, weil es ihn zur Laufzeit ja auch mehrfach gibt. In einer Entwicklungszeitsicht zeigen wir nur eine Box, weil es ja die gleiche Codebasis ist. Zeigen wir den Service in einer Laufzeitsicht nur einmal, entgeht uns vielleicht die Interaktion der Serviceinstanzen untereinander, die wir eigentlich ebenfalls bedenken und designen müssen. Ich muss mich also fragen: „Beschreibe ich gerade wie sich das System verhält oder wie ich das System bauen möchte?“.

Folgend will ich ein paar Tipps rund um Laufzeit und Entwicklungszeit geben, die mir helfen zu besseren Sichten kommen:

Mit Laufzeit anfangen

Sowohl wenn wir das System designen, als auch wenn wir (als Leser) versuchen zu verstehen, wie das System funktioniert, denken wir zuerst darüber nach, was passiert, wenn das System läuft und erst danach, wie es gebaut wurde. Die einzige Ausnahme, bei der es uns eventuell leichter fällt, mit der Entwicklungszeit anzufangen ist, wenn wir ein existierendes System nachdokumentieren, weil der Code ja schon da ist. Aber da wir die Sichten ja nicht (nur) für uns erstellen, sondern vor allem für alle anderen Menschen im Projekt, müssen wir versuchen, für die einen guten Zugang zu schaffen. Deswegen sollten wir zuerst zeigen, wie das System funktioniert, und dann wie wir es gebaut haben oder bauen wollen.

Aus diesem Grund halte ich es für ungeschickt, mit einer Baustein-Sicht anzufangen, die aus Entwicklungszeit-Elementen besteht, wie es beispielsweise arc42 vorschlägt. Stattdessen beginnen wir besser mit einer Zerlegung des Systems in Laufzeitstrukturen, das heißt, wir zeigen, welche Elemente zur Laufzeit existieren und wie diese miteinander interagieren. So wird es beispielsweise auch im C4 Model mit den Components gemacht.

Damit meine ich nicht, dass wir direkt mit Sequenzdiagrammen einsteigen sollten. Es wäre auch falsch, Laufzeitsicht mit Sequenzdiagramm gleichzusetzen. Mit Laufzeitsichten meine ich alle Sichten, die das System während der Ausführung beschreiben. Dabei können strukturelle Aspekte, wie bei Komponentendiagrammen oder Kommunikationsaspekte, wie bei Sequenzdiagrammen im Vordergrund stehen.

Eine Bank mit schöner Aussicht auf einen See in den Bergen

Auch die Entwicklungszeit ist wichtig

Auch wenn ich eigentlich immer mit der Laufzeit anfange, sollten wir Sichten zur Entwicklungszeit keinesfalls vernachlässigen. Hier können wir nämlich zeigen, wie wir die Strukturen, die wir zur Laufzeit aufgebaut haben, technisch umsetzen wollen. Dabei versucht man häufig, Wiederverwendung und Modularität zu optimieren. Ich kann also beispielsweise zeigen, wie ich bestimmte Gemeinsamkeiten in eine Basis-Library ausgelagert habe, die man einfach nur einbinden muss.

Typische Aspekte, die wir gut als Entwicklungszeitsichten beschreiben können, sind beispielsweise:

  • Wie bilden sich die Laufzeitstrukturen auf Konzepte der verwendeten Technologien ab?
  • Wie ist der Code in der Entwicklungsumgebung organisiert?
  • Wie ist der Code in Source Code Repositories organisiert?
  • Welche Basis-Frameworks oder Libraries gibt es und wie werden sie verwendet?

Deployment ist die Brücke

Das Bindeglied zwischen Laufzeit und Entwicklungszeit ist das Deployment. In Deployment-Sichten kann ich zeigen, in welchen Ausführungsumgebungen und auf welchen Rechenknoten das System ausgeführt wird. Hier kann ich nun wieder die Elemente der Entwicklungszeit-Sichten aufgreifen, zeigen, wie wir diese in Binaries, jars, Container oder ähnliches verpacken und dann auf Ausführungsumgebungen und/oder Maschinen deployen.

Fazit: Macht mehr Sichten!

Ich hoffe, da waren ein paar hilfreiche Tipps dabei, die Euch helfen, Sichten in der Softwarearchitektur zu erstellen und einzuordnen. Neben den ganzen Vorlagen, Tipps und Formalismen finde ich es immer am wichtigsten, sich zu überlegen, was das System ausmacht und das mit klaren Messages zu transportieren. So, als wenn ich einem Kumpel erzähle, wie das System funktioniert, das ich gerade baue. Um diese Messages herum baue ich dann eine Sicht auf. Damit bin ich meistens auf einem ganz guten Weg.

Schreibt mir gerne Fragen oder Feedback, gerne auch zu den vielleicht etwas kontroversen Aussagen im Artikel. Wir helfen Euch auch gerne bei der Erstellung von gut verständlichen Sichten für Euer System. Dabei können wir Euch als „Phantombildzeichner“ und „Ghostwriter“ Eurer Architektur entlasten und Euch dabei unterstützen, in Zukunft möglichst hilfreiche Architekturdokumentation zu erschaffen.

Ich habe schon den nächsten Artikel für die Reihe rund um bessere Architekturdokumentation in der Pipeline. Also, Stay Tuned!

Dominik

0 Kommentare

Einen Kommentar abschicken

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert