Follow my new blog

Montag, 8. Juli 2013

Freiheitsgrade nutzen

Wir engen uns beim Umgang mit Daten selbst ständig ein. Das passiert, weil wir uns der Freiheitsgrade und der Unsicherheit, in der wir uns bewegen, nicht recht bewusst sind.

Bei der Funktionalität ist es interessanterweise umgekehrt. Da wollen wir uns keine Tür durch vorschnelle Entscheidung verschließen. Der Grund ist allerdings derselbe: hohe Unsicherheit.

Das Ergebnis ist Software mit fixen Datenstrukturen und durch Allgemeingültigkeit verrauschten Code. Und der Preis dafür ist hoch. Die fixen Datenstrukturen widersetzen sich natürlich die notwendig häufigen Änderungen – denn es ist ja unzweifelhaft, dass wir in hoher Unsicherheit programmieren. Und der Code, der nach allen Seiten offen sein will – und deshalb eher nicht ganz dicht ist ;-) - ist schwer lesbar und noch schwerer veränderbar. Intentionen sind nicht klar zu erkennen und verschmiert über die Codebasis.

Ich sehe aus dieser Situation nur einen Ausweg: Wir müssen auf der einen Seite allgemeiner und auf der anderen spezifischer werden. Schlicht etwas mehr Lockerheit täte uns gut. Im Raum der Freiheitsgrade, die wir haben, sollten wir uns bewusster verorten.

Freiheitsgrad #1: Intention

Bei der Funktionalität bewegen wir uns solange wie es geht im Allgemeinen. Das Resultat sind die allgegenwärtigen CRUD-Benutzerschnittstellen. Weil wir nicht vorhersehen können oder wollen, was Anwender mit Daten tun möchten, lassen wir einfach alles zu. Statt eines Restaurants bieten wir ihnen einen Supermarkt. Selbst ist der Anwender. Soll er die rohen Datenstrukturen doch selbst formen.

Das macht drei Probleme: Erstens zwingt uns das zu frühzeitigen Entscheidungen über Datenstrukturen, weil wir sie ja dem Anwender zur Befüllung vorgeben müssen. Zweitens bindet das uns wie Anwender an diese Datenstrukturen, da sie nun Teil des Kontraktes zwischen Software und Anwender sind. Und drittens senkt das letztlich die Effizienz der Bedienung, weil Anwender selbst ihren Weg zur Bewältigung einer Aufgabe durch Datenstrukturen finden müssen.

Auf der Achse “Funktionale Konkretheit” legen wir uns häufig, viel zu häufig auf der linken Seite fest:

image

Stattdessen sollten wir uns weiter nach rechts orientieren. Die Benutzerschnittstellen sollten spezifischer, fokussierter sein in ihren Angeboten. Statt einem Dialog für 10 Zwecke lieber 10 Dialoge mit einem Zweck. Wir brauchen mehr Benutzerschnittstellen, die konkrete Angebote für die Intentionen der Anwender machen.

Dafür müssen wir die Domäne natürlich genauer kennen. Das klingt nach mehr Aufwand up-front. Aber ich glaube, das Gegenteil ist der Fall – zumindest, wenn wir uns von dem Glaubenssatz verabschieden, dass wir nicht so kleinteilig liefern können, weil spätere Erweiterungen dann schwierig werden.

Die Vorteile von intentionalen oder aufgabenorientierten Benutzerschnittstellen scheinen mir einfach zu groß, als dass wir sie ignorieren sollten. Anwender werden effizienter und wir befreien uns von der Bürde, einem Kontrakt treu zu bleiben, der die Anwender ohnehin nicht interessieren sollte. Ganz zu schweigen davon, dass dann unser Code klarer würde.

Was dann aber tun mit den Daten? Die würden dadurch ja befreit. Wir würden sie ja immer nur durch kleine Gucklöcher präsentieren. Ein Big Picture von Datenstrukturen wäre für keinen Anwender sichtbar. Wir müssten uns also nicht mehr up-front für ein solches Big Picture entscheiden.

Freiheitsgrad #2: Schema

Derzeit starren wir auf Datenbankstrukturen und in-memory Domänenobjektmodelle wie das Kaninchen auf die Schlange. Es ist Heulen und Zähne klappern. Wir haben Angst, uns falsch zu entscheiden bei Strafe, von Ineffizienz verschlungen zu werden.

Das ist ein grauenhafter Zustand, den wir aber gelernt haben, als unausweichlich, ja normal anzusehen. Es kann nicht anders sein. So funktioniert Softwareentwicklung eben. Am Anfang muss man sich halt für Datenstrukturen entscheiden. Und zwar am besten für so wenige wie möglich. “One size fits all” ist sogar das Beste.

Aber auch hier engen wir uns künstlich und unbewusst ein. Wir sollten lernen, ein Kontinuum zu sehen:

image

Wenn wir nach außen hin kein Big Picture von Datenstrukturen mehr liefern müssen, dann können wir uns auch intern davon befreien. Die traditionelle Objektorientierung mit ihrem Fokus auf Daten ist eine Verirrung. Sie ist verständlich, war vielleicht angesichts knapper Hardwareressourcen auch unumgänglich – deshalb müssen wir sie ja aber nicht unnötig fortsetzen.

