Definition

CQRS (Command Query Responsibility Segregation)

Was ist CQRS (Command Query Responsibility Segregation)?

CQRS (Command Query Responsibility Segregation) ist ein Programmierdesign und Architekturmuster, welches das Abrufen und Ändern von Daten unterschiedlich behandelt. CQRS verwendet Befehlshandler, um den Abfrageprozess zu vereinfachen und komplexe, systemübergreifende Änderungen zu verbergen.

Wenn das CQRS-Muster mit dem Event-Sourcing-Muster kombiniert wird, garantiert es ein Audit-Protokoll der Änderungen an der Datenbank, das zur Aufrechterhaltung der Transaktionskonsistenz beitragen kann. Darüber hinaus kann sein Lesemodell ein materialisiertes Ansichtsmodell der Schreibmodelldaten enthalten oder zur Generierung eines solchen verwendet werden.

CQRS ist ein Software-Architekturmuster, bei dem die Verantwortlichkeiten der Befehle und Abfragen eines Systems durch vertikale Aufteilung der Anwendungslogik getrennt werden. Man kann sich das Muster als eine Möglichkeit vorstellen, Lese- und Aktualisierungsvorgänge für einen Datenspeicher zu trennen. Das Ziel von CQRS ist die Maximierung der Anwendungsleistung, Sicherheit und Skalierbarkeit.

Bei CQRS übermittelt ein Befehl die Absicht des Benutzers. Es handelt sich um eine Anweisung, die der Benutzer dem System zur Ausführung einer bestimmten Aufgabe erteilt. Ein Befehl generiert kein Ergebnis und ist Teil des Schreibmodells. Eine Abfrage ist eine Informationsanfrage eines Benutzers. Sie gibt ein Ergebnis zurück, ändert jedoch nicht den Status und ist Teil des Lesemodells.

Abbildung 1: Schritte im CQRS-Muster.
Abbildung 1: Schritte im CQRS-Muster.

Greg Young entwickelte das CQRS-Muster im Jahr 2010, etwa zur gleichen Zeit, als Event Sourcing aufkam. Er definierte das Muster wie folgt: “[CQRS] uses the same definition of commands and queries that Meyer used and maintains the viewpoint that they should be pure. The fundamental difference is that, in CQRS, objects are split into two objects, one containing the commands, one containing the queries.” Auf Deutsch: „[CQRS] verwendet dieselbe Definition von Befehlen und Abfragen wie Meyer und vertritt den Standpunkt, dass sie rein sein sollten. Der grundlegende Unterschied besteht darin, dass bei CQRS Objekte in zwei Objekte aufgeteilt werden, von denen eines die Befehle und eines die Abfragen enthält.“ Young bezog sich dabei auf Bertrand Meyer, der den CQRS-Vorgänger, das Command-Query-Separation-Muster (CQS), entwickelte.

CQRS versus CQS

Obwohl CQRS mit CQS verwandt ist und von diesem inspiriert wurde, unterscheidet es sich dadurch, dass Objekte in zwei Objekte aufgeteilt werden: eines mit Befehlen und eines mit Abfragen. Durch die getrennte Modelldarstellung für Abfragen und Befehle eignet sich dieser Ansatz hervorragend für ereignisbasierte oder aufgabenbasierte Benutzeroberflächenanwendungen. Im Gegensatz dazu geht es beim CQS-Anwendungsarchitekturmuster hauptsächlich um die Trennung der Logik für Abfragen, die den beobachtbaren Zustand eines Systems nicht aktualisieren, von der Logik der Befehle, die Zustandsaktualisierungen durchführen.

Bertrand Meyer erwähnte CQS erstmals in seinem Buch Object-Oriented Software Construction. Es ist auch Teil seiner Arbeit an der Programmiersprache Eiffel. Er schlug vor, Methoden, die den Zustand ändern, von solchen zu trennen, die dies nicht tun. Dadurch können Abfragen in vielen Situationen sicher verwendet werden. Sie können auch überall eingeführt werden und ihre Reihenfolge kann bei Bedarf geändert werden.

Wann sollte das CQRS-Muster verwendet werden – und wann nicht?

Das CQRS-Muster ist in all diesen Szenarien nützlich:

  • Kollaborative Bereiche. Zahlreiche Benutzer können gleichzeitig dieselben Daten anzeigen.
  • Aufgabenbasierte Benutzeroberflächen. Benutzer werden durch eine Reihe von Schritten geführt, um eine Aufgabe abzuschließen.
  • Systeme mit hohem Datenverkehr. Die gleichmäßige Verteilung der Arbeitslast zwischen Lese-/Schreibvorgängen verbessert die Leistung und Skalierbarkeit in Systemen mit hohem Datenverkehr.
  • Abfragen aus Repositorys. Abfragen der Daten, die Benutzer anzeigen müssen, aus Repositorys.
  • Komplexe Geschäftslogik. Die Trennung von Lese-/Schreibprozessen kann dazu beitragen, das Anwendungsdesign zu optimieren, wenn es eine komplizierte Geschäftslogik enthält.
  • Optimierung von Lesevorgängen. CQRS kann dedizierte Lesemodelle erstellen, um die Lesevorgänge zu optimieren, insbesondere wenn eine Anwendung viele solcher Vorgänge hat.
  • Unterschiedliche Datenmodelle für Lese-/Schreibvorgänge. Durch CQRS können mehrere Datenmodelle für jeden Vorgang erstellt werden, indem die Lese-/Schreibmodelle getrennt werden.
  • Unterstützung für Event Sourcing. CQRS kann mit Event-Sourcing-Mustern kombiniert werden, um ein System zu entwickeln, das viele Ereignisse und Abfragen verarbeiten kann.

