Follow my new blog

Donnerstag, 4. Oktober 2012

Gesucht: dreckige Realität

In einem Blogartikel hat Matthias Bohlen seinen Ansatz beschrieben, die Form eines Softwaresystems zu entwerfen. Leider hat mich die Lektüre unbefriedigt zurückgelassen.

Dass Matthias einen anderen Ansatz verfolgt als ich, erklärt das allerdings nicht. Warum sollte mich das auch stören? Der Entwurf hat mich ja nicht in frustriert-sprachloses Erstaunen versetzt. Also erwächst aus der Andersartigkeit seines Ansatzes eher energievolle Spannung. Ich werde seinem Entwurf auch einmal meinen gegenüberstellen. Dabei kann ich wieder für mich etwas lernen. Sie können für sich vergleichen. Und vielleicht ergibt sich daraus auch noch ein konstruktives Gespräch.

Nein, der Ansatz ist es nicht, der mich frustriert hat. Es war eher die Aufgabe. Aber auch wiederum nicht konkret die Bankendomäne, sondern die, hm, Präsentation und der Scope. Beides war so typisch. Nämlich typisch – sorry to say, Matthias – flach.

Ich habe es schon so oft gesehen in Büchern und Artikeln: Da wird eine Technologie oder Methode vorgestellt anhand eines Beispiels, bei dem es einfach funktionieren muss.

Bis zu einem gewissen Grad ist das auch legitim oder sogar nicht zu vermeiden. Auch ich habe schon so Technologien oder Methoden vorgestellt; manchmal liegt es an der Zeit, die zur Verfügung steht. Gerade deshalb bin ich da aber vielleicht auch sensibel. Ich möcht es besser machen für Sie und auch für mich.

Das Problem bei einer flachen Aufgabenstellung, bei Anforderungen, die eine Technologie oder Methode quasi zum Selbstgänger machen ist schlicht, dass man sich in die Tasche lügt. Das kann dem Präsentierenden selbst nicht gefallen. Und es ist natürlich unschön für die Leserschaft.

Ja, das ist es, das hat mich bei Matthias Lösung gestört. Das unselige, weil schon so oft zitierte Überweisungsszenario ist so trivial, dass sich damit ja fast jede Methode erfolgreich demonstrieren lässt. Ein “normal” objektorientierter Ansatz hätte dabei genauso sinnvoll dargestellt werden können wir ein prozeduraler oder ein funktionaler. Ich kann jedenfalls nicht erkennen, dass Matthias’ DCI-Ansatz einem anderen überlegen ist.

Damit will ich keine Aussage über seinen Ansatz machen. Bei der Größenordnung an Aufgabe macht er für mich schlicht keinen deutlichen Unterschied. Er funktioniert. Klar. So wie der “normale” objektorientierte Ansatz auch vorher funktioniert hat.

Was wir bei Matthias sehen ist eine Momentaufnahme eines kleinen Szenarios. Und wenn wir in ein Buch zur Objektorientierung oder zu DDD schauen, dann sehen wir dort auch Momentaufnahmen. Da hat jemand lange an Code gearbeitet, ihn fein ziseliert – und präsentiert das kunstvolle Ergebnis.

Nur ist so leider nicht die Realität. Die ist dreckig und hektisch und nicht so strukturiert in ihren Anforderungen. Außerdem sind die deutlich umfänglicher. Da gibt es Seiteneffekte. Da müssen nicht-funktionale Anforderungen berücksichtigt werden. Und da verändert sich so einiges über die Zeit.

Wir brauchen mehr Demonstrationen von Technologien und Methoden, die es damit aufnehmen. Auch und gerade, wenn es um Methoden geht. Denn ob eine Technologie funktioniert oder nicht, das lässt sich leicht selbst feststellen. Zur Laufzeit gibt es gewöhnlich blitzschnell Feedback. Doch ob eine Methode funktioniert bzw. ob man sie richtig anwendet, das ist viel schwieriger festzustellen. Womöglich stellt sich das erst nach Wochen oder Monaten heraus, wenn man sich damit in eine Ecke gepinselt hat.

Matthias’ Lösung kommt mir insofern “erschlichen” vor. Das Problem ist so abstrakt, isoliert dargestellt, dass sich quasi alles wie von selbst fügt. Erklärungen sind kaum nötig. Jeder nickt sofort, wenn da die Rollen aufgetischt werden… und jeder hält es dann für ganz natürlich, dass ein Konto das andere auffordert etwas zu tun.

Aber das kann doch nicht Matthias’ Ernst sein. In welcher Bankenwelt sollen denn Konten direkt miteinander sprechen? Auch wenn ich kein Banker bin, hat das für mich keinen Rückhalt in der Realität der Bankdomäne. Deshalb scheinen mir die Rollen Quellkonto und Zielkonto “erschlichen”. Wie bei einer petitio principii, wo das zu Beweisende vorausgesetzt wird. Oder so ähnlich wie die Antwort auf die Frage aus Ottos Quizparodie, wer denn der berühmte Maler in Rembrandts Haus sei.