Auf den Schultern der relationalen Datenstruktureffizienzdenke der 1970er haben wir geglaubt, wir müssten möglichst früh und möglichst spezifische Datenmodelle entwerfen. Das waren wir dem knappen Speicher und der Konsistenz schuldig. Und auch der Funktionalität, die damit scheinbar einfach eine Heimat findet.

Aber was ist das Ergebnis? Entwickler, die an sich zweifeln, weil sie es nicht hinkriegen, Funktionalität zügig zu verorten. Code und Daten, die sich notwendigen Veränderungen widersetzen.

Wie anders könnte aber die Welt aussehen, wenn wir nicht mehr versuchen würden, Daten in “one size fits all” Schemata zu pressen? Geben wir diese Optimierung auf. Sie ist angesichts der Unsicherheit der Anforderungen vorzeitig. Wir haben keine Ahnung, was der Kunde wirklich will und wohin das alles führt? Dann sollten wir genau das auch mit unseren Daten ausdrücken.

Für mich bedeutet das, weniger up-front in Schemata zu denken und stattdessen Daten kleinstteilig in Bezug auf konkrete Funktionalität zu denken. Das Ergebnis sind Events, also Datendifferenzen. Die können hunderte Formen/Schemata haben – und ergeben nur zusammen ein Big Picture. Das existiert jedoch nicht vorab, sondern entsteht im doppelten Sinn erst über die Zeit.

Freiheitsgrad #3: Bevorratung

Kann man denn aber ohne Schema, d.h. ohne “das eine” Schema Software schreiben? Natürlich. Operationen können auch auf Events arbeiten: das ist dann (Complex) Event Processing ((C)EP).

Einen kleinen Eindruck habe ich davon in meinem vorherigen Blogartikel über Tic Tac Toe gegeben. Alle Domänenfunktionen kommen dort ohne eine spielspezifische Datenstruktur aus. Ein zweidimensionales Spielbrett wird ausschließlich für den Anwender hergestellt.

Größere Schemata – ob in-memory oder persistent ist egal – sind deshalb aber nicht “böse”. Sie haben ihren Zweck. Besonders, wenn man genau weiß, wie sie für einen konkreten Zweck aussehen sollten. Dann erhöhen sie Verständlichkeit und Effizienz.

Doch die Frage ist, wann und wie lange sollten Daten in solchen größeren Strukturen vorliegen? Wann ist Zustandsherstellung und –haltung angezeigt im Gegensatz zur Arbeit auf Zustandsänderungen (Events).

Ich denke, das muss im Einzelfall entschieden werden. Womit ich wieder bei den Intentionen bin. Je intentionaler die Benutzerschnittstelle, desto größer unsere Freiheit, uns immer wieder für die beste Art der Bevorratung von Zuständen in fixen Schemata zu entscheiden.

image

Mein Gefühl ist, wie halten Daten zu lange in zu großen Strukturen. Wir stecken in einem Mangeldenken fest. Wie Eichhörnchen bevorraten wir zu viel. Dabei ist die Welt doch schon längst im JIT-Zeitalter angekommen. Waren werden Just-in-Time geliefert und sogar Maschinencode wird JIT in kleinen Happen erzeugt. Warum gehen wir nicht so mit unseren Daten um? Wir haben die Prozessorressourcen, um kleine Datenstrukturen individuell für den aktuellen Anwendungskontext JIT zu füllen.

Schluss mit dem einen Objekt für Kunde, Rechnung, Spiel, Vertrag, Gerät, Fragebogen, Auktion oder was immer die Domäne sein mag. Schluss mit dem einen Schema! Schluss mit der dauerhaften Speicherung in dem einen Schema! Schluss mit der zwanghaften Kombination von Daten und Funktionalität [1].

Stattdessen: Mehr Logik direkt auf Events und auf fokussierte, JIT befüllte Datenstrukturen auslegen. OR-Mapping adé – zumindest für Datenroundtrips. Technologisch einfacher werden, um schneller und flexibler zu sein.

Freiheitsgrad #4: Zweck

Wo kleinere Datenstrukturen JIT befüllt werden, da stellt sich natürlich auch die Frage nach dem Zweck. Folgen Datenstrukturen dem Prinzip, das wir für Funktionalität so hoch halten? Haben Datenstrukturen auch immer nur eine Single Responsibility?

Nein, ich glaube, wir überlasten sie. Wir denken vor allem in Allzwecksdatenstrukturen. Ist ja auch klar: je weniger es gibt, desto mehr Zwecke müssen die erfüllen.

image

Dass solche Wollmilchsäue schnell unwartbar werden, ist kaum verwunderlich. Wenn wir es ernst mit Clean Code meinen, dann müssen wir das Tabu ins Visier nehmen, dass an den großen Allzweckschemata nicht zu rütteln sei. Ob ein Datenbankschema oder ein OO-Domänenmodell, ist egal. Zustand, d.h. akkumulierte Veränderungen, sollte in verschiedenen Formen vorliegen, die auf konkrete Zwecke zugeschnitten sind. Die Unterscheidung zwischen Lesen und Schreiben wie bei CQRS ist da nur ein Anfang. Am Ende kann, darf, sollte es viele verschiedene flüchtige und persistente Datenstrukturen geben.

Und alle sollten sich aus einer Quelle jederzeit re-generieren lassen. Soviel zum Thema Konsistenz. Die Datenwahrheit lebt wie in Zeiten des “Relationnismus” an nur einem Ort. Das scheint mir derzeit ein Event Store oder – das Bild gefällt mir eigentlich besser – eine Black Box.

