Historische Interaktionen

Die erste Frage bei der Überführung von Anforderungen in Code sollte den Interaktionen gelten: Welche Interaktionen braucht ein Softwaresystem? Das bedeutet, welche technischen Ereignisse [1] sind domänenrelevant und müssen verarbeitet werden?

Im einfachsten Fall bedeutet das, hinter jedem Menüpunkt und jeder Schaltfläche steht eine Interaktion. Löst der Anwender sie aus, soll im Softwaresystem etwas passieren.

Für mich beginnt die Softwareentwicklung also mit einer Diskussion über die Schnittstellen des Softwaresystems zur Umwelt. Dort finden Interaktionen mit Benutzern statt - seien das Menschen, andere Software oder Maschinen. Von dort treibe ich den Softwareentwurf nach innen weiter: outside-in design. Denn alle Strukturen im Softwaresystem müssen einem Bedarf in der Umwelt dienen, der über eine Schnittstelle vermittelt wird.

Wie der Entwurf nach Fund einer Interaktion dann bis zum Code fortschreiten kann, will ich hier nicht thematisieren. Von mir aus kann man das streng mit Test-Driven Design versuchen.

Entscheidend ist vielmehr, dass Interaktionen einen idealen Ansatzpunkt für jegliches weiteres Vorgehen bieten. Jede Interaktion kann nämlich als Funktion codiert werden [2].

Beispiel Benutzeranmeldung: Die Benutzeranmeldung erfolgt über einen Dialog, in dem der Anwender seinen Benutzernamen und ein Passwort einträgt. Dann drückt er die Schaltfläche “Anmelden”, um die Authentifizierung und Autorisierung anzustoßen.

Die Schaltfläche steht für die Interaktion “Anmelden”. Und die Interaktion kann als Funktion bool Anmelden(string benutzername, string passwort) realisiert werden.

Alternativ könnte die Interaktion durch einen Menüpunkt oder einen Tastatur-Shortcut ausgelöst werden. Das technische Ereignis ist letztlich unwichtig. Wichtig ist, was daraufhin passiert, das Verhalten des Softwaresystems. Und das kann komplett in einer Funktion gekapselt werden. Immer.

Soweit der Blick mit unbewaffnetem Auge.

Interaktionen unterscheiden

Jetzt aber die Lupe zur Hand genommen. Da zeigen sich nämlich Unterschiede in den Interaktionen bzw. Interaktionsfunktionen.

image

Reinheitsgrad: Ich denke, es lohnt sich die Unterscheidung zwischen reinen und unreinen Interaktionen. Reine Interaktionen arbeiten nur auf Daten, die ihnen übergeben werden. Beispiel: Wenn ich in einem Dialog einen Text eingeben kann, der auf Knopfdruck umgekehrt wird, damit ich sehen kann, ob ein Palindrom vorliegt, dann steht dahinter eine reine Interaktionsfunktion. Der oben skizzierte Anmeldedialog hingegen stößt eine unreine Interaktionsfunktion an. Mit Benutzername und Passwort allein kann nicht authentifiziert werden; dazu ist mindestens noch ein Verzeichnis von Benutzern nötig.

Verhaftungsgrad: Unreine Interaktionen zerfallen für mich darüber hinaus noch in solche, die zeitlich verhaftet sind - und solche, die quasi nur im Moment leben.

Zeitlich verhaftete Interaktionen benötigen für ihre Arbeit ein Gedächtnis. Ihnen ist die Vergangenheit wichtig - zumindest in Form eines akkumulierten Zustands. Man könnte auch sagen, sie seien historisch determiniert. Denn ihr Verhalten hängt nicht nur vom aktuellen Input ab, sondern von vorherigem Verhalten des Softwaresystems.

Der Anmeldedialog ist dafür ein Beispiel. Ob er einen Benutzer erfolgreich authentifiziert oder nicht, hängt davon ab, welche Veränderungen vorher am Benutzerverzeichnis vorgenommen worden sind.

Anders ist das bei Interaktionen, die zwar auch nicht nur auf ihrem Input arbeiten, sondern weitere Ressourcen brauchen. Doch da ist deren Geschichte nicht wichtig. Sie wird nicht in die Verarbeitung mit einbezogen.

Das ist zum Beispiel der Fall, wenn eine Interaktion etwas druckt oder Input mit der aktuellen Systemzeit vergleicht.

