Alle Wege führen nach Rom – IODA-Architektur mit XtendFlow am Beispiel

Roman and Arabic Numerals - Copyright Maureen Barlin Conceptual Art by Cildo Meireles, Madrid – Copyright Maureen Barlin, published under CC BY-NC-ND 2.0 here

Nun habe ich endlich mal Zeit gefunden, eine echte nicht-triviale, wenn auch einfache, Implementierung mit XtendFlow zu realisieren. Ich nehme dabei wieder mal, wie so oft, einen Artikel von Ralf Westphal als Vorlage. Er hat in seinem Artikel „IODA Architecture by Example“ für die leicht erweiterte Code-Kata Roman Numerals eine IODA-Architektur designt und in C# realisiert.

Etwas Historie

Dies hat als erste XtendFlow-Implementierung auf zweifache Weise seinen Reiz. Zum ersten nimmt mir dies Design-Arbeit ab und ermöglicht einen direkten Vergleich zwischen einer C#-basierten und einer XtendFlow-basierten Implementierung, die beide auf dem selben Design basieren. Zum zweiten ist dies die Gelegenheit mal in den eigenen Archiven zu wühlen. Denn meine allererste „beauftragte“ Implementierung beschäftigte sich kurioserweise genau mit der Konvertierung arabischer in römische Zahlen.

Cover der Arbeit "Programm zur Umwandlung arabischer in römische Zahlen"

Implementiert hatte ich dies damals, 1987 am Ende der 9. Klasse, zusammen mit einem Freund als Abschlussarbeit der außerunterrichtlich angebotenen Arbeitsgemeinschaft „Computerclub“. Die Arbeitsgemeinschaft verdankte unsere Schule dem glücklichen Umstand in der Nähe von Studentenwohnheimen gelegen zu sein, die für den universitären Betrieb sogenannte Computer-Kabinette hatten, jedes ausgestattet mit mehreren Klein-Computern des Typs KC85/2. Diese liefen, angetrieben von einem Z80 (U880), mit einem eigenen Betriebssystem, das ein eingebautes BASIC mitbrachte.

An einen Tutor kann ich mich nicht erinnern, so dass wir uns die notwendigen Programmierfertigkeiten autodidaktisch beibrachten. Objektorientierung gab es damals für uns noch nicht und so ist das einzige Design-Prinzip, an das ich mich heute noch erinnere, das EVA-Prinzip. Historisch gesehen interessant, denn es wurde von Flow-Design aus dem Nischendasein auf den untersten Abstraktionsebenen, zuständig die Ein-und Ausgaben von Methoden und Funktionen zu beschreiben, hervorgeholt und kam als grundlegendes Prinzip zur Beschreibung des Datenflusses zwischen Funktionseinheiten zu neuen Ehren. Ein schönes Beispiel für das dialektische Prinzip der Negation der Negation.

Aber zurück zur IODA und Flow-Design…

Integrationen