Fazit

Sie sehen, ich bin derzeit beseelt vom Thema Event Sourcing ;-) Aber es ist einfach so, dass darin für mich vieles zusammenläuft, was bisher getrennt nach einer Lösung gesucht hat. Und allemal sehe ich darin eine Antwort auf die ewig große Frage der Softwareentwicklung: Wie umgehen mit der Unsicherheit und Flüssigkeit der Anforderungen?

Bei all den nicht-funktionalen Anforderungen der Kunden scheint mir das die größte und gleichzeitig die unbewussteste: “Ich möchte mich so wenig wie möglich festlegen müssen.”

In Handwerker- oder auch Ingenieursmanier laufen wir dagegen jedoch Sturm. Seit Jahrzehnten. Es widerspricht allem, was wir in Tausenden Jahren gelernt haben. Sagen wir es denn nicht auch unseren Kindern, “Du musst dich entscheiden lernen!”?

Was aber, wenn man das aus vielfältigen Gründen nicht kann? Dann ist es doch widersinnig, es immer wieder zu fordern und sein Tun darauf auszulegen. Dann sind fixe Strukturen für Daten und Funktionalität kontraproduktiv.

Also sollten wir unsere Freiheitsgrade ausreizen. Sonst unterscheiden wir uns nicht von Fundamentalisten, die ständig auf dem Selben beharren, weil es nur so sein kann weil es immer so war und nur so sein darf.

Lernen wir, über den Tellerrand des bisher Kanonischen hinaus zu blicken. Das braucht natürlich Experiment und Übung. Da werden wir auch scheitern und mal zu weit gehen. Macht aber nichts.Wir können nur gewinnen. Denn eines ist ja klar: So wie es ist, kann es nicht bleiben. Wir ersticken an Inflexibilität, d.h. an Unfreiheit zur Veränderung.

Machen wir uns also auf den Weg. Ich würde sie gern hier sehen:

image

Endnoten

[1] Damit meine ich nicht, dass wir Objekte aufgeben sollten. Dass wir Daten und Funktionalität kombinieren können, soll erhalten bleiben. Ich bin also kein jünger extremer Funktionaler Programmierung. Aber wir sollten uns genauer als bisher überlegen, welche Funktionalität wir mit Daten zusammenfassen.

Funktionaltität, die der Konsistenz einer Datenstruktur dient (Stichwort: Abstrakter Datentyp (ADT)), gehört natürlich zu den Daten. Aber da gibt es für mich eine Hierarchie.

Ein Objekt, dass Name, Anschrift und Telefonnummer einer Person zusammenfasst, ist eine Datenstruktur, die nur wenig Funktionalität verträgt. Zum Beispiel könnte es sicherstellen, dass der Name nie leer ist. Genauso wie ein Stack sicherstellt, dass Pop() das zuletzt mit Push() eingelegte Element entnimmt.

Ein Objekt hingegen, dass ein solches Personenobjekt enthält (!), kann Funktionalität auf einer höheren Ebene haben. Zum Beispiel kann es zuständig dafür sein, dass keine zwei Personen mit derselben Telefonnummer existieren.

Wann jedoch ein Objekt andere enthalten sollte, also einen Zustand haben sollte, auf dem es arbeitet, und wann es andere erhalten sollte, also auf einem Fluss wechselnder Objekte arbeiten sollte, das ist eine andere Frage.

Mittwoch, 3. Juli 2013

Event Sourcing: Vorzeitige Datenstrukturoptimierung vermeiden

Datenstrukturen wohin Sie schauen. Wenn Sie Anforderungen studieren, sucht Ihr Gehirn sofort nach Zusammenhängen und Mustern für Daten. So sind Sie einfach trainiert. Die Objektorientierung scheint das nahezulegen. Der Umgang mit Datenbanken erfordert das allemal.

Ich kenne diesen Reflex jedenfalls genau. Wenn ich vor der Aufgabe stehe, einen Stack zu implementieren, frage ich mich, wie dafür die Daten strukturiert sein sollen. Speichere ich die Einträge in einem Array? Oder benutze ich besser eine Liste? Sollte die Liste einfach oder doppelt verkettet sein?

Oder wenn ich vor der Aufgabe stehe, ein Tic Tac Toe Spiel zu implementieren, dann frage ich mich, wie das Spielbrett intern abgebildet werden sollte. Ist es besser, alle Spielfelder in einem eindimensionalen Array zu halten? Oder sollte ich ein zweidimensionales Array benutzen?

Und falls dazu noch die Anforderung kommt, die Daten zu persistieren, dann mache ich mir Gedanken über das Persistenzparadigma – relational oder dokumentenorientiert oder key-value store usw. – und ein dazugehöriges Schema.

Puh… ganz schön viele Gedanken, die sich um Datenstrukturen drehen. Aber das ist ja auch verständlich, weil es um die eine Datenstruktur geht, quasi das Herzstück der Codes. Denn alle Funktionalität muss damit leben. Da greift man besser nicht daneben, sonst knirscht das Software-Getriebe später.

Dieses Vorgehen scheint alternativlos. So haben wir es schon immer gemacht. So muss man Softwareentwicklung planen.

Oder?

Nein! Ich glaube daran nicht mehr. Historisch gesehen finde ich diese Herangehensweise zwar verständlich – nur müssen wir deshalb ja nicht so weitermachen.