Es ist wichtig, die einzigartigen Anforderungen und Einschränkungen von CQRS zu berücksichtigen und gründlich zu prüfen, ob dieses Muster für die jeweiligen Anwendungsfälle geeignet ist. Verwenden Sie es nur in eingeschränkten Situationen, in denen es notwendig ist. Es ist nutzlos für Systeme, die dem Denkmodell Erstellen, Lesen, Aktualisieren und Löschen folgen.

Wie das CQRS-Muster ausgeführt wird

Die häufigste Art, CQRS einzusetzen, ist das Befehlsmuster, das heißt das Softwaresystem, das eine Schnittstelle auf hoher Ebene definiert. Zur Laufzeit nimmt die Basisklasse den Befehl entgegen, erstellt den entsprechenden Objekt-Handler – zum Beispiel „Aktualisieren“, „Löschen“ oder „Erstellen“ – und ruft eine Methode auf, um den Befehl auszuführen.

Vor und nach der Ausführung kann die Basisklasse protokollieren, dass die Methode aufgerufen wurde. Das Protokoll kann von und zu jedem beliebigen Zeitpunkt wiedergegeben werden. Sobald die Schnittstelle und der Dispatch-Code vorhanden sind, wird das Computerprogramm, das die Ereignisse aufnimmt und wiedergibt, als for-Schleife bezeichnet. Es liest eine Datei ab einem bestimmten Punkt und ruft dann für jede Zeile den Befehl in dieser Zeile mit den Daten in dieser Zeile auf. Die im Befehlshandler verborgene Komplexität wird durch den Handler begrenzt oder gekapselt.

Die Handler rufen jeden Schritt des Prozesses auf, um ein logisches Element im System zu erstellen, zu aktualisieren oder zu löschen. Sie können die erste Anfrage versenden und überwachen, wann diese Aktion abgeschlossen ist, und bei Bedarf Fehler und Rollbacks behandeln. Der Handler richtet einen lang andauernden, zweiphasigen Commit (Saga-Muster) ein, in dem er die Ergebnisse in das Ereignisprotokoll schreibt. Dieses Protokoll enthält eine Aufzeichnung jeder Systemänderung. Wenn dieses Protokoll auf konsistente Weise erstellt wird, sodass ein Programm es lesen und wiedergeben kann, ermöglicht es die Ereignisquellenbestimmung.

CQRS-Beispiel: Kundenauftragsprozess

Wenn ein Kunde eine Bestellung aufruft, wird ein einfacher Prozess ausgeführt: ein Lesevorgang aus einer Datenbank. Der Lesevorgang könnte aus einem NoSQL-Schlüsselwertspeicher wie Redis erfolgen, in dem alle Kundendaten gespeichert sind. Ein Microservice ruft die Informationen ab und eine Webseite zeigt sie an. Das Redis-Team kann in Zusammenarbeit mit den Frontend-Entwicklern den Lesevorgang problemlos bewältigen.

Die Schreibseite des Vorgangs ist viel komplexer und umfasst mehrere verschiedene Schritte und Abhängigkeiten. Wenn beispielsweise eine Bestellung vor dem Versand storniert wird, geschieht Folgendes:

  • Die Rücksendung muss im Cache storniert und im Hauptquellsystem der Aufzeichnung zurückgegeben werden.
  • Wenn es ein Data Warehouse gibt, muss der Datensatz daraus und aus anderen Hilfssystemen gelöscht werden.
  • Das physische Lager und der Versand müssen benachrichtigt werden, damit der Artikel nicht versendet wird.

Kreditkartengebühren müssen storniert werden, die Bestandszahlen im Lager müssen geändert werden und die Nachbestellung des Lieferanten muss um eins reduziert werden.

Da eine einzige Änderung (Stornierung der Bestellung) an so viele Stellen kopiert werden muss, müssen die Aktualisierungs-, Lösch- und Erstellungsseiten des Vorgangs entweder mit einem Enterprise Service Bus (ESB) oder einem Befehlshandler wie dem Saga-Muster interagieren. CQRS hilft bei der Erstellung der Logik zur Abwicklung dieser komplexen Transaktionen.

https://cdn.ttgtmedia.com/rms/German/ODS-vs-Data-Warehouse-deutsch.png

