Freitag, 4. November 2011

Teile und Herrsche! – CQRS, Teil 2

Vorbedingungen


Es sei angemerkt, dass man mit den Vorteilen der SOLID Prinzipien [1] vertraut sein sollte, um den ganzen Sinn von CQRS zu verstehen. Es entstanden im letzten Artikel viele Diskussionen über grundsätzliche Aspekte wie SRP oder OCL, weil diese Prinzipien nicht angewandt oder verstanden werden. Ebenso sollte man schon mal etwas über den Sinn von Layering und Modularität von Software gehört haben.

Zusammenfassung CQRS

Um noch mal die entscheidenden Punkte, die für einen Einsatz von CQRS sprechen, zu erläutern, hier eine kurze Zusammenfassung:

In CQRS geht es um eine weitere Auftrennung von Verantwortlichkeiten bzw. Belangen in einer Software, über Methoden und Klassen bis hin zu den Schichten der Applikation selbst.
In einer Businessanwendung geht es vor allem darum, dass Anweisungen zuverlässig und korrekt verarbeitet werden. Das Ergebnis sollte ein jederzeit gültiger und erwarteter Zustand sein, der alle Regeln der Geschäftslogik einhält. Dabei spielen Faktoren wie u.a. Transaktionssicherheit, Konsistenz oder Nebenläufigkeit eine Rolle.

Commands spiegeln, wie gesagt, den tatsächlichen Umfang und Wert der Software wider.
Was sie machen, ist der Funktionsumfang, also die Erfüllung gestellter Aufgaben.
Und wie gut sie es machen, die Qualität.

Die Summe aller Commands spiegelt den gesamten Funktionsumfang und die Fähigkeiten einer Domain wider.

Beim Anzeigen, Reporten und Überprüfen der Änderungen wiederum entstehen ganz anderen Anforderungen an die Software, z.B. Performance oder Skalierbarkeit.

Abfragen müssen nicht zwangsläufig aktuell oder konsistent sein. Dies ist übrigens auch aus der inzwischen verbreiteten NoSQL-Technologien bekannt: Eventual Consistency [2].

Da jede Art von Abfrage den Zustand zur Zeit der Abfrage selbst widerspiegelt aber nicht unbedingt den augenblicklichen Stand, liefert sie sehr oft sowieso nur „veraltete“ Daten.

Ein fehlerhaftes Reporting lässt sich immer wieder neu aufbauen.
Aber ein inkonsistenter Zustand der Domain und der Daten nur sehr schwer.
Umgekehrt kann man sagen, dass die meisten Anwendungen wenige Befehle verarbeiten müssen, diese aber mit höchster Präzision. Hingegen müssen sie aber ein Vielfaches an Querys abarbeiten und deren Ergebnisse schnell ausliefern.

Allein diese kurzen Erläuterungen zeigen schon, dass es einerseits komplett verschiedene  Anforderungen an die Software gibt es aber auch komplett unterschiedliche Implementierungen geben sollte. Denn für so unterschiedliche Belange wird es kaum ein System geben, dass in der Lage ist, beide Belange optimal darzustellen.

CQRS in der Praxis: Implementierung mit DomainEventing

Es gibt viele Möglichkeiten, CQRS zu implementieren. Einen davon möchte ich hier vorstellen: Domain Eventing mit Commands und Events.
Wenn  man den Wert von CQRS erkannt hat und mit der Umsetzung beginnt, ist der nächste, fast zwangsläufig logische, Schritt die Einführung einer Messagingarchitektur auf Domainebene.

Es geht darum, dass das komplette Verhalten einer Domain über Commands gesteuert wird.Dabei entstehen Zustandsänderungen, wie Erzeugung von neuen Objekten oder beliebige andere  Änderungen des Zustands.

In der Praxis sieht das beispielweise so aus:



Wem die Begriffe Repository und Aggregate nicht bekannt sind: das sind Bausteine, die in Domain Driven Design [3] verwendet werden. Eine Repository ist im Prinzip ein DAO und eine Aggregat nichts anderes als ein einzelnes Objekt oder ein Objektgraph.

Das Wichtigste dabei ist: eine Repository  stellt genau ein Aggregat zur Verfügung, das in der Lage ist, einen Command vollständig abzuarbeiten.


Die einzelnen Bausteine sind:

Command
Ein Command ist ein Befehl, der eine Statusänderung durchführen soll.
Er ist immer im Imperativ benannt: CreateCustomer, AddContactPerson, LockCustomer.

Technisch ist ein Command ein simples DTO, dass nur die Daten bereithält, die zur Ausführung des Befehls gebraucht werden.