Für mich scheint der “data structure first” Ansatz zunehmend kontraproduktiv. Er versucht gleich zu Beginn der Entwicklung etwas zu optimieren, das sich eigentlich erst über die Zeit ergeben muss. Beim Stack und für Tic Tac Toe mag das noch nicht so offensichtlich sein. Liegen da die Datenstrukturen nicht sehr klar auf der Hand? Aber bei Ihren Anwendungen ist das sicherlich anders. Beweis dafür ist die Bewegung, die über Jahre in Ihren Schemata stattgefunden hat. Die sehen sicherlich nicht mehr so aus wie am Anfang.

Datenstrukturen sind kein Selbstzweck. Wenn Sie eine Datenstruktur planen, müssen Sie vielmehr immer genau im Blick haben, wer deren Konsument ist. Was sind dessen Bedürfnisse? Wissen Sie das aber genau? Können Sie das wissen? Ist das nicht auch eine Form von Big Design Up-Front (BDUF) und ein Fall von Premature Optimization?

Klar, bei Tic Tac Toe ist da natürlich zunächst einmal der Anwender. Der will auf seinem Bildschirm ein zweidimensionales Spielbrett sehen.

Und dann ist da die Domänenlogik. Die muss auch mit dem Spielbrett umgehen. Aber ist für sie ebenfalls eine zweidimensionale Struktur die beste? Vielleicht. Vielleicht aber auch nicht. Herausfinden werden Sie das erst, wenn Sie die Domänenlogik implementieren.

Vielleicht ist es sogar so, dass verschiedene Aspekte der Domänenlogik unterschiedliche Datenstrukturen bevorzugen würden. Braucht die Bestimmung des nächsten Spielers eine zweidimensionale oder eindimensionale Datenstruktur oder gar etwas anderes? Wie steht es mit der Prüfung, ob ein Spieler gewonnen hat? Wie wird am leichtesten festgestellt, ob ein Zug überhaupt gültig ist?

Wenn Sie nach der einen Datenstruktur zur Erfüllung aller Anforderungen an den Entwurf herangehen, befinden Sie sich schnell im Kreuzfeuer vieler “Stakeholder”, d.h. Aspekte. Die Suche nach einem Optimum ist da vergeblich, würde ich sagen. Es kann immer nur ein Kompromiss herauskommen. Die Frage ist nur, wie sehr Sie sich dabei aufreiben.

Warum also nicht Zeit sparen? Schluss mit BDSDUF = Big Data Structure Design Up-Front. Seien Sie schnell statt gründlich – insbesondere wenn die Gründlichkeit ja ohnehin kein stabiles Ergebnis liefern kann. Statt in die Glaskugel schauen Sie auf die ohnehin stattfindende Entwicklung beim Umgang mit Daten und versuchen, Muster zu entdecken. Dann kommen Sie früher oder später schon zu stabile(re)n Datenstrukturen.

Den Plural meine ich hier ernst. Statt sich auf die eine Datenstruktur zu kaprizieren, versuchen Sie mal, mehrere zuzulassen.

Events als Datengranulat

Ich will versuchen, Ihnen konkreter zu beschreiben, was ich damit meine. Als Beispiel soll Tic Tac Toe dienen. Die Verarbeitung eines Zuges könnte so aussehen:

image

Klar ist dabei, womit die Verarbeitung angestoßen wird – durch Meldung der Koordinate des Spielfeldes, auf das ein Spieler einen Stein setzen will – und was die Verarbeitung am Ende als Ergebnis liefern soll: die aktuelle Konfiguration des Spielfeldes sowie den Spielstand (ob es weitergeht oder das Spiel zuende ist durch Gewinn oder Unentschieden).

Alle anderen Daten liegen im Dunkeln. Also könnte es losgehen mit der Spekulation, wie denn “das Spiel” über diese Kette von Verarbeitungsschritten am besten repräsentiert werden sollte, was die eine beste Repräsentation sein sollte.

Was immer Sie nun dazu denken… Ich möchte Ihnen etwas anderes vorschlagen. Wie wäre es, wenn es keine spielspezifische gemeinsame Datenstruktur gäbe? Wie wäre es, wenn es keine Datenstruktur gäbe, die den aktuellen Zustand darstellte?

Stattdessen schlage ich vor, eine Liste von Zustandsänderungen zu führen. Im Falle von Tic Tac Toe ist das ganz, ganz einfach. Diese Zustandsänderungen sind die Züge. Jeder Zug verändert die Konfiguration des Spielbretts und den Spielstand. Das kann als Ereignis (Event) angesehen werden. Und diese Events schlage ich vor zu speichern.

Statt Spielbrett mit Daten für Spielfeldbelegungen…

image

…gibt es eben nur eine Liste von Events:

image

Diese Events beziehen sich zwar auf die Vorstellung eines zweidimensionalen Spielbretts, doch das existiert eben nicht statisch.

Und wie sollen die Verarbeitungsschritte nun vorgehen?

Fangen wir mal einfach an: Für den Spielerwechsel muss nichts getan werden. Es ist kein spezieller Zustand zu führen. Welcher Spieler dran ist, ergibt sich aus der Anzahl der Züge:

image

Das ist auch nur relevant für das Rendering des Spielbretts.

Wie ist es mit der Zugausführung – vorausgesetzt, der Zug ist valide? Das ist ein Einzeiler:

image

Es muss ja nur memoriert werden, welcher Zug gemacht wurde. Kein Spielstein wird auf einem Spielbrett platziert.

Und wie sieht die Validation aus? Die beschränkt sich auf die Prüfung, ob die Koordinate des Zuges schon einmal vorgekommen ist:

image

Falls ja, liegt ein Fehler vor. Nur der Einfachheit halber bricht das Spiel dann mit einer Exception ab.

Jetzt die Spielstandprüfung. Die ist aufwändiger. Aber das wäre sie auch, wenn der Spielbrettzustand vorgehalten würde:

image

Ob ein Unentschieden vorliegt oder nicht, ist schnell entschieden. Wenn alle Felder belegt sind, also 9 Züge gemacht wurden, geht nichts mehr.
Die möglichen Gewinnpositionen werden einzeln geprüft. Züge auf horizontale, vertikale und diagonale Feldreihen werden selektiert. Falls in einer Reihe nur Züge desselben Spielers gemacht wurden, liegt ein Gewinn vor.
Züge von Spieler X werden mit 1 bewertet, die von O mit –1. Eine Gewinnreihe hat dann den Wert 3 bzw. –3.
Zum Schluss ist natürlich doch ein Spielbrett der herkömmlichen Art nötig. Das zeigt das obige Flow-Diagramm ja schon. Allerdings überlasse ich das nicht dem Spielerwechsel – das wäre ein Widerspruch zum SRP -, sondern verpacke es in eine eigene Routine:
image
Bei CQRS spricht man von ReadModels auf der Query-Seite. Und genau darum geht es ja auch hier: eine Datenstruktur, die nur für lesenden Gebrauch bestimmt ist. Dass die jedes Mal neu generiert wird, macht nichts. Auch ein Cache ist eine Optimierung, die hier vorzeitig wäre.
Zur Abrundung noch die Integration dieser Operationen in einer eigenen Methode:
image

Fazit

Ich habe mir die Entscheidung für die eine alleinseligmachende Datenstruktur gespart. Alle Aspekte des Tic Tac Toe Spiels haben für sich entscheiden können, was ihnen am besten taugt. Und wie sich herausstellt, konnten alle mit einer Event Source sehr gut leben.
Das ist insofern bemerkenswert, als dass eine Event Source eine generische Datenstruktur ist. Die sieht in allen Anwendungen gleich aus. Es ist nicht mehr als eine Liste von Events. Zugegeben, die sind bei TTT sehr, sehr simpel. Aber auch wenn Events eigene Klassen sind, ändert sich das Prinzip nicht. Eine Event Source ist und bleibt eine schlichte Liste. Und aus der kann bei Bedarf jede konkretere Datenstruktur generiert werden.
Deshalb bezeichne ich die Events auch als Datengranulat. Sie sind wie kleine Kunststoffkügelchen, aus denen man bei Bedarf allerlei Nützliches formen kann.
Mit einer Event Source bin ich also schneller am Start. Und wenn ich feststelle, dass ich aus den Events die eine oder andere Datenstruktur öfter generiere, also sich ein Muster herausschält, dann kann ich mir Gedanken darüber machen, ob ich dafür einen Cache einrichte. Denn nichts anderes sind die üblichen fein ziselierten Datenstrukturen in unseren Anwendungen.
Ihre Bevorratung dient der Effizienz – und kostet uns Zeit in der Entwicklung und Flexibilität während der Evolution der Software.
Deshalb: Versuchen Sie doch einmal, solche vorzeitige Optimierung zu vermeiden. Geben Sie solchem Event Sourcing eine Chance.

Montag, 1. Juli 2013

Eine Black Box für Software

CQRS hat mich jetzt gepackt. Auf der DWX Konferenz hatte ich Gelegenheit, mich darüber länger mit Jan Fellien auszutauschen. Den Moment, wo ich innerlich “Aha!” und “Wow!” ausrief war, als ich erkannte, was die Aufgabe einer Event Source ist.

Nicht nur ist Event Sourcing für mich die Antwort auf meine Frage nach einem Datengranulat. Denn aus den “Daten-Kügelchen” vieler kleiner Events kann man sich größere Strukturen in immer neuer Weise “gießen”. Es gibt für mich nicht mehr die Frage, ob “das eine Datenschema” für eine Anwendung relational oder dokumentenorientiert oder sonstwie sein sollte. Stattdessen gibt es soviele Schemata und Datenbanken wie man braucht. Und alle werden aus der einen Quelle gespeist: aus der Event Source.

Daten für einen Zweck in einem bestimmten Schema bereitzustellen, kann immer dynamisch aus der Event Source geschehen. On demand. In dem Augenblick, wenn sie benötigt werden. Wem das zu langsam ist, der muss halt die Daten in dem Schema cachen. Doch das ist dann eine bewusste Optimierung.

Mit einer Event Source kann man diese Optimierung dann vornehmen, wenn man sie braucht. Bis dahin ist man frei von lästigen Überlegungen, wie denn ein Schema am besten aussehen sollte. Welche Erleichterung!

Aber dieser Gedanke hatte mich nicht überfallen auf der DWX. Den hatte ich schon vorher. Im Gespräch mit Jan kam mir vielmehr ein sehr mächtiges Bild für die Event Source in den Sinn.

Die Event Source ist die Black Box einer Software.