Aber nochmal: Damit will ich Matthias´ Ansatz nicht per se kritisieren, sondern nur sagen, dass ich seinen Wert anhand des flachen Beispiels noch nicht erkennen kann. Wo die Anwendungsfälle so klein sind, dass Objekte und Rollen auf der Hand liegen, kann ich jede Methode rechtfertigen, die Objekte und Rollen favorisiert.

Um mich zu überzeugen braucht es also etwas Größeres und Dreckigeres. Etwas, das nicht schon für eine Methode analysegerecht kleingehackt ist.

Aus diesem Grund habe ich schon vor längerer Zeit in Anlehnung an die kleinen Code Katas größere Application Katas beschrieben. Einige finden sich hier: http://clean-code-advisors.com/ressourcen/application-katas. Technologisch sollten sie keine Herausforderungen darstellen. Aber methodisch kann man sich an ihnen abarbeiten. Deshalb liegen sie in mehreren Iterationen vor.

Wie passt darauf der Ansatz von Matthias? Wie flott segelt er durch diese Anforderungen? Wie leicht lässt sich eine DCI-Struktur über die Iterationen verstehen und verändern?

Diese Fragen gelten aber natürlich nicht nur für Matthias. Jeder, der eine Methode für die Softwareentwicklung im Ärmel hat, kann sich daran probieren. “Normale” Objektorientierung genauso wie Funktionale Programmierung oder der de facto Stil eines Teams. Ich selbst demonstriere Flow-Design immer wieder an solchen Beispielen im Rahmen von Artikeln oder im Seminar “Agile Architektur” der Clean Code Developer Akademie.

Doch die AppKatas sind weit weg von Matthias’ Aufgabenstellung. Da mag es schwer fallen zu sehen, was ich mit “mehr dreckige Realität” meine. Deshalb hier meine Version seines Überweisungsszenarios, als ganze Anwendung mit einem UI und persistenten Daten.

Application Kata - Banküberweisung

Auch das ist natürlich immer noch eine Spielzeugvariante dessen, was in Banken tatsächlich läuft. Aber es sind zumindest mehr Facetten darin enthalten, so dass es schwieriger wird, seinen Entwurfsweg zu finden. Die Entscheidungen sind nicht vorgekaut. Ein wie immer geartetes Modell – um das es ja beim Entwurf geht – liegt nicht auf der Hand.

Also, das ist eine Art von Herausforderung, wie ich sie häufiger formuliert und dann angegangen sehen möchte. Alles andere ist Spielkram.

 

PS: Wer sich beklagen möchte, in der Aufgabenstellung seien zu viele Dinge nicht ausformuliert und deshalb sei es schwierig zu entwerfen, der kann gern in den Kommentaren hier Fragen stellen. Dann konkretisiere ich als “Kunde” :-)

Kommentare:

Matthias Bohlen hat gesagt…

Hallo Ralf,

danke für diese Steilvorlage! Ich habe DCI genau so einfach vorgestellt wie Jim und Trygve, weil ich wollte, dass jeder einzelne Schritt genau aus dem vorherigen hervorgeht. Man muss das als Leser regelrecht sehen können. Erst dann sollte man zu dreckigeren Beispielen übergehen, was wir jetzt auch gern tun können!

Schauen wir erst mal, was denn an Deinem Beispiel wirklich so viel anders ist! Spielen wir mal "breaking the model". Ich sage Dir, was mir als Modell im Kopf herumgeht, und Du sagst, was daran nicht mit Deinem übereinstimmt.

Also:

Zunächst, DCI setzt ja erst später an, wenn das GUI schon vorbei ist. Für's GUI gibt es MVC (auch von Trygve, nur wesentlich älter). Ich gehe also davon aus, dass alle GUI-relevante Funktionalität zunächst per MVC abgefrühstückt wird, so lange bis alle Daten beisammen sind und im Kontext drinstehen. Also Kontonummern, BLZen, Namen, Beträge, Texte, etc. Bis dahin ist noch gar kein DCI in Aktion.

Außerdem scheint Dein Use Case mehrere "Gewohnheiten" (= GUI-lose Sub-Aktivitäten, die auf einen Algorithmus abzubilden sind) zu enthalten, nicht wie bei mir nur eine:

1) Kontodaten validieren
2) Deckung de Quellkontos prüfen
3) Zu einer BLZ den Banknamen ermitteln
4) TAN an den Kunden übermitteln
5) Geld überweisen und Konten führen

Ich hatte nur die Nr. 5, Du hast zusätzlich noch Nummern 1 bis 4. OK, das ist nur mehr Masse, kein prinzipieller Unterschied.

