Ein Dialog über Software-Kategorien und zur Frage "Wer darf mit wem?"

Nein, das ist kein Bild von Gipf-Oberfrick sondern...
Beitrag erstellt am: 29.11.21
Wer hier regelmässig rein schaut, kennt das Thema Software-Kategorien (hoffentlich) und weiss: Ich halte ART-0 für eine der nützlichsten Werkzeuge für eine belastbare Komponentenbildung.

Hier noch einmal die Übersicht mit den möglichen Kategorien und den grundsätzlich erlaubten Abhängigkeiten:

ART-0

ART-0 Software-Kategorien und die erlaubten Abhängigkeiten
Wer nicht mehr sicher ist, was die einzelnen Kategorien bedeuten, findet die Details im folgenden Artikel: Modulbildung: Strukturierung des Systems auf der Basis von Software-Kategorien.

Die obige Grafik zeigt grundsätzlich, welche Komponenten welcher Kategorie auf Komponenten einer anderen Kategorie zugreifen dürfen, sie beantwortet jedoch nicht alle Fragen.

Darf beispielsweise eine R-Komponente eine andere R-Komponente nutzen? Und wie sieht es mit einer T-Komponente aus: Darf eine T-Komponente eine beliebige andere T-Komponente direkt nutzen?

Genau diese Fragen sind heute in einem Teams-Chat mit einem guten Freund und Team-Kollegen aufgetaucht. Er ist aktuell dabei, die Architektur eines Systems strukturell zu bereinigen, und wir hatten uns über die Rolle von R-Komponenten unterhalten. Nach unserem Austausch, habe ich mir folgenden Dialog vorgestellt:

X: Ich nehme an, dass R problemlos andere R inkludieren darf?! Oder anders: Komponenten der gleichen Kategorie darf man immer inkludieren.

Ich: Betr. gleicher Kategorie: Nicht zwingend!

X: Das heisst?

Ich: Grundsätzlich gilt: Die Wurzelkategorie 0 darf von allen inkludiert werden. Hier ist dann die wichtige Frage: "Was zählen wir im System zur Kategorie 0?"

X: Ich dachte, das ist klar: Alles, was gut getestet und stabil ist. Also z.B. alle Standard-Bibliotheken des Java SDK.

Ich: Ja und nein! In Java würde ich beispielsweise das Collection-Framework zur Kategorie 0 zählen, nicht aber Swing oder JDBC. Das sind Komponenten der Kategorie T.

X: Das aktuell betrachtete System basiert auf C++...

Ich: Du hast Java erwähnt... ;-)

X: Ja, ich weiss...

Ich: ... In deinem Fall wären Qt als technische Bibliothek für das GUI oder die JSON-Strukturen, die Domänendaten transportieren, zwei Beispiele für Komponenten, die man versehentlich zur Kategorie 0 zählen könnte, obwohl es sich um T- bzw. R-Komponenten handelt.

X: Verstanden. Was ist mit Datentypen wie String oder Int? Die gehören doch sicher zur Kategorie 0?

Ich: Ja. Sonst könnten wir nichts Sinnvolles tun, und wir wären gezwungen, alles selber zu machen. Komponenten der Kategorie 0 helfen uns ja dabei, Komponenten nicht kompatibler Kategorien zu verbinden. Allerdings lohnt es sich, den breiten Einsatz dieser sog. Omnitypes kritisch zu hinterfragen, Stichwort: primitive Obsession und string-basierte Programmierung. Hier lohnt sich aus meiner Sicht ein Blick auf die Idee der Domain Primitives.

X: Verstehe. Wie sieht es mit den weiteren Kategorien aus?

Ich: Die Mischkategorie A-T ist verboten. Das heisst, wir vermengen nie und niemals technische und fachliche Belange. Leider passiert das schnell, wenn man nicht gehörig aufpasst. Hier hilft uns jedenfalls das DIP.