Ich meine das im Sinne der Flugschreiber, die auch als Black Box bezeichnet werden. Die zeichnen Ereignisse während des Fluges auf. Wenn etwas schief geht, kann man durch Abspielen der Aufzeichnung versuchen, die Ursache zu finden.

Das leistet für mich nun auch eine Event Source bzw. ein Event Store für Software. Alle Domänenevents werden doch gespeichert (record). So kann man den Zustand eines Programms zu jeder Zeit rekonstruieren. Was an Zustand in-memory ist nur eine Optimierung; Zustand in einem Read-Model ist auch nur eine Optimierung. Maßgeblich ist einzig das, was in der Black Box steht.

image

Wenn also ein Programm oder auch nur ein Teil abstürzt, kann es neu gestartet und aus der Event Source auf den letzten Stand gebracht werden (replay). Die Speicherung auch kleinster Veränderungen des Zustands ist ja kein Problem, weil die Events als “Zustandsdifferenzen” ganz simpel und schnell persistiert werden können.

Events kommen aus der Domäne. Da spielt die Anwendungsmusik. Dafür wird Software gemacht.

Die Domäne spielt immer wieder aber auch Events ab, da sie ja nicht ihren kompletten Zustand in-memory halten will.

Andere Konsumenten von Events werden über sie per Notifikation informiert. Die sind also von der Eventquelle entkoppelt. Falls sie jedoch offline waren, können sie sich “verpasste” Events wieder vorspielen lassen, um sich auf den aktuellen Stand zu bringen.

Bei CQRS ist ein typischer Event-Konsument natürlich das Read Model. Es fertigt aus einzelnen Events fixe größere Strukturen, die auf unterschiedliche Abfragemuster zugeschnitten sind.

Ich finde das Bild der Event Source als Black Box sehr eingängig und motivierend. Damit werde ich mich jetzt mal intensiver beschäftigen…

Freitag, 28. Juni 2013

Angst besiegt

Mein “monolithischer Schreibtisch” hatte mich in den letzten Jahren immer wieder in Angst versetzt, wenn der Heizungsableser sich ankündigte. 2012 ist es dann auch zum Desaster gekommen: der Schreibtisch bracht zusammen, als ich versuchte, ihn von der Heizung abzurücken. Darüber habe ich hier berichtet.

image

Seitdem ist einiges passiert. Vor allem bin ich raus aus der Angst. Es hat ein bisschen gedauert, doch schließlich habe ich meinen Schreibtisch so entkoppelt, dass mir die Heizungsablesung in diesem Jahr keine Angst mehr gemacht hat.

Innerhalb von 1-2 Minuten habe ich meinen Schreibtisch von der Heizung abgerückt, so dass der Ableser problemfrei seine Arbeit machen konnte. Ein “Begrüßungsgeschenk” war nicht nötig.

2013-06-24 20.46.31-1

Das Geheimnis meiner Angstfreiheit?

1. Ich habe zusammengebracht, was zusammengehört. Tischplatte und Tischbeine sind nun fest verschraubt. Enge Kopplung, wo hohe Kohäsion herrscht. Das ist konsequent, würde ich sagen.

2. Ich habe die Abhängigkeiten (Kabel) klarer gegliedert. Unnötige Verbindungen habe ich gekappt. Andere so gebündelt, dass sie einfach zu erkennen und beim Abziehen des Tisches leicht zu behandeln sind.

“Clean Office” to the rescue ;-) Ordnung und Struktur: die machen das Leben im Allgemeinen einfacher. Und wer dabei Hilfe braucht, dem empfehle ich die Kontaktaufnahme mit Freundin und Kollegin Andrea Kaden von Zeitgewinn Hamburg, http://zeitgewinn-hamburg.de/ Die hat eine Menge Tipps und Tricks auf Lager für mehr Klarheit in Büro und Organisation. Damit die Konzentration beim Wesentlichen bleibt und nicht von Angst vor Veränderungen aufgezehrt wird.

imageUnd natürlich machen Ordnung und Struktur auch das Entwicklerleben einfacher. “Clean Code” ist das Stichwort. Wer dazu Hilfestellung sucht, der wende sich gern vertrauensvoll an mich :-)

Montag, 10. Juni 2013

Bloggen unter Mac OSX

Bloggen jetzt auch von Mac OSX aus? Das muss ich ausprobieren. Ich habe keine Lust, immer wieder für bequemes Bloggen Windows mit dem Windows Live Writer anzuwerfen. Der macht das Schreiben und Publizieren zwar sehr einfach - doch Windows ist eine Last in dem Zusammenhang.

Aber jetzt bin ich irgendwie auf Byword gestoßen. Eigentlich benutze ich Mou für Markdown und bin damit zufrieden. Doch Byword hat mich mit dem neuen Blogging-Feature gelockt. Das kostet dann zwar nochmal extra… Doch wenn es funktioniert, ist das ein kleiner Preis für den Gewinn an Bequemlichkeit.

Und so habe ich mit jetzt mal Byword zugelegt und versuche mich an einem ersten Blogposting damit.


Einziger Nachteil bisher: Bilder werden nicht unterstützt. Doch da hoffe ich mal auf ein Update. Das wird schon kommen. Wenn ich mit meinem Kauf dazu beitragen kann, dann freue ich mich.

PS: Das Bild von Byword habe ich nachträglich im Blogger Editor reingesetzt :-)