Mein Anspruch war, das von Ralf entwickelte Flow-Design möglichst direkt aus den Flow-Diagrammen umzusetzen. Schließlich sollte sich der Vorteil der internen DSL, die in XtendFlow realisiert ist, auszahlen. Der Startpunkt des Designs ist bei Ralf die Funktionseinheit convert mit folgendem Flow-Design (Diagramm entnommen aus Ralf Westphal's Artikel „IODA Architecture by Example“):

Flow Diagram Diagram inkludiert von http://geekswithblogs.net/theArchitectsNapkin/archive/2015/05/02/ioda-architecture-by-example.aspx

Die Implementierung erfolgt bei Ralf wie bei mir in der Klasse Head. Die Verbindung der Funktionseinheiten folgt in Xtend direkt dem Diagramm und wird im Konstruktor spezifiziert:

XtendFlow Code Snippet der Funktionseinheit - Head Flow Definitionen

Das lässt sich erst mal gut an… Das Diagramm ist direkt aus dem Code ablesbar, so ähnlich wie bei den textuellen Flow-Spezifikationen der NpantaRhei-Runtime.

Zoomen wir eine Stufe tiefer. Die „business logic“ ist hier wie dort in der Klasse Body implementiert, deren Flow im folgenden zu sehen ist (Diagramm entnommen aus Ralf Westphal's Artikel „IODA Architecture by Example“):

Flow-Diagramm der Funktionseinheit convert mit höherem Detailgrad

Der zugehörige XtendFlow liest sich stringent, der Wiedererkennungseffekt ist groß:

XtendFlow Code: Body Flow Declarations

number ist der Input-Port, result und error die Ausgabe-Ports der integrierenden Funktionseinheit Body, die im Flow der Funktionseinheit Head oben als Variable convert referenziert wird.

Der Aufbau der Datenflussverbindungen erfolgt bei XtendFlow grundsätzlich in den Konstruktoren und damit zum Zeitpunkt der Instanziierung der Funktionseinheit als Objekt. Während im C#-Projekt der Datenfluss zum Zeitpunkt des Aufrufs der integrierenden Funktion aufgebaut wird, wie es in der Methode Convert der Klasse Body gut zu sehen ist.

In XtendFlow sind die Ein- und Ausgabeports der Funktionseinheit per Annotation im Kopf der sie repräsentierende Klasse deklariert. Die Funktionseinheit selbst ist mit der Annotation @Integration als integrierende Funktionseinheit markiert:

XtendFlow Code: Body Port Declarations

Zugegeben, die Port-Deklaration der Xtend-Funktionseinheit ist schwerer zu erfassen als die Methodensignatur in C#.

Convert Method Signature of C# Project

Aber die Annotationssyntax von Java, die von Xtend wiederverwendet wird, ist eben sehr gesprächig. Das wird sich auch bei den Operationen bemerkbar machen…

Operationen

Ich greife mal die Operation convert to roman beispielhaft heraus. Um sie mit anderen Funktionseinheiten über Ports verbinden zu können, müssen natürlich ihre Ports definiert werden:

XtendFlow Code: ConvertToRoman Port Declaration

Die Implementierung der Verarbeitung der Eingangsdaten – hier der arabischen Zahl – sind dann sehr ähnlich, nur dass in XtendFlow der Methodenname an den Port-Namen gebunden ist und die Resultatsliste unter Ausnutzung der Erweiterungsfunktion join zusammengefügt und dem Port output übergeben wird:

Comparision of XtendFlow and C# Code Regarding Processing Input

Vielleicht ist hier noch Optimierungsbedarf bei XtendFlow: Eine Annotation, die eine Methode in eine Funktionseinheit hüllt und Port-Namen vergibt, über die die Ein- und Ausgaben der Methode in Flow-Geflechte eingebunden werden können, wäre in diesem Fall denkbar und könnte bei kleineren Methoden, die als Funktionseinheiten genutzt werden sollen, generell hilfreich sein. Folgendermaßen könnte das dann aussehen:

XtendFlow Code: Method Annotation Declaring Function Unit Name and Port Names

Jedoch wäre diese Möglichkeit auf Methoden mit genau einem Parameter und damit einem Eingangs-Port beschränkt. Bei mehr als einem Eingangsport macht es mehr Sinn die XtendFlow-DSL zu benutzen.

Projektstruktur

Einigen Aufwand habe ich getrieben, um die Projektstruktur des XtendFlow-Projektes der von Ralfs C# Projekt nachzuempfinden. Dies dient vor allem der besseren Vergleichbarkeit und Nachvollziehbarkeit. Hier sind die beiden Projektstrukturen gegenübergestellt.

Picture Showing a Xtend and C# Project Structure Comparision, Screenshot from IDEs

Insgesamt ging die Implementierung mit XtendFlow gut von der Hand. Ralfs Flow-Design-Zeichnungen ließen sich praktisch direkt in XtendFlow umsetzen. Die XtendFow-Annotationen sind manchmal zu sperrig, um damit auch kleinste Funktionsbrocken als Funktionseinheiten umzusetzen. Ich denke, hier stoßen interne DSLs an ihren Grenzen. Weiter trägt dann nur noch die Umsetzung der Flow-Design-Konzepte in einer eigenen flow-orientierten Programmiersprache. Mit Xtext ist dies vielleicht gar nicht so schwer zu realisieren.

Nachtrag

Die Sourcen dieses Projektes sind auf GitHub zu finden.