X: Da kommen dann die R-Komponenten ins Spiel!

Ich: Korrekt! R-Komponenten vermitteln zwischen Komponenten nicht kompatibler Kategorien, immer dann, wenn ein Element der Kategorie 0 nicht ausreicht.

X: Ist das auch für zwei Komponenten der Kategorie A so?

Ich: Bei der Beantwortung dieser Frage helfen uns die strategischen Muster im Domain-driven Design: Es gilt, sich bewusst für die gewünschte Form der Zusammenarbeit zu entscheiden. Solange die Komponenten zum gleichen fachlich abgegrenzten Kontext gehören, ist eine direkte Verbindung unproblematisch. Selbstverständlich klar gerichtet und ohne zirkuläre Abhängigkeiten.

X: Ja, diese Herausforderung verschwindet auch mit Software-Kategorien nicht...

Ich: Stimmt. Es gibt jedoch durchaus ein paar Tricks, um das besser in den Griff zu bekommen. So hilft beispielsweise die folgende Regel, fehlerhafte bzw. unnötige Abhängigkeiten zu erkennen bzw. zu vermeiden: Ein Modul oder Paket, das ein anderes Modul oder Paket importiert, sollte den Grossteil der importierten Abstraktionen (Datenstrukturen und Funktionen) nutzen. Aber das ist eine andere Geschichte.

X: Also zurück zur Verbindung von zwei fachlich abgegrenzten Kontexten: da gibt es doch den Anti-Corruption-Layer (ACL), um zwei fachliche Kontexte zu verbinden...

Ich: Genau! Sobald wir zwei fachlich abgegrenzte Kontexte haben, müssen wir uns entscheiden. Am flexibelsten ist in der Tat ein Anti-Corruption-Layer, also eine R-Komponente, die zwischen den Kontexten vermittelt, wiederum auf Basis von DIP und der Idee der Verwendungsschnittstelle. Hier müssen die beiden Kontexte nichts voneinander wissen. Grundsätzlich kommen aber auch die anderen strategischen Muster in Frage.

X: Und wie sieht es mit R-Komponenten aus: dürften R-Komponenten andere R-Komponenten verwenden?

Ich: Ja, sonst könntest du kein System überhaupt aufsetzen. In deinem System gehört main() zur Kategorie R und nutzt zahlreiche andere R-Komponenten, die den Glue-Code enthalten, mit dessen Hilfe wir unser System als Ganzes zusammen setzen.

X: Dann bleibt noch die Verbindung T-T: Darf ich das?

Ich: Das kannst du machen. Ich gebe aber zu bedenken, dass ein Entwickler einer T-Komponente sein T durchaus als A sehen darf. Und dann gelten für ihn und seine Komponente wiederum die gleichen Regeln wie oben. Auch was das Preisgeben von fachlichen Typen dieser Komponente angeht.

X: Das heisst?

Ich: Wenn ich eine technische Bibliothek bereit stelle, und ich biete interne Datentypen als Argumente und Rückgabewerte in meiner API, dann beschränkt dieser öffentliche Vertrag die interne Weiterentwicklung meiner Lösung: ich kann diese Datentypen nicht mehr einfach ändern.

X: Also eine schlechte Idee!

Ich: Wenn ich von meinen Clients unabhängig bleiben möchte, ja. Das gilt für jede Komponente, übrigens auch in einem ereignisbasierten System: Sobald ich interne Domänenereignisse nach aussen publiziere, verliere ich die Möglichkeit, diese Ereignisse innerhalb des fachlichen Kontextes einfach weiter zu entwickeln.

X: Das leuchtet mir ein. Mir jedenfalls haben es die R-Komponenten angetan. Das ist ziemlich schick!

Ich: Ja, und sie sind für eine korrekte Umsetzung von DIP zwingend!

X: Dazu musst du mir bei Gelegenheit nochmals mehr erzählen...