Eine weitere Simplifizierung, die ich drin hatte, war die Tatsache, dass es sich um eine interne, synchrone Überweisung handelt. Du lässt Überweisungen zu einer anderen Bank zu. Das ist für DCI zunächst kein Unterschied: Das Zielkonto implementiert dieselbe Rollenmethode "transferFrom", bildet sie jedoch auf Messaging ab (z.B. per Proxy). Der wirkliche Unterschied ist dabei die Asynchronität - es könnte beim Gutschreiben auf das Zielkonto in der anderen Bank noch ein Fehler passieren, der später wieder an die Ursprungsbank zurückgemeldet werden muss. Ist komplizierter, OK, aber ebenfalls noch kein prinzipieller Unterschied.

Ich verstehe insgesamt nicht, warum das "dreckige" Beispiel so viel schwieriger zu entwerfen sein soll als das simple Beispiel. Bisher ist es einfach nur mehr, nichts wirklich anderes.

Ein wirklich *dicker* Unterschied, der durch DCI gemacht wird, entsteht ja erst, wenn ich statt eines Use Cases 10 oder 100 Use Cases auf demselben Domänenobjekt "Konto" implementiere: Beim üblichen Design bestünde dann m.E. die Gefahr, das Domänenobjekt mit Methoden zuzumüllen, die in Wirklichkeit ins Rollenobjekt gehören. Spätestens nach dem dritten oder vierten Use Case würde ich mich dann fragen, wie ich das Domänenobjekt refaktorisieren muss, um es kleiner und eindeutiger zu bekommen. Und wenn ich das Wort "Rolle" dabei nicht im Kopf hätte, würde ich mich schwer tun, ein eindeutiges Split-Kriterium für die Domänenklassen zu finden. Mit DCI aber wäre 100% klar, dass die Domänenklasse erst gar nicht großartig wächst, sondern lediglich 5 bis 100 Rollenklassen entstehen, in die das "Fleisch" der Use Cases reinkommt, sofern es zur Rolle gehört und nicht zur Interaktion zwischen den Rollen (letztere käme in den Kontext, nicht in die Rollen).

Also mach mir am besten *zwei* deutlich verschiedene Use Cases, die sich aber *beide* auf Konten abspielen. Dann schauen wir mal, welche Rollen die Konten darin spielen und ob wir zusätzliche Rollenklassen bekommen werden. *Das* wäre dann für mich der *wirkliche* Unterschied, den DCI hineinbringt.

My 2 cents...
Matthias

Ralf Westphal - One Man Think Tank hat gesagt…

@Matthias: Fein, dass du den Ball annimmst. Aber im Tor sehe ich ihn noch nicht ;-)

Dass du dich auf Punkt 5 in deinem Artikel konzentrierst, ist mir schon klar. Darum dreht sich meine Kritik. Denn aus meiner Sicht ist der Rest eben nicht trivial. Oder anders: jeder einzelne Punkt ist genauso trivial. Doch in Summe entsteht etwas Komplizierteres. Und genau darum geht es.

DCI genauso simpel zu wiederholen wie Coplien/Reenskaug, bringt ja niemanden recht weiter. Das steht sogar der Akzeptanz bzw. Weiterentwicklung entgegen. Wie gesagt: da lügt man sich schnell was in die Tasche.

Außerdem sehe ich es eben gerade nicht als so simpel an, "den Rest" mit "Ach, MVC macht das!" oder "Ach, da muss eben nur der Kontext angereichert werden!" abzutun.

Nicht ohne Grund habe ich die Aufgabe so formuliert. Einerseits in kleinen Happen, so dass sie einfach zu verstehen ist. Damit habe ich ein Stück Analyse vorweggenommen.

Andererseits aber eben auch nicht sooo einfach, als dass die ganze non-MVC (oder allgemeiner: non-UI) Funktionalität in Punkt 5 stecken würde.

Außerdem ist nun Punkt 5 etwas dicker geworden durch die TAN-Prüfung.

Dass ein weiterer Use Case nötig wäre, um dich herauszufordern, sehe ich nicht. Dieser ist eben durch lästige "Gewohnheiten" so dreckig geworden, dass nicht mehr klar ist, was DCI dazu sagt.

Die ist kein rein algorithmisches Problem mehr. Dies ist eins mit Seiteneffekten. Und es ist eins, wo die Domänenlogik nicht einfach einmal aufgerufen wird und alles in einem Rutsch abarbeitet.

Deshalb: Nimm doch die Herausforderung an. Sie scheint dir so einfach, dann sollte die Lösung auch einfach sein. Das UI kann trivial bleiben. Eine Konsolenanwendung reicht völlig, die schrittweise die Abfrage der Überweisungsdaten vornimmt.

Wie wäre das? Wir basteln jeder eine komplette Konsolenanwendung für das Szenario. Und wenn wir die haben, dann können wir über weitere Use Cases und Veränderungen nachdenken.

