Fünf Künstler - ein Bild.
Dazu eine Analogie: Software ist wie Kunst. Am Anfang ist das weiße Blatt Papier. 5 Entwickler versuchen auf diesem weißen Blatt Papier eine geforderte Problemstellung umzusetzen. Die Herausforderung besteht darin, dass Menschen zu einer gegebenen Problemstellung meist unterschiedliche Lösungen finden werden.
Ist das schlecht?

Ein Bild mit mehereren Lösungen
In diesem Fall leider: ja. Die Inhomogenität führt leider zu Verwirrung und erlaubt es anderen Künstlern auch nicht, das Bild zu vollenden (“Was wollte er mir damit sagen?”).
Wenn man fünf Künstler mit der Aufgabe “zeichne mir eine Landschaft” auf die Reise schickt, kann es folgende Erwartungshaltungen geben:
- Erwartet man sich 5 Landschaftsportrait, so funktioniert das gut.
- Erwartet man aber, dass fünf Künstler an einem Portrait zeichnen, so wird die Sache schon etwas schwieriger.

Arbeiten mehrere Künstler an einem Bild wird es schwierig
Aber welche Lösung gibt es?
Einer der fünf Künstler muss die groben Konturen vorgeben und die anderen müssen im vorgegebenen Rahmen zeichnen. Dass die vier Künstler nicht 100% in allen Dingen mit dem anderen Künstler übereinstimmen werden, liegt in der Sache der Natur. Aber das Bild sollte zumindest homogen sein und zum Schluss sollte etwas Sinnvolles rauskommen, sodass sich ein Laie schon schwertun wird festzustellen, dass hier fünf Künstler am Werk waren. Welche Konturen haben wir in der Softwarewelt?

Konturen erleichtern die Zusammenarbeit
Komponenten
Komponenten sind die kleinsten Bausteine, welche uns helfen, Struktur in die Software zu bekommen. Wie geht der Künstler vor? Er wird zuerst das Bild im Groben einteilen und dann weiter verfeinern.
Folgende Prinzipien helfen uns in der Softwarewelt dabei unsere Komponenten zu finden:
- Abstraction: “Weglassens von Einzelheiten und des Überführens auf etwas Allgemeineres oder Einfacheres” (Wikipedia)
- Problem decomposition: Ein komplexes Problem in kleinere – beherrschbarere – Probleme zerlegen
- Separation of concerns (SoC): Separierung, sodass jeder Teil eine wohl definierte Aufgabe hat
Quelle: https://room2-allenton-2021.weebly.com/thinking-thursday.html
Aus dem Alltag bietet sich hier die Analogie des Frühstücks an:
- Das Frühstück wird in Subprobleme zerlegt – dadurch sinkt die Komplexität der Einzelprobleme (ähnlich Divide & conquer aus der Algorithmik) und die Wiederverwendbarkeit wird gesteigert.
- Die Subprobleme wiederrum haben eine klare “Aufgabe” (Separation of concerns).
- Ebenfalls werden aber in Diagramm (im “Ablauf”) auch die Einzelheiten, wie das Kochen von Wasser genau funktioniert, weggelassen – also Abstraction.
Die “Aufgabe” wird nach außen über eine Schnittstelle angeboten. Ich denke mir oft im Alltag: Es ist so wie eine Sandkiste – wohldefiniert, abgeschlossen und dadurch eindeutig separierbar. Die Sandkiste ist also mein Sicherheitsnetz für die Struktur.