PPS: Publikation funktioniert. Aber wenn ich dann in Blogger das Posting nachbearbeite, sind die Absätze weg. Sehr schade.

PPPS: Ebenfalls nicht so schön: Wenn ich in Byword den Text korrigiere und wieder publiziere, dann wird ein neues Posting angelegt.

Was ist denn so schwer daran, einen Blogeditor wie Windows Live Writer für Mac OSX zu entwickeln? Ich verstehe das nicht...

Donnerstag, 30. Mai 2013

Daten als systemrelevante Größe behandeln

Systemrelevante Größen sind in der Softwareentwicklung wie auch sonst im Leben zu vermeiden. Dieser Meinung bin ich immer noch und sogar immer mehr.

Doch auch wenn wir sie vermeiden sollen, heißt das nicht, dass wir das immer können. Manches ist einfach systemrelevant, weil es die fundamentale Wahl für ein System repräsentiert. Es muss konstant bleiben - ansonsten handelt es sich um ein ganz anderes System. Für die westliche Welt sind das z.B. eine Demokratie oder Marktwirtschaft.

Für die Softwareentwicklung gibt es das auch. Da gibt es einen Aspekt, der ist so zentral, dass wir nicht um ihn herum kommen. Er ist bestimmend. Es ist unveränderlich.

Damit meine ich nicht, ein bestimmtes Betriebssystem oder eine Entwicklungsplattform. Im Gegenteil! Die müssen ständig disponibel.

Nein, ich meine die Daten. Die Daten einer Software bzw. eines Unternehmens sind aus meiner Sicht eindeutig systemrelevant. Sie sind förmlich das Fundament. Sie gilt es zu hegen, zu pflegen, zu erhalten. Nicht umsonst heißt es: data outlives application.

Anwendungen kommen und gehen über Jahre und gar Jahrzehnte. Doch die Daten bleiben. Sie wachsen nur. Allemal in Zeiten scheinbar unbegrenzten Speicherplatzes scheint es töricht, Daten zu löschen. Wer weiß, wann sie einmal nützlich werden könnten für eine Big Data Auswertung?

Wenn das aber nun so ist, dass Daten unvermeidbar systemrelevant sind, was bedeutet das für den Umgang mit ihnen?

Ich denke, systemrelevante Größen brauchen eine spezielle Behandlung. Sie stehen ja quasi außerhalb des Gesetzes. Wir sind von ihnen abhängig. Deshalb müssen wir darauf achten, dass sie uns keine Diktatur aufzwingen. Wenn eine Größe systemrelevant ist, dann ist sie ein Herrscher. Mit einem "guten König" lässt es sich da leben; aber einen unberechenbaren Nero wollen wir nicht über uns haben.

Letztlich sollte auch die systemrelevante Größe an einem guten Verhältnis mit ihren Untertanen interessiert sein. Denn die Abhängigkeit besteht letztlich in beide Richtungen. Ohne ein System, ist die Größe nichts.

Also, was bedeutet es für die Softwareentwicklung oder genauer: für die Softwarearchitektur, dass Daten eine unvermeidlich systemrelevante Größe sind?

Wie im richtigen Leben folgt daraus die Notwendigkeit zu Transparenz und Flüssigkeit, würde ich sagen.

Systemrelevante Größen müssen besonders offen und verständlich sein. Weil sie systemrelevant sind, können sie ja nicht ersetzt werden. Wenn man sie nicht mehr versteht, gibt es keine Alternative.

Systemrelevante Größen müssen besonders flüssig sein. Ihre Form muss sich verändern lassen. Sie dürfen sich nicht aus innerer Trägheit/Festigkeit dem Wandel widersetzen. Denn Wandel - soviel sollte gewiss sein - wird nötig sein. Systemrelevante Größen haben eine lange Lebensdauer, so dass Veränderungen in der Umwelt und deshalb Anpassung unvermeidbar sind.

Alles, was die Transparenz/Verständlichkeit und die Flüssigkeit beeinträchtigt, ist also zu vermeiden.

Was bedeutet das für Daten konkret?

Daten müssen in einer Weise gehalten werden, die transparent und verständlich ist. Klingt vielleicht einleuchtend - wird aber oft nicht beachtet. Einkaufspolitik, Tradition, Performance und andere Kräfte ziehen Daten oft in eine Form, die Transparent und Verständlichkeit reduzieren. So weit reduzieren, bis nur noch eine kleine Gruppe von Personen die Daten versteht. Die bestimmt dann über das Schicksal einer Organisation - ob sie will oder nicht.

Was Transparenz im konkreten Fall bedeutet, weiß ich nicht. Das ist von Fall zu Fall, von Datenbasis zu Datenbasis verschieden. Das eine große Unternehmensdatenmodell mit 1468 eng verwobenen Tabelle in einer Oracle Monsterserver ist aber sicherlich keine Lösung.

Eher sind es Daten in unterschiedlicher Organisationsform in verschiedenen Bounded Contexts, die transparent sind.

Daten müssen darüber hinaus auch in einer Weise gehalten werden, die es leicht macht, ihre Form immer wieder zu verändern. Formate und Technologien, die es schwer machen, zu exportieren und zu importieren, sind zu vermeiden. Lock-In jeder Art ist eine Gefahr für das System. Wer sagt, "Wir sind eine XYZ-Company!" ist schon auf dem falschen Weg. (Setzen Sie für XYZ ein Persistenzprodukt oder eine Technologie oder ein Paradigma Ihrer Wahl ein.)

