Die IODA Architektur

Über die Jahre haben einige grundlegende Architektmodelle um unsere Gunst gekämpft. Mir fallen ein in chronologischer Reihenfolge ein:

  • MVC (Ja, das rechne ich zu Architekturmodellen, weil es Leute gibt die sagen, "Unsere Software hat eine MVC-Architektur.)

Auch ich habe mir erlaubt, dieser Liste einen Eintrag hinzuzufügen. 2005 kam mit die Idee der Softwarezellen. Die habe ich in einigen englischen Blogartikeln beschrieben - allerdings sind da über die Jahre die Bilder abhanden gekommen :-(

Leider ist es über die Jahre mit der Wartbarkeit/Wandelbarkeit von Software trotz dieser schönen Architekturmodelle nicht wesentlich besser geworden. Auch Anwendungen, die sich einen Schichtenmodells rühmen, waten heute meist im tiefen Brownfield.

Woran kann das liegen?

Ich glaube nicht, dass die Architekturempfehlungen falsch sind. Es steckt eine Menge Beachtenswertes in ihnen. Doch es muss auch noch etwas fehlen. Sonst wäre die Situation der Codebasen nicht so, wie sie ist.

Gemeinsamkeiten

Ich versuche mal, die obige Vielfalt ein wenig zu ordnen. Dann wird klarer, was da ist. Immer eine gute Sache, bevor man etwas verbessert.

Verantwortungsstruktur

Die augenfälligste Gemeinsamkeit aller Architekturmodelle ist die Benennung und Trennung von Verantwortlichkeiten. MVC und Schichtenmodell z.B. sagen ganz klar: Es gibt drei wesentliche Aufgaben in (jeder) Software. Die sollten in unterschiedlichen Modulen[1] realisiert werden.

Bemerkenswert ist, dass die Verantwortlichkeiten alle inhaltlicher Art sind. Sie beziehen sich auf die Herstellung von Verhalten zusammengesetzt aus unterschiedlichen Aspekten. So gehört zum Verhalten einer Software z.B. die Erzeugung von Seiteneffekten auf Bildschirm (Presentation Layer des Schichtenmodells oder View bei MVC) oder Festplatte (Data Access Layer des Schichtenmodells oder DB bei Clean Architecture) sowie ganz wesentlich die Domäne (Business Logic Layer des Schichtenmodells oder Entities bei Clean Architecture).

Beziehungsstruktur

Die identifizierten Verantwortlichkeiten setzen die Architekturmodelle in Beziehung. Sie definieren, welche Verantwortlichkeit mit welchen anderen in einem Abhängigkeitsverhältnis steht. Dabei geht es immer um funktionale Abhängigkeiten, d.h. Dienstleistungsbeziehungen.

Beim Schichtenmododell ist das am einfachsten: jede Schicht ist nur von der darunterliegenden abhängig. Auch die Clean Architecture hält die Abhängigkeiten unidirektional: sie weisen von außen nach innen. Bei MVC und seinen Verwandten hingegen sieht es anders aus.

image

System-Umwelt-Trennung

Und schließlich geht es auch noch um eine deutliche Trennung von Softwaresystem und Umwelt. Den Verantwortlichkeiten an der Systemgrenze kommt eine besondere Bedeutung zu. Das stellen insb. die Hexagonale Architektur und Softwarezellen heraus.

Bei den Softwarezellen nenne ich diese Schale deshalb ausdrücklich Membran. Über Adapter findet ein bidirektionaler Austausch der Domäne mit der Umwelt statt.

image

Anders als die Hexagonalen Architektur unterscheiden Softwarezellen jedoch, wie sie mit der Umwelt in Verbindung stehen. Dass die Umwelt auf sie einwirkt, ist davon zu trennen, dass sie auf die Umwelt einwirken. Ersteres geschieht durch Portal-Adapter, Letzteres durch Provider-Adapter.

Holarchie

Nur die Softwarezellenarchitektur schlägt darüber hinaus noch vor, Softwaresysteme auf mehreren Abstraktionsebenen mit dem selben Mittel zu beschreiben. Die Softwarezelle selbst ist damit Modellierungselement.

Mit dem Schichtenmodell kann man eine Software einmal in Schichten zerlegen. Mit der Onion Architektur kann man eine Software einmal aufgebaut aus Schalen beschreiben. Aber mit Softwarezellen kann eine Software nicht nur einmal in Portale, Provider und Domäne zerlegt werden, sondern auch noch in kleinere Softwarezellen.

Das Softwarezellenmodell ist mithin selbstähnlich hierarchisch. Auf jeder Ebene der Hierarchie befinden sich Softwarezellen, die wiederum aus Softwarezellen zusammengesetzt sein können.[2]

Eine solche Hierarchie, bei der die Teile gleichzeitig Ganze sind, wird auch als Holarchie bezeichnet.

Das gemeinsame Problem: Funktionale Abhängigkeiten

Das sieht doch alles ganz ordentlich aus, oder? Mehr oder weniger detailliert werden inhaltliche Verantwortlichkeiten in funktionale Abhängigkeit gesetzt. Mal laufen die in die eine Richtung, mal in die andere. Wenn es damit nicht funktioniert, dann wird man das Modell wohl noch nicht sauber oder fein genug angewandt haben. Was sollte man auch anders tun?

Meine Vermutung ist, dass das Problem tiefer liegt, wenn trotz der Architekturmodelle die Software in die Unwartbarkeit rutscht. Nicht sauber mehr davon anwenden hilft dann, sondern aufhören und etwas anderes tun.

Woran könnte es denn aber liegen, dass es nicht einfach klappt mit diesen Modellen?

Ich denke, die Separierung von Verantwortlichkeitskategorien ist nicht das Problem. Sie ist dringend nötig. Zuviel davon kann es kaum geben. Lieber die Verantwortlichkeiten etwas differenzierter sehen, als zu viel davon in einem Modul zu versammeln. Das Single Responsibility Principle (SRP) und seine Verwandten (SoC, SLA, ISP) sind nicht umsonst eines der immer wieder beschworenen Fundamente der Softwareentwicklung.

Auch die deutliche Trennung eines Softwaresystems von der Umwelt durch eine explizite Membran ist nicht das Problem. Sie dient dazu, dass Softwaresystem im Sinne des SRP zu fokussieren und von äußeren Einflüssen zu entkoppeln.

An einer holarchischen Sicht, die ohnehin von den meisten Modellen nicht thematisiert wird, kann es auch nicht liegen. Im Gegenteil: Ich denke, mehr davon würde der Softwareentwicklung helfen. Sie macht die Zerlegung von Kompliziertem einfacher, weil die Zerlegungsebenen immer wieder mit denselben Mitteln beschrieben werden.

Was bleibt sind also die Abhängigkeiten.

Ja, ich denke, dass die funktionalen Abhängigkeiten zwischen inhaltlichen Verantwortlichkeiten das Problem sind. Logische Kopplung lässt sich nicht vermeiden. Die jedoch mit funktionaler Kopplung auszuweiten, öffnet der Ausbreitung von Veränderungen Tür und Tor.

Alle Architekturmodelle kranken an funktionalen Abhängigkeiten. Dabei ist es egal, ob die von links nach rechts oder oben nach unten verlaufen. Funktionale Abhängigkeiten sind böse!

Das wissen Sie aus Ihrem täglichen Leben: Solange Sie eine Aufgabe allein erledigen können, ist alles gut. Die Schwierigkeiten explodieren, sobald Sie davon abhängig sind, dass Ihnen jemand etwas zuliefert. Sie verlieren damit die Hoheit über die Erledigung. Wenn etwas nicht klappt, interessiert es den Abnehmer Ihrer Leistung nicht, ob Sie das zu verschulden haben oder Ihr Zulieferer. Sie sind Verantwortlich für das Ergebnis. Und schon fängt das Micro-Management an. Und schon seufzen Sie unablässig, “Wenn man nicht alles selbst macht…”

Inhaltsunabhängige Verantwortlichkeiten

Um aus dem Problem der funktionalen Abhängigkeiten zwischen inhaltlichen Modulen herauszukommen, schlage ich nun ein neues Architekturmodell vor. Es unterscheidet sich sehr grundlegend von den vorgestellten - aber genau das macht seinen Charme aus, finde ich. Aber der Reihe nach…

Unabhängige Module

Aus den funktionalen Abhängigkeiten kann man sich nicht herausdefinieren. Schichtenmodell wie Clean Architecture leiden gleichermaßen unter ihnen. Da hilft keine Änderung der Ausrichtung der Abhängigkeiten. Nicht die Richtung ist entscheidend, sondern ihre schiere Existenz.

image

Das bedeutet, eine bessere Architektur muss funktionale Abhängigkeiten komplett abschaffen. Welche Verantwortlichkeiten man auch identifizieren mag, die Module, in die man sie kapselt, sollten unabhängig von einander sein.

Auf das Schichtenmodell bezogen würde das bedeuten: Presentation Layer, Business Logic Layer und Data Access Layer haben keine funktionalen Beziehungen zu einander. Keine. Nicht direkt, nicht indirekt. Sie kennen sich nicht einmal.

Solche Module nenne ich Operationen.

Verbindende Daten

Natürlich können Operationen nicht komplett unabhängig von einander sein. Dann könnte man aus ihnen ja nichts Größeres zusammensetzen. Aber funktionale Abhängigkeiten im Sinne von request/response Dienstleistungsaufrufen gibt es nicht.

Stattdessen kommunizieren die Operationen mittels Nachrichten. Sie tauschen also Daten aus. Und sie können auch Zustand, sogar gemeinsamen haben. Das heißt, Operationen sind von Daten abhängig.

image

In der Objektorientierung können Daten allerdings selbst wieder Funktionen anbieten. Sind Operationen dann nicht von diesen doch funktional abhängig?

Technisch ist das richtig: Logik[3] in einer Operation ruft Logik in Daten auf.

Doch “inhaltlich” ist das für mich nicht gravierend. Die Logik in Daten ist ja dünn. Sie bezieht sich nur auf die Daten selbst, dient nur der Strukturherstellung und Konsistenz. Domänenlogik hat nichts in Daten zu suchen.

Daten sind für mich auf einer Stufe mit APIs. Die müssen Operationen ja auch benutzen können. Daran lässt sich nichts ändern. Am besten deshalb, wenn APIs Black Boxes sind. Dann können sich Operationen nicht an ihre Interna koppeln. Dasselbe gilt für Daten. Datenstrukturen sind selbstgeschriebene APIs zur Verwaltung von Speicher. Wie eine Liste oder Queue oder ein Baum aus einem Framework, den Operationen nutzen.

Angezogene APIs

Operationen können nicht funktional unabhängig sein. Sie können ja nicht alle Logik selbst schreiben, die nötig ist, um Verhalten zu erzeugen. Sie müssen auf die Schultern von Riesen steigen, um Daten zu persistieren, Grafiken zu animieren, zu drucken, zu komprimieren, zu verschlüsseln, zu kommunizieren usw.

Die Logik von Operationen muss sich deshalb abhängig machen von _API_s aller Art, die solche Dienstleistungen anbieten.

image

APIs kapseln Logik, anderer Leute Logik ;-) Für die nutzenden Operationen sind sie Black Boxes des notwendigen Logik-Übels. Denn wenn sich an der Implementation von APIs etwas ändern, dann muss Operationslogik u.U. nachgezogen werden.

Integrierte Prozesse

Auch wenn Operationen von Daten abhängig sind, kennen sie einander nicht. Es fehlt ihnen der funktionale Zusammenhalt, der nötig für das Gesamtverhalten eines Softwaresystems ist.

Den herzustellen ist nun eine ganz eigene Verantwortlichkeit. Die herauszustellen ist zentral für meinen Architekturmodellvorschlag.

Es braucht eine Instanz, die unverbundene Einzelteile zu einem Ganzen zusammensetzt. Diese Leistung nenne ich Integration.[4] Einzelteile werden in und zu etwas größerem integriert.

image

Für die Integration sind ebenfalls Module zuständig. Doch die enthalten keine Logik. Sie sollen nicht selbst mittel Logik verhalten herstellen, sondern fügen andere Logik (Operationen) lediglich so zu Prozessen zusammen, dass in Summe Verhalten entsteht.

Im Sinne des SRP halte ich das für sehr konsequent. Integration ist eine ganz eigene Verantwortlichkeit, wie Operation zu sein oder Daten zu strukturieren.

Integrationsmodule sind zu diesem Zweck abhängig von Operationen, d.h. sie kennen sie, um sie “zusammenstecken” zu können. Doch eine funktionale Abhängigkeit besteht nicht. Denn Integrationen enthalten keine Logik.

Die IODA Architektur

Haben Sie es bemerkt? Die Module, von denen ich bisher gesprochen habe - Integration, Operation und auch Daten -, haben keinen inhaltlichen Bezug mehr. Das Architekturmodell, welches ich vorschlage, interessiert sich nicht dafür, ob sie ein Modul View nennen oder Business Logic oder Adapter oder Portal oder Use Case usw.

Die Verantwortlichkeiten des Architekturmodells sind orthogonal dazu. Und sie sind viel universeller.

Ich nenne es die IODA Architektur, weil damit seine Bestandteile in Richtung der Abhängigkeiten benannt sind.

image

In dieser Architektur gibt es Abhängigkeiten. Ohne Kopplung kein Verhalten erzeugt durch mehrere Beteiligte. Aber die sind unterschiedlich stark funktional. Vor allem fehlen aber funktionale Abhängigkeiten zwischen den “Arbeitspferden”, den Operationen. Sie tragen die Logik-Last eines Softwaresystems.

Auch diese Struktur ist wieder rekursiv zu verstehen. Jedes Modul kann, wenn seine Aufgabe zu umfangreich wird, wieder nach IODA zerlegt werden. Das gilt insbesondere für Operationen.

image

Zusammenfassung

Das unausgesetzte Problem des unwartbaren Codes wird nicht gelöst, in dem wir inhaltliche Module immer wieder neu in funktionale Abhängigkeiten verstricken. Ich bin davon überzeugt, wie müssen diesen ausgetretenen Pfad verlassen. Das Pferd der funktionalen Abhängigkeitshierarchien ist totgeritten.

Stattdessen müssen wir uns auf grundlegende und universelle Verantwortlichkeiten besinnen und die weitgehend unabhängig halten. Das geschieht durch “ausbluten” von Datenstrukturen, d.h. Modulen, die Daten sind. Sie dürfen nur minimale Logik enthalten. Und das geschieht durch die Konzentration von Logik in Operationen, die einander nicht kennen (vgl. Prinzip der gegenseitigen Nichtbeachtung).

Dem Metamodell von Software muss das, was getan wird, egal sein. Welche Operationen, welche Daten es gibt, ist unwichtig. Aber dass es Operationen und Daten gibt, dass Operationen durch spezielle Instanzen zu einem Ganzen integriert werden, das kann dem Metamodell nicht egal sein.

So entsteht Software als Summe von Prozessen, deren Schritte in Operationen implementiert sind, die Daten konsumieren und produzieren unter Zuhilfenahme von APIs.

Wenn das keine allgemeingültige Beschreibung jeder Art von Software ist, dann weiß ich auch nicht mehr.


  1. Für mich sind Module Container für Code zur Entwicklungszeit zum Zwecke der Entkopplung. Logik in Modulen zusammenzufassen kapselt sie, um höhere Wandelbarkeit zu erzielen. Mit “Modul” meine ich kein Sprachkonstrukt einer speziellen Programmiersprache, sondern jedes Mittel, um Logik zu bündeln und Teile von ihr von außen unzugänglich zu machen. Unterprogramme sind insofern genauso Module wie Klassen oder Bibliotheken. ↩

  2. Mit dem Schichtenmodell oder MVC ist eine solche Zerlegung nich möglich. Mit der Hexagonalen Architektur oder auch Onion/Clean Architecture wäre die selbstähnliche hierarchische Zerlegung jedoch denkbar - nur habe ich sie in der Literatur bisher nicht beschrieben gesehen. ↩

  3. Logik sind für mich der Code, der Verhalten herstellt. Das sind Transformationen/Ausdrücke, Kontrollanweisungen und Hardware-/API-Zugriffe. ↩

  4. Manchmal denke ich auch, es handelt sich um Komposition oder Koordination. Aber “Integration” war irgendwie zuerst als Begriff in meinem Kopf, so dass ich ihn nun stehenlasse. ↩


wallpaper-1019588
[Comic] Batman: City of Madness
wallpaper-1019588
Hyakushō Kizoku: Anime erhält eine dritte Staffel
wallpaper-1019588
Arcanadea: Figuren-Franchise erhält einen TV-Anime
wallpaper-1019588
Gintama: Neuer Teaser zum Spin-Off-Anime veröffentlicht