Ich erkläre mich bereit, dafür einen genaueren UI-Entwurf zu machen und Beispieldaten für die "Datenbanken" zusammen zu stellen. Außerdem würde ich ein git Repo aufsetzen, in dem wir beide unsere Lösungen ablegen.

Und dann schauen wir, ob und welche Unterschiede es in unseren Ansätzen gibt. Vielleicht liegen wir gar nicht so weit auseinander? :-)

Matthias.Bohlen hat gesagt…

Hi Ralf,
OK, lass uns loslegen.

1) Ich konzentriere mich im ersten Schritt auf die Elemente von DCI: Domänenobjekt, methodenlose Rolle, Kontext.
1a) einmal für das, was das System *ist*
1b) einmal für das, was das System *tut*

2) Ich entwerfe damit Gewohnheit für Gewohnheit.

3) Dann gebe ich Dir die Form, die dadurch entsteht, und Du gibst mir Feedback, ob sie Dein mentales Modell *als Kunde* widerspiegelt.

4) Wenn die Form für Dich (in der Rolle Kunde) OK ist, füllen wir sie (jeder für sich, diesmal beide in der Rolle Entwickler) mit Struktur.

5) Wir vergleichen, was dabei herauskommt.

Ist das für Dich gut so? Würde das beweisen, dass DCI nicht nur für Spielkram funktioniert?

Gruß
Matthias

Ralf Westphal - One Man Think Tank hat gesagt…

@Matthias: Fein, dass du mitmachen willst.

Aber um ehrlich zu sein... Mich interessiert als Kunde dein Entwurf nicht :-) Ich kann dir als Kunde sagen, wie die Anwendung funktionieren soll. Und ich will am Ende lauffähigen Code. Ob du zwischen diesen beiden Eckpunkten entwerfen musst... keine Ahnung. Nicht mein Ding als Kunde. Du würdest mir da was von Rollen erzählen und ich würde sagen, "Ich kenne nur Konten, keine Rollen." ;-)

Ich will mit dieser Haltung zwischen Anforderungsanalyse und Entwurf unterscheiden. Interessant ist für mich erstmal nur Letzterer. Wenn du dafür bei den Anforderungen irgendwie beginnst und da Rollen rausliest, dann ist das dein Entwurfsding.

Die Anforderungen sind ja gerade deshalb so einfach gehalten, dass es da keine Analyseabsprache mehr geben muss.

Wenn du während einer Analyse zusammen mit dem Kunden auch über Rollen reden würdest... dann ist das dein Ding. Hat aber nichts mit Entwurf zu tun - aus meiner Sicht. Ist Analyse.

Zwischen beidem gibt es natürlich einen Übergang. Du wirst also im Entwurf Bezug nehmen auf Anforderungen. Klar. "Ich lese in den Anforderungen X, das übersetze ich nach Y im Entwurf."

Nochmal: Das System ist so einfach, deshalb können und sollten wir den Entwurf erstmal von der Analyse trennen. Es braucht kein Analysegespräch. Du machst einfach deine eigene Analyse und leitest daraus deinen Entwurf ab. Je deutlicher ist, warum du auf Entwurfsbestandteil Y kommst, desto schöner.

Und ich werde es genauso halten.

Nur um es uns zu vereinfachen und unsere Entwürfe vergleichbarer zu halten, habe ich vorgeschlagen, das UI näher zu beschreiben. Das mache ich dann mal... Kommt als Aktualisierung des Anforderungsdokumentes. Stay tuned.

Matthias Bohlen hat gesagt…

> Nicht mein Ding als Kunde. Du würdest mir da was von Rollen
> erzählen und ich würde sagen, "Ich kenne nur Konten, keine Rollen." ;-)

Deckt sich nicht mit meiner Erfahrung von Kundengesprächen.
Kunden erzählen mir immer auch von Rollen (tust Du ja auch,
z.B. im Wort "Empfängerbank").

Die Rollen im mentalen Modell des Kunden zu ignorieren, erhöht die Unsicherheit in dem, was rauskommt.

Doch wie auch immer: Nehm' ich Dich als "hard core Fachbereichsmitglied", der eben nix davon hören will. Die gibt's ja auch in nicht allzu geringer Anzahl. Brauchen wir halt u.U. eine Iteration mehr.

Bis dann...
Matthias

Ralf Westphal - One Man Think Tank hat gesagt…

@Matthias: Ich versteh natürlich, was du meinst, wenn du sagst, dass auch Kunden über Rollen reden würden. Aber im Rahmen der Übung finde ich das verwirrender denn nützlich. Und es funktioniert eben auch nicht mit jedem. Eine Voraussetzung dafür, dass die Entwurfsmethode sollte es auf jeden Fall nicht sein.

(Ich sage übrigens nicht, dass du Rollen im "mentalen Modell des Kunden" ignorieren sollst. Ich sage nur, wir müssen darüber nicht diskutieren. Nimm sie gern an, beschreibe sie gern. Wenn du aus den Anforderungen etwas dazu herauslesen kannst, dann tu das.)

