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



Keine Kommentare:

Kommentar posten