Zugriffsart: Unreine Interaktionen, die nur im Moment leben, können auf ihre Ressourcen lesend oder schreibend zugreifen. Sie interessieren sich nicht für die Vergangenheit, also dürfen sie Zustand auch einfach verändern. An der Ressource ist anschließend nicht abzulesen, in welchem Zustand sie vorher war.

Aber wie ist das bei unreinen verhafteten Interaktionen?

Ich glaube, hier sollten wir umdenken. Bisher ist dort auch lesender und schreibender Zugriff erlaubt - ohne dass vorherige Zustände erhalten blieben.

Aus Effizienzgründen war das bisher so. Wir konnten nicht anders oder haben das zumindest geglaubt. Doch es hat immer schon der Natur dieser Interaktionen widersprochen.

Das haben wir schmerzvoll erfahren, wenn wir Mühe hatten undo/redo zu implementieren oder Daten mit Geltungsdauern zu verwalten oder historische Daten zu pflegen oder wir uns mit der Versionierung von Datenstrukturen herumschlagen mussten.

Wenn wir nun aber mal von eingefahrenen Mustern Abstand nehmen, dann können wir mit solchen Interaktionen natürlicher umgehen. Das bedeutet für mich, dass bei “historischen Interaktionen” nur lesender und anfügender Zugriff erlaubt sind. Es gibt keinen schreibenden im Sinne von verändernden Zugriff. Denn der würde ja die Geschichte umschreiben.

Die Geschichte, das ist die Liste der Veränderungen, die zum aktuellen Stand der Daten geführt hat. Ja, ich glaube, die sollte unangetastet bleiben. Wir können davon nur profitieren.

Fazit

Nicht jede Interaktion ist zeitlich verhaftet. Wir tun also gut daran, genauer hinzuschauen. Die Unterscheidung zwischen Interaktionsfunktionen mit und ohne Seiteneffekt finde ich jedoch zu einfach. Denn Seiteneffekte können sehr unterschiedlich sein.

Die Ausgabe auf einem Drucker ist etwas anderes, als die Fortschreibung von Unternehmensdaten in einer Datenbank. Event Sourcing scheint mir in dieser Hinsicht ein sehr probates Mittel, um Klarheit zu schaffen.

Interaktionen, die mit Zustand zu tun haben, der sich über die Lebenszeit eines Softwaresystems durch das Softwaresystem entwickelt, sollten sich dafür ein Gedächtnis bewahren. Das ist für mich der neue Default.

Und damit dieser Default nicht über Gebühr verstört, sollte klar sein, wo er gilt. Das sind nur die “historischen Interaktionen”. Alle anderen - auch wenn sie Seiteneffekte haben - können weiter wie bisher gehandhabt werden.

 

PS: Natürlich können unreine Interaktionen auch gemischter Art sein: Sie können aus Wiedergabe und Seiteneffekt oder aus Lookup und Aufzeichnung bestehen usw.

In jedem Fall sollten diese Aspekte aber in eigenen Funktionen gekapselt sein. Das Integration Operation Segregation Principle (IOSP) gilt auch hier.

Endnoten

[1] Technische Ereignisse sind Tastendrücke, Mausklicks/-bewegungen oder Notifikationen von Timern oder Geräten.

[2] Die Schnittstelle zwischen Umwelt und Domäne nehme ich von dieser Funktion aus. Die Interaktionsfunktion kümmert sich nicht darum, woher ihre Input-Daten kommen und was mit ihren Output-Daten gemacht wird. Sie kennt die Schnittstelle nicht, über die sie aufgerufen wird. Das kann ein WPF-Dialog oder ein Webservice sein.

Es ist also nicht Sache einer Interaktionsfunktion, sich Daten aus Eingabefeldern des UI zu beschaffen oder Ergebisse im UI, über das sie aufgerufen wurde, anzuzeigen.


wallpaper-1019588
[Comic] Das Mietshaus
wallpaper-1019588
TOKYOPOP: Shojo-Lizenzen für das Frühjahrsprogramm 2025
wallpaper-1019588
Der Sommer, in dem Hikaru starb – Neue Details zum Anime bekannt + Trailer
wallpaper-1019588
Bogus Skill – Crunchyroll zeigt Anime im Simulcast