Wenn du iterativ arbeiten willst, dann ist das ok. Aber falls du Feedback von mir haben willst, dann interessiert mich nur lauffähiger Code :-) Schneide also gern die Anforderungen klein und nähere dich an. Aber bitte agil, also in Inkrementen.

Ob du mir die zeigst oder das nur in deiner Darstellung machst, ist mir egal.

Ein Repository habe ich bei github angelegt. Beispieldaten liegen auch darin, u.a. knapp 700 KB ganz reale Bankleitzahlen :-)

Matthias Bohlen hat gesagt…

Hi Ralf,

habe mittlerweile die ersten 4 Schritte des Anwendungsfalles implementiert und in Git abgelegt. Bin mit der Struktur jedoch noch nicht zufrieden, der Code muss noch durch einiges Refactoring durch.

Du kannst das Programm starten, indem Du das Shellskript "runProgram.sh" aufrufst. Du brauchst dafür eine JVM und Groovy. Das Programm läuft ohne zu compilieren, das macht Groovy "on the fly" für Dich.

Der 5. Schritt des Anwendungsfalles (Geld überweisen) wird die Struktur nochmal deutlich beeinflussen, denke ich.

Über den Entwurf und die Lösung schreibe ich im Laufe des Oktober noch einen Blogpost.

Hast Du schon deine GUI-Spec?

Bis dann...
Matthias

Ralf Westphal - One Man Think Tank hat gesagt…

@Matthias: Interessant, wie du vorgehst. In der Reihenfolge der Bedienungsphasen. Bin gespannt auf deine Begründung im Blogartikel :-)

Mal schauen, dass ich Groovy auf meinem Mac zum laufen kriege. Im Augenblick muss ich mich aber noch um andere Sachen kümmern. Bitte also nicht ungeduldig werden, wenn meine Lösung morgen noch nicht vorliegt.

Was meinst du mit GUI-Spec? Du hast die Benutzerschnittstellenbeschreibung im Anforderungsdokument gesehen? Mehr ist nicht nötig. Ein GUI mit Fenstern und so macht die Sache nicht spannender, nur technisch aufwändiger.

Matthias Bohlen hat gesagt…

Ah, ich hatte den Abschnitt "UI-Skizze" in den Anforderungen noch nicht gesehen!

Das deckt sich ja gut mit dem, wie ich es implementiert habe - es war ja auch in den Anforderungen schon klar vorgegeben, dass immer erst validiert werden muss und es dann erst weitergehen darf! Das führte zu sehr feingranularen Aufrufen an den Kontext, die ich so nicht erwartet hatte. Ist aber auch O.K.

Wir brauchen tatsächlich keine Fensteroberfläche mehr, denn der Controller des Fensters würde auch nur mit dem Kontext sprechen, so wie es das Command Line UI tut. Insofern ergibt sich für DCI nichts Neues.

Matthias Bohlen hat gesagt…

@Ralf: So, jetzt ist das Geld-Überweisen ebenfalls eingebaut. Im neuesten Commit
https://github.com/ralfw/KataBankueberweisung/commit/660deee7f6d3319e2d37324d4f590e1413b26045
kannst Du die neue Methode geldUeberweisen in der Klasse GeldUeberweisenContext finden.

Das Journal-Schreiben habe ich eingebaut, das Schreiben der Auftragsdatei noch nicht (wäre vom Prinzip her identisch).

Ich denke, ich höre hier zunächst auf (muss mich noch um andere Dinge kümmern). Mir war es wichtig, sichtbar zu machen, wie groß der Unterschied zwischen einem dummen Domänenobjekt wie "Konto" und einem Rollenobjekt wie "UeberweisungsGeldQuelle" ist. In letzterem darf das volle Leben toben. Der Use Case ist das Aufführung, der Kontext ist das Theaterstück, das UI ist die Bühne. Eine Rolle kann wie in einem echten Theaterstück viel Text haben und mit anderen Rollen Beziehung aufnehmen. Die Schauspieler (Domänenobjekte) haben nicht unbedingt dieselben Beziehungen wie die Rollen.

Du als Flow-Design-Anhänger wirst natürlich von allem das Gegenteil behaupten, oder? :-) Ich bin gespannt auf Deine Lösung des Katas!

Demnächst mehr in meinem Blog.
Matthias

Ralf Westphal - One Man Think Tank hat gesagt…

@Matthias: Super! Das ging ja schnell. Ich muss vorher noch an Artikeln arbeiten :-( Aber meine Lösung kommt schon noch. Scharre schon mit den Füßen :-)

Wenn du das Schreiben des Auftrags nicht implementierst, find ich das ok. Aber es wäre schön, wenn du die Methode(n) dafür zumindest als Stubs eintragen und nutzen würdest. Damit man sieht, wo du das ansiedeln würdest. Das gehört für mich schon noch zum Entwurf, würd ich sagen.