Analogie Sandkiste
Allerdings sind Komponenten im Regelfall noch relativ grob. So kann ich eine Komponente “E-Mail Client” haben. Innerhalb dieser “Sandkiste” ist aber noch nicht definiert, wie die “Konturen” ausschauen.
Durch die weiter oben beschriebene Strategie der Dekomposition, kann man das Problem natürlich in Subprobleme (Subkomponenten) zerlegen:
- Kommunikation mit dem Mail-Server
- E-Mail Kommunikation
- IMAP / SMTP
- MAPI
- E-Mail interpretieren
- Digitale Signaturen / Encryption
- HTML Parser für “Rich-Mails”
- Attachment Processor
- Inhalte verstehen
- Ich schreib hier mal salopp “AI” 😉
- E-Mail Kommunikation
Innerhalb der Subkomponenten will man aber u.U. auch modularisieren. Modularisierung ist ein Compiletime Konzept, welches ebenfalls erlaubt, Software-Konturen zu zeichnen. Wir wollen z.B. den Protocol Support leicht erweiterbar machen. Hier denken wir schon in einzelnen Klassen und Interfaces.
Neben der Organisation in Modulen (aka “packages” oder “class libraries”, …) gibt es natürlich auch noch andere Formen der Organisation auf Class-Ebene: Das Code-Design. Weitere Informationen findet man z.B. hier https://refactoring.guru/design-patterns.
Wir wollen also für unsere Komponenten (aber auch Modulen) folgendes erreichen:
- Hohe Kohäsion (Cohesion)
- Niedriges Kopplung (Coupling)
- Wiederverwendbarkeit
- Austauschbarkeit
- Testbarkeit (gut isoliert testbar)
- Lesbarkeit & Wartbarkeit
Zusammengefasst: durch unsere “Sandkisten” haben wir ein Konstrukt erschaffen, welches uns Homogenität in der Umsetzung erlaubt und bis zu einem gewissen Grad auch Sicherheitsnetz für Schadensbegrenzung, da ich innerhalb der Sandkiste immer sauber machen kann, ohne andere dafür berühren zu müssen. Innerhalb der Sandkiste helfen uns Module und Design-Patterns die “Sandburg” und den “Wassergraben” richtig zu postieren.
Architekturstil (Architectural Style)
Ein weiteres Konzept auf hoher Flugebene ist der Architekturstil. Ein Architekturstil beschreibt eine allgemeine, abstrakte Struktur eines Softwaresystems – also eine Sammlung von Regeln, wie Komponenten miteinander interagieren dürfen.
Beispiele:
- Client-Server
- Layered (Schichtenarchitektur)
- Event-driven
- Pipe-and-Filter
- Microservices
- Service-Oriented Architecture (SOA)
- Publish-Subscribe
Zu beachten ist, dass ein Softwareprojekt in den unterschiedlichen Domänen natürlich unterschiedliche Architekturstile haben kann. So kann Pipe-and-Filter bei der Daten-/Dokumentverarbeitung Sinn machen, während Microservices für den Rest Sinn machen. Auch hier ist es wieder wichtig, dass ein Künstler (oder eine Gruppe mit 1 Resultat) die Richtung vorgibt und damit der Wildwuchs nicht die Oberhand gewinnt.
Architekturpattern (Architectural Pattern)
Etwas feiner sind wiederum Architekturpattern. Ein Architekturpattern ist eine wiederverwendbare Lösung für ein konkretes Problem in einem bestimmten architektonischen Kontext. Es ist konkreter als ein Stil und oft auf ein Problem oder ein Ziel hin optimiert. Es bietet also eine strukturierte Lösung innerhalb eines Architekturstils.
Beispiele:
- Model-View-Controller (MVC)
- Model-View-ViewModel (MVVM)
- Broker Pattern
- Microkernel Pattern
- Layered Pattern
- CQRS (Command Query Responsibility Segregation)
- Ports and Adapters
Dazu möchte ich später einmal näher eingehen. Speziell die letzten beiden haben es mir angetan.
Bild vs. Software: Einhalten der Konturgrenzen garantieren
Am Bild sieht man schnell, wenn ein Künstler die Konturen ungeschickt überzeichnet. Software ist im Regelfall aber zu komplex, um es mit einem Blick (vor allem visuell) zu erfassen. Ich sehe oft bei Relationalen Datenbanken, wie wichtig Constraints sind (nicht nur einfache Check-Constraints sondern u.U. auch tabellenübergreifende Constraints). Diese “schreien” bei Übertretung.
In der Architektur nennt man es Fitnessfunktionen. ArchUnit, C# Roslyn Plattform bieten einen guten Startpunkt. Aber auch die visuelle Darstellung auf mehreren Ebenen macht Sinn. Hier musste ich aber lernen: Frameworks wie ARC42 oder C4 sind nur so gut, wie deren Aktualität. Tools wie EventCatalog zeigen dann sehr schön, wie wichtig Automatisierung ist, um eine Living Dokumentation zu haben.
Fazit
Der führende Künstler hat somit viele Möglichkeiten, Strukturen und Konturen vorzugeben, um eine Homogenität in der Lösung zu schaffen. Innerhalb dieser Konturen können sich Entwickler frei bewegen. Etwaige Refactorings fallen dann dementsprechend klein aus und sind gut zu kontrollieren.