Ein Command wird an einen CommandBus (der mit mindestens einer Repository ausgestattet wird) geschickt, dieser sucht den entsprechenden Handler und übergibt diesem das Command.
Werden diese Commands auch mitgeloggt erhält man damit eine komplette Historie aller Vorgänge und Zustandsänderungen einer Domain. 

CommandHandler

Führt das entsprechende Kommando aus, in dem er über die Repository ein Aggregat holt und den Befehl an dieses delegiert, oder (über eine Factory) ein Aggregat erstellt und es über Repository/Unit of Work persistiert. 

Event
Events sind Messages, die eine Zustandsänderung beschreiben. 
Es sind geschehene Ereignisse, die aus der Ausführung von Commands resultieren und deshalb immer in der Vergangenheitsform benannt (CustomerLocked, MoneyTransferred) und nicht mehr änderbar (!) sind.


Sie werden zuerst im EventBus registriert (i.d.R. aus einem Service heraus) und an der Stelle erzeugt, an der das Command als abgearbeitet bezeichnet werden kann. Events werden an den EventBus geschickt (raise).
  
EventHandler
Wenn die Sicherstellung der Konsistenz eines Aggregates exakt bestimmt werden kann, wird an dieser Stelle dafür gesorgt, dass alle bisher gesammelten Events im EventBus veröffentlicht werden (publish).
Dies kann im Aggregat selbst, in unserem Fall jedoch oft erst in der Repository sichergestellt werden (nach erfolgreichem save()). Der EventBus sucht alle Events, die die Interfaces der registrierten Events implementieren und führt diese aus. So können mehrere EventHandler nach einem bestimmt Event die weitere Arbeit übernehmen.
  
CommandBus, EventBus
Das können vollwertige Messaging Lösungen (z.B. ActiveMQ)[4] sein, wenn es um verteilte Anwendungen oder asynchrone Prozesse geht. In der Regel sind es aber simple Container, die nur für eine Transaktion (Thread) aufgebaut werden und Commands und Events abhandeln und verwalten. 
Wichtig dabei sind folgende Dinge:
  •  Ein Command wird immer vollständig oder gar nicht abgearbeitet.
  •  Ein Command darf genau einmal abgearbeitet werden.
  •  Für jedes Command gibt es exakt EINEN CommandHandler.
  •  Commands und Events müssen serialisierbar sein.
  •  Für einen Event kann es mehrere EventHandler geben.


Vorteile DomainEventing

Dieser Mechanismus hat gleich mehrere Vorteile:
Commands und CommandHandlern geben präzise die Intention des Befehls wider. CommandHandler sind quasi als Microservices anzusehen, durch den Fokus auf die Erledigung genau einer Aufgabe sind sie zudem besonders gut testbar.

Die Testbarkeit wird dadurch erhöht, dass man Testfälle so definieren kann:
  1. Ausgangssituation (given)
  2. Befehl, der ausgeführt werden soll (when)
  3. Erwartetes Resultat bzw. eingetretenes Ereignis (then)
Man braucht also nicht mehr auf Werte zu testen, um zu sehen, ob eine Zustandsänderung erfolgt ist (wovon sowieso stark abzuraten ist, da es sehr anfällig ist und dem Prinzip der Kapselung widerspricht), sondern braucht nur zu schauen, ob ein Event erzeugt wurde.

Beispiel „Kunden sperren“:

public function testLockCustomerRaisesEvent ()
{
    EventBus::clearAll();
    EventBus::register('CustomerLocked');
    
    $cmd = new LockCustomer(array(‘customerId’ => 1);
    $cmdBus = new CommandBus($this->customerRepository);
    $cmdBus->dispatch($cmd);

    $event = EventBus::getLastPublished();
    $this->assertType('CustomerLocked', $event);
}


Die Hauptaufgabe der Eventhandler ist es, weitere Prozesse innerhalb eines Threads anzustoßen. Damit sie in der Domain Sinn machen, dürfen sie z.B. nur innerhalb des Threads weitere Commands erzeugen. Dies sollte man aber so weit wie möglich vermeiden.

Alle außerhalb liegenden (z.b. asynchrone oder auf anderen Systemen operierende) werden wie gehabt über bestehende Messaginglösungen abgearbeitet.
Eine der häufigsten Aufgaben von EventHandlern ist die Denormalisierung der Aggregate in die entsprechenden Datenhaltungen für die Lesevorgänge zu erledigen.

Nachteile DomainEventing

Hm, gute Frage. Am ehesten noch die erhöhte Anzahl von Interfaces und Klassen.
Das ist aber nicht wirklich ein Nachteil. Denn viele kleine Klassen haben im Gegensatz zu wenigen aber zu großen Klassen sowieso Vorteile.

Fazit aus der Praxis

Bei einer aktuellen Anwendung, mit der ich mich als Teil eines Teams gerade beschäftige,  handelt es sich um ein großes und komplexes System, dass aus knapp 1.000.000 Codezeilen, ca. 220 Datenbanktabellen und etwa 250 GB Daten besteht.

Wie es viele von uns (leider) kennen, gibt es kaum oder nur wenige Tests. Und wenn, dann meist nur Integrationstests. Also eine typische Legacy-Anwendung (mit Legacy meine ich hier nicht Fremdsysteme, sondern nicht getestete Software – was das gleiche ist.)
Dabei zeigte sich sehr schnell, dass es nur eine relativ kleine Anzahl von Commands und Events im Verhältnis zur Größe des Sourcecodes gibt.
Und es zeigte sich ebenso, wie man schrittweise Legacy Code umbauen kann, in dem man die Anforderungen schrittweise  in Commands umsetzte und diese erst mal nur über Wrapper bestehende Services benutzen und später dann ausgetauscht werden.

Aber durch die oben erwähnte ideale Testbarkeit konnte man das sogar bei einem bisher kaum mit Tests versehenem System relativ einfach tun.
Es wurde bei so einem System schnell klar, dass dies das eigentliche Killerfeature von DomainEventing ist.

Freitag, 14. Oktober 2011

Teile und Herrsche! - CQRS, Pt. 1



Im Leben eines Entwicklers gibt es immer wieder „Aha“- oder sog. „Breakthrough“-Momente, in denen man entweder etwas entdeckt, dass die eigenen Fähigkeiten beträchtlich erweitert oder lange bestehende Probleme löst.

Hier geht es um ein solches, (noch) kaum bekanntes und bisher selten angewendetes Prinzip für das Design von objektorientierter Software.  Meines Erachtens ist es sehr einfach  zu verstehen und auch sehr einfach anwendbar. Aber noch größer sind die Vorteile die es bietet.

Aber erst mal die Auflösung des Akronyms:

CQS = Command Query Separation  (nach Bertrand Meyer):
“Separate command methods that change state from query methods that read state. But not both.”

Das heißt, die Trennung von Methoden, die entweder Zustände ändern (Befehle verarbeiten) oder den Zustand eines Objekts lesen bzw. darüber berichten.

CQRS = Command Query Responsibility Segregation
Auf Deutsch: Auftrennung der Verantwortlichkeit nach Befehl und Abfrage.

Ist doch das gleiche, oder?
Nicht ganz, bei CQRS ist nicht die Rede von Methoden…

SOLIDe Klassen und Methoden

Bevor ich in die Details gehe, möchte ich darauf hinweisen, dass dem Leser die SOLID Prinzipien [1] und auch Clean Code [2] ein Begriff sein sollten. Zusammengefasst geht es um elementare Prinzipien, mit Hilfe derer man qualitativ hochwertige, d.h. vor allem änderbare, wartbare und testbare Software schreibt.

Schauen wir uns eine oft in dieser oder ähnlicher Form auftretende, „traditionelle“ Klasse eines Models an:

class Customer
{
    private $name;
    private $zipcode;
    private $street;
    private $city;
   
    public function doThis() {
        // ...some sophisticated domain logic
        return $yuk;
    }
   
    public function doThat() {
        // ...some other sophisticated domain logic
        return $yak;
    }
   
    public function setCity($aCity) {
        $this->city = $aCity;
    }
   
    public function getCity() {
        return $this->city;
    }
   
    // ... a bunch of other setters and getters
}

Selbst in diesem rudimentären Beispiel erkennt man beim genaueren Hinschauen mehrere Probleme und Verstöße gegen SOLID:
  1. SRP: was ist die Verantwortlichkeit dieser Klasse?Sie tut mehrere Dinge (doThis, doThat), man kann einen ihrer Zustände direkt setzen (setBar) und sie berichtet direkt über einen ihrer Zustände (getCity)
  2. Information Hiding – sie exponiert ihren Zustand direkt.
  3. Die Befehlsmethoden doThis und doThat führen zwar eine Anweisung aus, geben aber zugleich auch etwas zurück.
  4. setCity ist fast schon kriminell, denn letztendlich soll es eine Änderung (eines Teils) einer Adresse sein, es werden aber einzelne Attribute des Konzepts Adresse gesetzt. (Aber das ist Thema des kommenden Posts „Das ultimative Ende aller Getter und Setter“…) 
Sowohl SOLID als auch Clean Code propagieren das „Tell, don’t ask!“-Prinzip [3]:
Sag einem Objekt was es tun soll und frage nicht erst, ob es das tun kann (wie elementar wichtig diese Regel ist, werde ich hoffentlich mal getrennt abhandeln).
Und nach CQS sollte eine Methode, die einen Befehl darstellt, keine Informationen zurück liefern bzw. gar keine Rückgabe haben.

Aber das grundlegendste Problem dieser ( und der meisten mir bekannten Klassen oder Modelle) ist die Verletzung von SRP.
Denn die Verantwortlichkeit der obigen Klasse kann man minimal so formulieren als „Die Klasse Foo macht doThis und doThat, und man kann mit ihr bar setzen und lesen“.

Eine einzige Verantwortlichkeit ist das nicht. Überprüft bitte eure bestehenden Modell-Klassen eines aktuellen Projektes. Wie viele davon machen zu viel und verstoßen gegen SRP?

Ein weiteres Beispiel für einen möglichen Service:

class BlogService
{
    public function setPost($post);
    public function deletePost($postId);
    public function getPost($postId);
    public function getAllPosts();
}

Auch hier sehen wir keine klare Verantwortlichkeit. Vielmehr ein Sammelsurium aus mehreren Anweisungen und vieler Abfragen.

Jetzt kommt CQRS ins Spiel.
Im Prinzip geht es darum, die obigen Prinzipien und CQS auf einer höheren Ebene anzuwenden:

„Trenne zwei Endpunkte (oder Objekte) und gib dem einen die Aufgabe, Befehle zu verarbeiten und einem anderen, Abfragen durchzuführen.“

Das ist die Essenz  von CQRS.


CQRS ist eine m.E. logische Weiterentwicklung von CQS, SOLID, Clean Code und Domain Modeling.
Wenn obige Methodiken und Prinzipien eine Trennung von Anweisungen (commands) und Abfragen (queries) auf Methodenebene propagieren, macht CQRS dies auf Klassen- bzw. sogar Architekturebene.

Verdopple deine Klassen

Was bedeutet die Anwendung von CQRS in der Praxis?

Zu aller erst mal nur, dass man seine Klassen (Service- als auch Domain-Klassen) klar nach der jeweiligen Verantwortlichkeit trenne sollte. Also nichts anderes, als das wir aus jeder Klasse zwei machen, so dass sie entweder nur Befehle verarbeiten (also Zustände ändern)  oder Abfragen durchführen.

Aus dem einen Service BlogService wird dann:

class BlogService
{
    public function setPost($post);
    public function deletePost($postId);
}

class BlogReadService
{
    public function getPost($postId);
    public function getAllPosts();
}

Jetzt hat jede von beiden zumindest eine fokussierte  Aufgabe: die eine führt alle möglichen Befehle im Zusammenhang mit Posts durch und die andere berichtet uns darüber.

„Aber dann habe ich ja doppelt so viele Klassen wie vorher!“

Ist das relevant?

Entweder halten wir grundlegende, inzwischen allgemein akzeptierte, Prinzipien guter OOP ein oder behaupten es nur und mauscheln uns weiter durch. Wenn die Anzahl der Klassen das größte Problem ist, dann ist es wohl eher ein Luxusproblem.
Und dabei werden nicht nur die bereits oben erwähnten Prinzipien befolgt, sondern auch noch viele Weitere, wie OCP oder ISP.

[Ha, nun habe ich doch wieder Getter benutzt. Hier sind sie aber harmlos, da sie nur Zustände reporten, nicht aber Vermutungen anstellen, um darauf basierend etwas mit den abgefragten Zuständen zu tun]

Was bringt das alles?

Selbst diese simpelste Form von CQRS bringt enorme Vorteile und beseitigt viele Nachteile einer “traditionellen” (meist dreischichtigen) Architektur.

Simples CQRS ähnelt übrigens sehr dem MVC Pattern:



Auch dabei wird eine Trennung von Befehlen (controller) zu Abfragen bzw. Ansichten der Daten (view) bereitgestellt.
CQRS geht noch einen Schritt weiter und führt eine zusätzliche vertikale Trennung ein:



Daneben gibt es noch etliche weitere Vorteile:
  • Transaktionen können wesentlich spezieller und isolierter ausgeführt werden
  • Module, Klassen und Methoden sind wesentlich expliziter, da sie nur auf ganz spezielle Aufgaben fokussiert sind
  • Die eigentliche Businesslogik wird nicht durch unnötige Abfragen aufgebläht
  • Man kann sich mehr auf Konsistenz statt auf Skalierung konzentrieren

Oder noch spezieller
  • Lesevorgänge erfordern kein kompliziertes und langsames Umwandeln komplexer Objektgraphen zu DTOs
  • Es können hoch performante Abfragen erzeugt und eigesetzt werden
  • Skalierung kann bei Abfragen praktisch unbegrenzt erfolgen
  • Es können spezialisierte Persistenzmethoden für die jeweilige Aufgabe eingesetzt werden (DB, Key-Value-Store, DocDB, 1NF, 3NF)


Fazit und Vorschau

Natürlich hat CQRS, wie alles, seine Grenzen, Ausnahmen und auch seinen Preis.
Da es aber ein sehr rudimentäres, klares und auf bekannten Prinzipien  basierendes Konzept ist, sind diese fast zu vernachlässigen.

Die Vorteile sind fast unglaublich.
Ich setze es nun bereits länger ein und bis auf die nötige Umstellung auf eine andere Denkweise bei der Entwicklung von Softwareanwendungen, gab es kaum praktische Probleme. Es war auch völlig egal, ob man das Prinzip in kleinen oder sehr komplexen Projekten einsetzt. Bei steigender Komplexität sind die Vorteile auf Dauer noch evidenter.

Ein weiteres Thema, das oft als „Konsequenz“ oder Erweiterung zu CQRS angesehen wird, ist Domain Eventing mit Commands und Events. Oder Event Sourcing, ein Ansatz zur ausschließlichen Speicherung von Zustandsänderungen von Objekten.
Falls genügend Interesse zu diesen weiterführenden Themen oder zu CQRS vorliegt, kann ich gerne die entsprechenden Artikel veröffentlichen.

[1] http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29

[2] http://www.amazon.de/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882

[3] http://pragprog.com/articles/tell-dont-ask



Samstag, 10. September 2011

No Model No Cry

Das Zend Framework hat es richtig gemacht: ein MVC-Framework, das kein M(odel) zur Verfügung stellt.

Wie kann ein MVC ohne ein M(odel) vollständig sein?

Nun, weil es nicht nur ein mögliches Model gibt. Und vielleicht auch, weil die meisten Entwickler, die aus der klassischen und ebenso gesetzten LAMP-Welt kommen, glauben, ein Model sei in erster Linie die Abbildung bzw. Umwandlung einer (statischen) Datenbankstruktur in eine objektorienterte Struktur.

Ein Model ist in erster Linie, nun ja, eben ein Modell. Und ein Modell ist immer eine vereinfachte Darstellung eines komplexen Systems oder Zusammenhangs. Das Modell eines Flugzeugs kann nicht fliegen aber man kann es im Windkanal testen. Ein Modell bildet niemals die gesamte Komplexität ab, sondern vereinfacht und abstrahiert Konzepte.

Der realen Welt, mit Objekten, die sich einen Teufel darum scheren, wie sie in statischen Strukturen abgebildet werden. Sie leben und sie tun etwas. Manche von ihnen sterben und andere werden geboren.
Allein daraus lässt sich ableiten, dass es mindestens zwei Modelle geben muss: eines für "lebendige" Objekte und eines für die statische Abbildung von Objekten, quasi für "veraltete Daten". Und in der Regel gibt es noch zwei weitere Modelle: ein Backend- und ein Frontend-Model. Zu all diesen Themen gibt es bald mehr.

Aus diesen gewagten Behauptungen erkennt man, welch weise Entscheidung Zend getroffen hat.

Welchen Sinn machen dann MVC-Frameworks überhaupt?

MVC sollte vor allem als ein logisches Pattern gesehen werden. Wie man dann V, C und M implementiert, das ist, wie so oft, für den Anwendungsfall zu entscheiden. Viel wesentlicher ist nämlich, dass man überhaupt die Dinge voneinander trennt, also statische von dynamischen Inhalten, das Lesen vom Schreiben von Daten und Objekten und lebendige von toten Objekten (also deren statischem Abbild).

Da Views und Controller meist einen ähnlichen technischen Background und Lösung von vielen wiederkehrenden Anforderungen sind (z.B. Routing), macht ein Framework sicherlich einen Sinn.
Wobei hier auch anzumerken ist, dass oftmal nur die Controller relativ simpel bleiben (sollten), Views können eine hohe Komplexität erreichen. Aber das sind Themen für andere Artikel...