Werde ich nun von allem das Gegenteil behaupten? Ich glaube nicht, dass du mein Ergebnis sooo gegensätzlich finden wirst. Wenn du von "dummen Domänenobjekten" sprichst, kann ich das gut nachvollziehen. Ich bin nicht dagegen.

Am meisten werden wir uns aber wohl in der Heransgehensweise unterscheiden. Doch das ist nur zum Teil im Code zu sehen. Klar, mit Flow Design wird er anders als deiner aussehen. Gravierenderes passiert aber in der Zeit, glaub ich. Deshalb ist es auch ganz gut, dass du schon fertig bist. Dann sind deine Commits drin - und anschließend kommen meine. So kann man unsere Schrittfolgen unterscheiden.

Bin gespannt auf deinen Blogartikel. Ich schreibe meinen vielleicht gleich parallel zu Entwurf/Implementation, sozusagen ein bisschen literate programming :-)

Matthias Bohlen hat gesagt…

> Wenn du das Schreiben des Auftrags nicht implementierst,
> find ich das ok. Aber es wäre schön, wenn du die Methode(n)
> dafür zumindest als Stubs eintragen und nutzen würdest.
> Damit man sieht, wo du das ansiedeln würdest.
> Das gehört für mich schon noch zum Entwurf, würd ich sagen.

Die Stubs hatte ich schon drin - steht in der Rollenklasse "UeberweisungsGeldSenke" als Methode ueberweisungsAuftrag()

Bin sehr gespannt, wie es weitergeht.

P.S.: Diese Captchas, die Dein Blog von mir verlangt, wenn ich kommentiere, sind aber schwierig zu erkennen!

Mike Bild hat gesagt…

Hallo Matthias, Hallo Ralf,

ich habe heute die Beschreibung der App-Kata gesehen. Ich verstehe Ralfs Einwände, bin aber über die Komplexität der "neuen" Aufgabe sehr erstaunt. Respekt! In einer Anwendung mit einem exklusiven Ressourcenzugriff ist die Aufgabe bereits schon nicht ohne. In einem kollaborativen oder gar verteilten System, und das sind Banküberweisung nunmal, meiner Meinung nach unglaublich schwierig. Das Konfliktpotential ist gigantisch und in der genauen Anforderungsumsetzung, meiner Meinung nach, so nicht ohne weiteres lösbar.

Selbst im exklusiven Zugriff sehe ich, zumindest mit den vorgeschlagenen Dateien und Datenformaten, keine sinnvolle Chance eines halbwegs konsistenten Zustands der zugesicherten Überweisung bzw. eines Rollbacks im Fehlerfall. Bei mehreren Dateien wäre eine Transaktionen um die Schreibvorgänge nötig. Matthias - das fehlt mir in deinem Code auf jeden Fall. In einem verteilten Szenario müsste es sich gar um eine Art 2 Phase Commit oder verteilte Transaktion handeln. Weg vom oft geträumten Traum "alles geht immer - ist sofort - zu jeder Zeit - an jedem Ort immer verfügbar" ab in eine praxisrelevante Lösung. Es ist und bleibt "Eventual Consistent". Die Anforderungsabteilung muss sich Gedanken um eine wie auch immer geartete Konfliktlösung bzw. Kompensation machen.

Ralf - Was wäre wenn:
Der Überweisungsauftrag geschrieben, aber das Journal oder die Kontoliste nicht geschrieben werde konnte? Auf welche Information kann ich mich im Zweifel Berufen? Welcher Stand wird dem Benutzer dann überhaupt noch angezeigt? Auf welchen Stand kann sich die Geschäftsregel - "Ist das Konto noch gedeckt." - berufen? Schließlich kann ich keine Überweisungen mehr tätigen, wenn da was nicht stimmt.

Besser wäre doch eine atomare Operation für Journal, Abbuchung und Zubuchung. Eine "Wahrheit" in die eine Datei. Dazu müsste man jedoch das Schema und die die Art der Daten-Partitionierung anpassen können. Ralf - geht das denn im Sinne der Anforderung?

Im übrigen lese ich das erste mal Groovy und empfinde den Code als sehr angenehm lesbar. Leider kenne ich mich weder mit DCI noch mit Groovy wirklich aus. Spannend ist der Ansatz allemal, da DCI mit Groovy offensichtlich einen sinnvollen Ersatz zu "Gott-Objekten" mittels des Tricks von Mixins demonstrieren. Weg vom überholten OOP Alltag - das macht Lust auf mehr ;-).

In einem kollaborativen Szenario wäre, z.B. die (zugesicherte) Prüfung des Kontostands während der Eingabe, auf jeden Fall eine härtere Nuss. Genau daran werde ich ich mich mit mal mit einer "Event Centric" Lösung in JavaScript probieren.

In diesem Sinne - Happy Coding! Cheers, Mike