ETL ist deshalb auch here to stay und wird weiter an Bedeutung gewinnen. Weil die Vielfalt der Optionen, der Anwendungen und Bounded Contexts wächst, mit/in denen Daten gesammelt und verarbeitet werden.

Event Sourcing halte ich aus diesem Grund auch für zeitgemäß. Denn damit werden Daten im Grunde von jedem Format befreit. Das macht sie maximal flüssig. Events sind quasi ein Granulat, das in immer neue Form je nach Bedarf gegossen werden kann.

Ja, ich denke, Transparenz und Flüssigkeit sind die beiden zentralen universellen nicht-funktionalen Anforderungen an die Datenhaltung. Sonst ist sie nicht nachhaltig. Sonst kommt es zu hemmendem Lock-In der einen oder anderen Art - und das kostet Geld.

Daten sind eben ein oder vielleicht sogar die einzige wirklich systemrelevante Größe in der Softwareentwicklung. Deshalb müssen wir mit ihnen in besonderer Weise umgehen.

Dienstag, 28. Mai 2013

JavaScript Unit Testing mit WebStorm Schritt für Schritt

JavaScript tut Not. Es hilft halt nichts. Lieber würde ich mich zwar mehr mit F# beschäftigen, doch dringender scheint mir JavaScript-Fingerfertigkeit. Denn mit JavaScript kann ich meine Reichweite vergrößern in Richtung Web und Mobile. Bisher bin ich ja eher der “Desktop GUI Guy” ;-) Und mit JavaScript kann ich coole Entwicklungen schneller mitmachen im Bereich Backend, z.B. node.js oder Cloud APIs. .NET Bindings werden da ja eher stiefmütterlich behandelt.

F# brächte mir zwar Vorteile bei der Strukturierung von Geschäftslogik. Doch da bin ich weniger Unzufrieden mit C# als bei Reichweite und Modernität.

Also mehr JavaScript. Hier ein bisschen, da ein bisschen. Vor allem aber auch gern testgetrieben.

Als Entwicklungsumgebung habe ich mir mal WebStorm von JetBrains angeschafft. Das war sehr preisgünstig beim letzten Cyber Monday und schien ordentlich.

Damit geht auch testgetriebene Entwicklung – nur ist die nicht so einfach zu starten, wie bei VS mit ReSharper. Deshalb hier ein Spickzettel zunächst einmal für mich selbst:

1. Verzeichnisse einrichten: In einem WebStorm Projekt zwei Verzeichnisse einrichten, eines für den Produktionscode, eines für Testcode.

image

Wie die Verzeichnisse heißen, ist eigentlich egal. Ihre Namen müssen nur korrekt im nächsten Schritt referenziert werden.

2. Konfigurationsdatei angelegen: Bei .NET findet ein Testrunner die Tests automatisch in den Assemblies eines Projektes. Bei JavaScript muss man dafür jedoch eine Konfigurationsdatei anlegen. (Zumindest für den JavaScript Test-Driver, der mit WebStorm ausgeliefert wird.) Der Name der Datei ist nicht so wichtig, die Extension muss allerdings .jstd sein:

image

Die Konfiguration ist simpel: Sie enthält die Adresse des Testrunners, der in einem Browser läuft. Und sie listet die Quellen für Produktionscode (load:) und Tests (test:):

server: http://localhost:9876

load:
  - src/*.js

test:
  - tests/*.js

Die Quellen nehmen Bezug auf die oben angelegten Verzeichnisse.

3. Tests werden im Testverzeichnis angelegt. Am besten zur weiteren Konfiguration des Testframeworks einen Probeweisen Test anlegen. Im ersten Schritt sieht der nur so aus:

image

Wichtig ist, dass angeboten wird, die Bibliotheksdateien des Testframework zu laden. Das sollte mit dem Shortcut getan werden. Dann sieht das Projekt so aus:

image

Durch die Referenzierung dieser Dateien steht für Tests Intellisense zur Verfügung:

image

Achtung: Tests müssen den Präfix “Test” haben!

4. Leider läuft der Probetest jetzt noch nicht einfach so. Es muss erst noch eine Laufzeitkonfiguration für das Projekt angelegt werden:

image

image

image

Hier wird die .jstd-Datei referenziert! Und der Name der Konfiguration taucht dann im Run-Menü auf:

image

5. Jetzt den Probetest ausführen lassen. Falls das nicht funktioniert, läuft der Testserver wahrscheinlich nicht. Der wird im Browser gehostet. Gestartet werden kann er über die IDE:

image

image

Durch Klick auf ein Browser-Icon wird der Testrunner-Server als Seite im Browser geöffnet:

image

Und nun – Wunder der Technik! – wird auch der Test über Run ausgeführt. Die IDE erstrahlt in frischem Grün:

image

 

Das war´s. Jetzt, da ich das Setup nochmal Schritt für Schritt durchlaufen bin, ist es gar nicht mehr so undurchsichtig. Aber wer weiß… Wenn ich mal wieder längere Zeit JavaScript nicht in die Hand nehmen sollte, hilft mir diese Erläuterung bestimmt, schneller wieder reinzukommen.

Und es bewahrheitet sich der alte Spruch, dass man durch Lehren, also Erklären, am besten lernt.