Abbildung 2: Ein operativer Datenspeicher unterscheidet sich in wichtigen Punkten von einem Data Warehouse, einschließlich der Anforderungen an die Funktionsweise von CQRS.

Herausforderungen mit CQRS

Einige Herausforderungen im Zusammenhang mit CQRS sind:

  • Komplexität. CQRS erhöht die Komplexität der Codebasis. Anstelle einer einfachen relationalen Datenbank, wie einer SQL-Datenbank, verfügt die Anwendung nun über Befehlshandler, Dispatching und Protokollierung, die alle das Fehlerpotenzial erhöhen.
  • Messaging. CQRS erfordert kein Messaging, aber es ist üblich, Messaging zur Verarbeitung von Befehlen und zur Übertragung von Aktualisierungsereignissen zu verwenden, wobei die Anwendung Nachrichtenfehler oder doppelte Nachrichten verarbeiten muss.
  • Konvertierung. Es kann schwierig sein, auf CQRS umzustellen, wenn derzeit online nicht komplexe Systeme ausgeführt werden, die kein komplexes Übergangsprotokoll haben, oder Systeme, die Aktualisierungen im Batch-Modus über Nacht durchführen.
  • Eventuelle Konsistenz. Die Trennung der Lese-/Schreibdatenbanken kann dazu führen, dass die Lesedaten veraltet sind. Die Aktualisierung der ersteren, um Änderungen in der letzteren widerzuspiegeln, kann zu Problemen bei der Datenkonsistenz führen. Es kann schwierig sein zu erkennen, wann ein Benutzer eine Anfrage basierend auf veralteten Daten aus der Lesedatenbank gestellt hat.
  • Verzögerte Datensynchronisierung. Änderungen, die im Schreibmodus vorgenommen werden, können einige Zeit in Anspruch nehmen, bis sie im Lesemodus angezeigt werden, was zu Problemen führen kann, wenn ein Benutzer den Lesemodus vor der Synchronisierung der Änderungen abfragt.
  • Kosten. CQRS-Muster können die Hardwarekosten erhöhen. Auch die Kosten für die Cloud-Nutzung können steigen, da für die Bereitstellung zusätzliche Datenbanktechnologien erforderlich sind.

Kombination von CQRS mit anderen Mustern: CQRS und Event Sourcing

Das CQRS-Muster wird häufig zusammen mit dem Event-Sourcing-Muster verwendet. Letzteres ist ein Ansatz für eine Reihe oder Abfolge von Ereignissen. Ereignisse sind benutzergenerierte Aktionen, wie zum Beispiel das Klicken einer Maustaste oder das Drücken einer Taste auf der Tastatur. CQRS-basierte Systeme verwenden separate Lese-/Schreibdatenmodelle, wobei jedes Modell auf relevante Aufgaben zugeschnitten ist und sich oft in physisch getrennten Datenspeichern befindet.

Bei CQRS verfolgt das System frühere Änderungen in Datenzuständen und benachrichtigt Benutzer, wie sie damit umgehen sollen. Jeder Schritt des Datentransformationsprozesses wird aufgezeichnet, nicht nur der aktuellste Zustand. Da das System selbst als Sammlung aller Änderungen im Laufe der Zeit neu erstellt werden kann, wird eine neue Ansicht der Daten im System erstellt: das Transaktionsprotokoll. Dieses Protokoll ist nicht nur eine Textdatei, sondern ein tatsächlicher Befehlssatz, der wiedergegeben werden kann, um die Änderung zu reproduzieren. In einigen Fällen kann das Ereignisprotokoll rückwärts abgespielt werden, um große Änderungen rückgängig zu machen.

Die Einrichtung der Ereignisquellen erfordert die Erfassung der Änderungen sowie die Erstellung der Software zur Wiedergabe dieser Änderungen. Bei sorgfältiger Bereitstellung können Befehlshandler in ein Protokoll schreiben und so das Transaktionsprotokoll für die Ereignisquellen erstellen. Durch das Ausführen jeder Zeile im Protokoll über einen Befehlshandler werden Ereignisquellen ausgeführt.

Die Muster von CQRS und Event Sourcing überschneiden sich erheblich. Der Befehl in CQRS ist eine Möglichkeit, Event Sourcing zu erstellen. CQRS funktioniert gut mit Event Sourcing, da es das System nach Geschäftsvorgängen unterteilt und Ereignisse das Ergebnis dieser Vorgänge sind.

Welche Muster gibt es noch?

CQRS ist mit dem domänengesteuerten Design – einem expliziten Domänenmodell – der Ereignisbeschaffung und dem Befehlsmuster kompatibel. Ein ESB kann die Nachrichten zur Ausführung der Funktionen Erstellen, Aktualisieren und Löschen übermitteln. Das allgemeine Muster ist die serviceorientierte Architektur (SOA).

Diese Definition wurde zuletzt im Oktober 2024 aktualisiert

Erfahren Sie mehr über Softwareentwicklung