Ralf Westphal - One Man Think Tank hat gesagt…

@Mike: Das sind alles wunderbare Fragen die du aufwirfst. Genau das ist dreckige Realität, die üblicherweise zu kurz kommt.

Wenn Matthias ursprüngliches Beispiel sauber war, dann ist meine Neufassung im Lichte deiner Fragen aber auch nur staubbedeckt :-) und nicht dreckig.

Aber zu mehr als Staub reicht es bei mir erstmal nicht, da ich die Problemdomäne nicht gut genug kenne. Spekulationen, die leicht abgleiten können in sinnlose Maximalforderungen, wollte ich aber vermeiden.

Insofern: Wir können uns alles mögliche ausdenken, wie man das Szenario noch komplizierter/realistischer machen könnte... aber für den Moment reicht mir der Staub erstmal, um unsere Ansätze zu vergleichen.

Wie wäre es allerdings, wenn du, Mike, den Ball aufnimmst und so wie ich ein Anforderungsdokument schreibst? Sinn eines solchen Dokuments ist, unabhängig und vor jeder Entwicklung das Ziel zu formulieren, um sich nicht in die Tasche zu lügen bei Entwurf und Implementation.

Mir ging es um mehr Dreck bei der Funktionalität. Mehr Rauschen, mehr Realität durch UI usw. Dir könnte es um noch mehr Dreck durch nicht funktionale Anforderungen gehen. (Allerdings ist zu bedenken, dass immer noch die Überschrift "Entwurf" ist und nicht Tiefenbohrung bei irgendwelchen Technologien.)

Wie wäre das? Du formulierst eine weitere Iteration. Die kann ja anknüpfen an das, was bei mir steht.

Mike Bild hat gesagt…

Ich hab auch keine Ahnung von Banküberweisungen, finde Deine Anforderungsbeschreibung jedoch sehr gut. Sie entspricht der Realität die ich häufig erlebe. Keine bestens erdachten und formulierten Konzepte mehr. Alles schnell schnell … "Ist ja recht einfach…" klingt es. Doch das schöne an diesem Beispiel ist, es hat unglaublich viele kleine Tücken mit einer kleinen Änderung. Manchmal sind diese dann irgendwie technisch, haben allerdings eine große Auswirkung auf die Vorstellungen einer fachlichen Anforderung.

Ich bin also ganz deiner Meinung. Häufig bestehen die meisten Artikeln aus "Traum"-Beispielen. Macht immer diesen "Na geht doch ganz einfach" Eindruck. Mach hier, nimm das - läuft. Gern nehme ich den Ball auf, denke aber der Satz "Die Anwendung muss beliebig viele parallele Überweisungen ermöglichen." würde schon reichen. Damit ist die Anforderung ein ganz anderes Kaliber. Möglicherweise ist ein ganz anderes Design/Architektur nötig. Genau das ist jetzt spannend - Wie weit geht's den mit der sogenannten Agilität in dieser oder jenen Architektur und Implementierung? Sprechen wir, mit diesem kleinen Satz einer neuen Anforderung, nicht schon von einer ganz anderen "Kategorie/Klasse" von Anwendung?!? Kann, im Falle des Falles, die (komplexe) Domäne gerettet werden? Geht das einfach? Ist die neue Integration einfach? Der Rest geht, meiner Meinung nach, eher in die Richtung - wegschmeißen und neu machen.

Ich versuch mich mal und bin auf eure Beispiele gespannt.

Ralf Westphal - One Man Think Tank hat gesagt…

@Mike: Das ist natürlich eine andere Nummer. Eben noch war die Anwendung eine, die mit Daten auf einer Fileshare zufrieden sein konnte. (Abgesehen von möglichen Konflikten beim Update der Kontenliste.) Jetzt soll belastbarer Mehrbenutzerzugriff möglich sein.

Aber warum nicht? :-)

Bisher war die Spezifikation dieser nicht-funktionalen Aspekte schlecht. Das müsste jetzt deutlich besser werden. Was bedeutet also "Die Anwendung muss beliebig viele parallele Überweisungen ermöglichen."?

Der Client muss immer noch aus BLZ einen Banknamen machen.
Der Client muss immer noch irgendwie bestimmen können, ob der Überweisungsbetrag gültig ist.
Der Client muss immer noch bestimmen können, ob das Senderkonto gültig ist.

Die Masterfrage scheint mir: Wie aktuell muss die Kontenliste mit dem aktuellen Kontostand sein?

Wenn sie superaktuell sein soll, dann muss wohl um den Journaleintrag und die Aktualisierung eine ACID Tx gewickelt werden.

Wenn nicht... dann muss die Kontenliste gar nicht vom Client gepflegt werden (was ja auch realistischer ist), sondern wird vom selben Batch aktualisiert, der auch jetzt schon angenommen die Auftragsliste verarbeitet.

Solange der nicht nur nachts läuft, sondern alle paar Minuten, würde es reichen, pro Sitzung den Kontostand zu cachen und nur dort zu aktualisieren für weitere Prüfungen.

Die ultimative Wahrheit jedoch, wäre immer noch irgendwo auf dem Server, d.h. es kann passieren, dass der Client eine Überweisung autorisiert - sie dann aber doch nicht ausgeführt wird, weil auf dem Server ein anderer Kontostand erreicht ist. (Es können ja Daueraufträge im Hintergrund gelaufen sein.)

Tja... Wie ist das nun, Mike? Wie funktioniert die Bank?

Einstweilen bastle ich weiter am bisherigen Szenario. Mit der neuen Flow Runtime macht das echt Laune.

Matthias Bohlen hat gesagt…

Hi Mike,

schön, dass Du hier mitdiskutierst!

Klar, in einem realen Beispiel müsste hier mittels Transaktionen die Datenintegrität sichergestellt werden. Das würde aber an meinem Entwurf nicht so sehr viel verändern. Um den Kontext herum müsste noch eine Schale, die Transaktionen startet und im Fehlerfall zurückrollt. Und die Persistenz müsste natürlich nicht auf CSV-Dateien, sondern auf einer echten DB mit vernünftigem Persistenzframework laufen. Die Anbindung an die Empfängerbank müsste mit transaktionalem Messaging erfolgen.

Was ich auf jeden Fall (auch ohne Deine Zusatzanforderungen) an dem Entwurf noch ändern würde: Ich habe in meinem Entwurf den Kontext gleichzeitig als Controller für das UI genutzt. Das sollte lieber auseinander geschnitten werden. MVC wäre "vorne" beim GUI, DCI gehört "hinten" auf den Server. Controller und Kontext müssen zwei getrennte Dinge sein, das habe ich beim Implementieren wohl schnell mal übersehen.

@Ralf: Ist Dein Entwurf schon fertig? Bin so gespannt, den Flow-Code zu sehen.

Gruß
Matthias

Mike Bild hat gesagt…

@Matthias: Ich kenne mich wie gesagt nicht mit DCI aus, ist aus meiner Sicht dann allerdings eher ein Software Design Muster und keine "Top-Level" Architektur. Schade, dass so wichtige Architekturthemen wie Integrität, Verfügbarkeit und Performance (CAP) mit Technologie (Distributed Transaction Coordinator, Datenbank Transaktionen) erschlagen werden. Das ist - IMHO - einer der Kernthemen der Architektur und distanziert thematisch vom Thema Design, Entwicklung und Infrastruktur. Klar könnte man jetzt sagen, dass Thema Datenintegrität ist mit Transaktionen gelöst - das hat allerdings Grenzen und befriedigend ist das für mich allerdings nicht.

Dennoch bin ich auf alle Lösungen gespannt und ein Vergleich lohnt damit.

Ralf Westphal - One Man Think Tank hat gesagt…

So, nun hab ich endlich zwischendurch in kleinen Happen auch meinen Code gebastelt. Der ist im Repo. Und demnächst beschreibe ich mal den Entwurf dahinter und darin.

Ist mit Mono entwickelt. Kann man also auch mit Mono auf Linux/OSX laufen lassen. Passende Downloads der Runtime finden sich hier:

http://www.go-mono.com/mono-downloads/download.html

Also: Repo clonen, dann im Verzeichnis ralfw/bin die Anwendung aufrufen mit "mono bankueberweisung.console.exe".

Enjoy!

Matthias Bohlen hat gesagt…

@Mike: Mit DCI habe ich diesmal nicht die Ziele, die Du erwähnst. Für die non-func requirements wie Performance, Integrität, Verfügbarkeit usw. gibt es m.E. gute Lösungen, das ist unser tägliches Brot als Architekten. Ich hatte in meinen Sommer-Blogposts, die Ralf hinterfragt hat, diesmal *andere* Probleme im Sinn:

* sich langfristig aufblähende, unwartbaren Domänenklassen
* unklare Verantwortungsverteilung der Objekte der Geschäftslogikschicht
* Überlastung von Architekten
* Bedeutung von Architektur im Umfeld von Lean/agilen Teams

Den Ansatz eines Form-/Struktur-Splits mit Hilfe von DCI fand ich wegen dieser Probleme so interessant und: DCI ist so technik-unabhängig und macht so wenige Annahmen, dass man für die "üblichen" Probleme andere Ansätze hinzukombinieren kann. Ich suche keine Top-Level-Architekturen, sondern Baukästen, die sich zu Top-Level-Architekturen zusammenstecken lassen. DCI ist wieder so ein Element in meinem Baukasten.

Das erklärt wahrscheinlich, warum Dir an meinem Ansatz etwas fehlt.

Mike Bild hat gesagt…

@Matthias: Vielen Dank! Verstehe ich natürlich.

Ich baue weiter an einer JavaScript-Version.

Danke euch und viele Grüße,
Mike