Juli 2016

Band 31, Nummer 7

CQRS – Erstellen sehr reaktionsfähiger Systeme mithilfe von CQRS

Von Peter Vogel | Juli 2016

Das CQRS-Muster (Command Query Responsibility Separation, Zuständigkeitstrennung für Befehle und Abfragen) ist in den letzten drei bis vier Jahren immer beliebter geworden. Es handelt sich gewiss um ein wichtiges Tool in durch Zusammenarbeit geprägten Szenarien, in denen eine Datenmenge von mehreren Prozessen aktualisiert wird (in seiner Kolumne „Innovation“ vom Juni 2015, „CQRS für allgemeine Anwendungen“ unter bit.ly/1OtQba3, plädiert Dino Esposito sogar für einen noch umfassenderen Einsatz). Ich gehe noch einen Schritt weiter und behaupte, dass CQRS tatsächlich das Standardentwurfsmuster für ASP.NET MVC-Entwickler ist, die Daten abfragen, um sie in ihren Sichten anzuzeigen, und dann Befehle zum Aktualisieren von Tabellen aufrufen, wenn Daten wieder zurück zu ihren MVC-Controllern gesendet werden.

CQRS ist jedoch eine Methodik, die im Rahmen einer größeren Strategie umgesetzt werden sollte. Der erste Schritt in dieser Strategie ist Domain-Driven Design (DDD), was von Julie Lerman in ihrer Kolumne vom Juni 2013, „Verkleinern von EF-Modellen mithilfe von gebundenen DDD-Kontexten“ unter bit.ly/1TfF7dk, beschrieben wird. DDD führt zum Aufteilen Ihrer Anwendung in zusammenarbeitende Domänen, von denen jede zusätzlich zu ihrem eigenen dedizierten Geschäftsmodell eine eigene Datenbank haben kann. DDD bietet Strategien und Taktiken, die es ermöglichen, dass Domänen unabhängig voneinander entwickelt werden, aber dennoch weiter zusammenarbeiten.

Definieren von Bestandsdomänen

Doch DDD beseitigt nur potenziell die Notwendigkeit von Zusammenarbeit. Sehen Sie sich z. B. eine Onlineverkaufsanwendung an. Hier gibt es ein wichtiges gemeinsam genutztes Datenelement: Lagerbestände. Damit das Unternehmen nicht versucht, etwas zu verkaufen, was es nicht hat, kann es entweder einen genauen Bestand der verfügbaren Menge der einzelnen Artikel führen oder stets „für alle Fälle“ einen bestimmten Lagerbestand vorrätig halten. In unserer heutigen „schlanken“ Welt spielt die zweite Option keine Rolle mehr , denn Unternehmen wollen nicht mehr Bestand vorhalten als nötig.

Das Aktualisieren der verfügbaren Menge basierend auf Geschäftstransaktionen ist komplizierter, als Sie vielleicht meinen, denn in der Praxis muss ein Warenwirtschaftssystem viele verschiedene Transaktionen verarbeiten. Die offensichtlichen Transaktionen sind das Verringern der Bestandsmenge beim Verkauf eines Artikels und deren Erhöhung beim Eingang neuer Artikel. Darüber hinaus führt ein Unternehmen in regelmäßigen Abständen eine Inventur durch, um die tatsächlich vorrätige Menge für jeden Artikel zu bestimmen. Da selbst in einem ordnungsgemäß verwalteten System die Genauigkeit der Inventur nicht immer bei 100 % liegt, erfordert diese Zählung eine Änderung der Bestandsmengen. Darüber hinaus werden mitunter Artikel gefunden, die irgendwie fehlerhaft sind und deshalb aus dem Bestand entfernt werden. Es kann auch vorkommen, dass nachdem ein Artikel verkauft und dem Bestand entnommen wurde, der Kunde den Auftrag storniert, weshalb der Artikel wieder im Bestand aufgenommen wird.

Unternehmen wollen alle diese verschiedenen Transaktionen nachverfolgen und die Informationen für jede Transaktion spezifisch halten. Wenn neue Artikel eingehen, wollen Unternehmen z. B. wissen, welche Rechnung zu deren Kauf genutzt wurde. Wenn fehlerhafte Artikel gefunden werden, wollen Unternehmen die Ursache des Fehlers wissen. Und während der Bestanderfassung wollen Unternehmen den Umfang der etwaigen Diskrepanz erfahren. Die Buchhaltung braucht diese Informationen, um die „Lage des Unternehmens“ ordnungsgemäß melden zu können. Die Betriebsabteilung benötigt sie für eine genaue Planung. Da diese zusätzlichen Angaben benötigt werden, können diese Transaktionen nicht als bloße Hinzufüge- und Entnahmevorgänge im Bestand behandelt werden.

Jedoch gehören nicht alle diese Transaktionen zur selben Domäne. Diese Transaktionen sind auf Domänen aufgeteilt, die „Vertrieb“, „Buchhaltung“, „Betrieb“, „Wareneingang“ usw. heißen. Das Aufteilen von Transaktionen auf Domänen spiegelt die Wirklichkeit wider, dass verschiedene Domänen unterschiedliche Anforderungen haben.

Die meisten Domänen brauchen beispielsweise keine minutengenauen Daten. Wenn ihr Betrieb einen Geschäftstag hinter den tatsächlichen Transaktionen hinterherhinkt, wäre das kein Problem. Die Buchhaltungsabteilung muss z. B. nur die Finanzlage des Bestands am Monatsende kennen, wobei es meist ausreicht, wenn die Informationen in den ersten Tagen des Folgemonats zur Verfügung stehen. Wenngleich es möglich wäre, Bestandsdaten aktueller zu halten, ist es schwierig, einen geschäftlichen Grund dafür anzuführen. Die Bestandsdaten dieser Abteilungen können „schließlich konsistent“ sein.

Das Vertriebssystem kann hingegen nicht mit „schließlich (d. h. irgendwann einmal) konsistenten“ Daten arbeiten. Die Vertriebsabteilung muss die genau verfügbare Menge kennen, damit sie entscheiden kann, ob ein Artikel dem Kunden gezeigt wird („Nur noch 2 vorrätig! Jetzt kaufen!“). Während die meisten Domänen mit nur einem Wert für die verfügbare Menge eines Artikels arbeiten, weist das Vertriebssystem dafür ggf. zwei Werte auf. Ein Wert ist die „reservierte“ Menge (von einem Kunden angeforderte Artikel, der im Begriff ist, einen Auftrag zu erstellen). Der andere Wert steht für den „noch zum Verkauf verfügbaren Bestand“. Wenn ein Kunde zwei Artikel kauft, wird die reservierte Anzahl um zwei erhöht und der zum Verkauf verfügbare Bestand um zwei verringert. Nach Abschluss des Verkaufs wird entweder die reservierte Menge um zwei verringert oder, wenn der Benutzer den Auftrag storniert, werden die beiden Artikel wieder dem zum Verkauf verfügbaren Bestand hinzugefügt.

Sowohl die Buchhaltungs- als auch die Betriebsabteilung benötigen eine flexible relationale Datenbank, in der Tabellen auf verschiedene Weisen verknüpft werden können. Sie müssen diese Daten auch durchsuchen können, und zwar mitunter auf Weisen, die vor dem Auftreten eines bestimmten Problems noch nicht berücksichtigt wurden. Angesichts der beteiligten Datenmengen und der Notwendigkeit der Untersuchung des Transaktionsverlaufs ist auch eine Aufteilung in Seiten erforderlich.

Für das Vertriebssystem ist so viel Flexibilität nicht erforderlich. Die Beziehung zwischen Entitäten sind fest entsprechend dem Entwurf der Benutzeroberfläche. Gleiches gilt für die Suchanforderungen (wenngleich eine Seitenverwaltung weiter unterstützt werden muss).

Die Anforderungen an die Antwortzeit sind auch je nach Domäne unterschiedlich. In den meisten Abteilungen richtet eine in Sekunden gemessene Antwortzeit wohl kaum einen Schaden an. Doch für das Vertriebssystem muss die Antwortzeit in Sekundenbruchteilen gemessen werden.

Der Aufbau eines einzelnen Systems zum Erfüllen all dieser Anforderungen wäre wohl schwierig (wenn nicht sogar unmöglich). Das Entwickeln einer Anwendung für jede Domäne ist zumindest möglich. Beispiel: Die Produktmanagementabteilung verfügt über eine Produktliste, die ständig mit neuen Produkten und Informationen zu vorhandenen Produkten aktualisiert wird. Die Vertriebsabteilung pflegt hingegen eine schreibgeschützte bzw. nur abfragbare Produktliste, die regelmäßig mit den Daten in der Produktmanagementabteilung aktualisiert wird.

Stellen Sie sich Domänen als das Prinzip der eindeutigen Verantwortlichkeit vor, das auf Unternehmensebene umgesetzt wird. Jede Domäne ist für einen Teil des Geschäfts allein verantwortlich. Während das Unternehmen selbst eher komplex ist, kann jede Domäne (relativ gesehen) einfach sein.

Die CQRS-Lösung

Alle diese Domänen nutzen jedoch weiter gemeinsam dieselben Bestandsmengen. Wenn Transaktionen Domänen wie Buchhaltung und Wareneingang durchlaufen, müssen sie das Vertriebssystem über die Änderungen an den Bestandsmengen informieren. Sogar innerhalb des Vertriebssystems können mehrere Kunden ggf. versuchen, dieselben Artikel zu kaufen, wodurch sich Bestandsmengen jeweils nach oben und unten verändern, was einen Sperrmechanismus verlangt, während diese Werte angepasst werden.

Das CQRS-Muster zeigt hier dadurch seinen Nutzen, dass es mehr bietet als der typische ASP.NET MVC-Entwickler in Betracht ziehen würde. In den meisten Domänen können die Anwendungen ihre eigenen Datenbanken abfragen, die die Informationen enthalten, die die Domäne braucht. Sobald der Zeitpunkt gekommen ist, einen Befehl zum Anpassen der Bestandsmenge aufzurufen, müssen alle Domänen die Daten der Onlinevertriebsdomäne aktualisieren. Die Verpflichtung dazu gilt in beide Richtungen: Beim Verkauf von Artikeln muss das Vertriebssystem die Buchhaltung-, Betriebs- und anderen Domänen über Änderungen am verfügbaren Bestand aufgrund des Verkaufs einzelner Artikel benachrichtigen.

Anstatt jedoch die Daten einer anderen Domäne zu aktualisieren, ist jede Domäne nur verpflichtet, andere Domänen über etwas zu benachrichtigen, an dem diese anderen Domänen interessiert sind (in diesem Fall über die verfügbare Menge). Jede Domäne muss für das Aktualisieren ihrer eigenen Daten zuständig sein, da jede Domäne im Gegensatz zu den anderen Domänen weiß, wie ihre Daten zu verwalten sind.

Die Betriebsabteilung untersucht beispielsweise laufend die Beziehungen zwischen ihren Daten, um Lagerreichweiten vorherzusagen und zu bestimmen, was für Schwankungen bei der Bestandsmenge sorgt. Diese Domäne muss die Flexibilität beim Abfragen von Daten unterstützen, die eine herkömmliche relationale Datenbank bietet. Die Komplexität in der Betriebsdomäne wird von der Art der Analyse gesteuert, die in dieser Domäne erforderlich ist.

Die Vertriebsdomäne benötigt hingegen etwas einfacheres. Sie muss die Bestandsmenge (reserviert und zum Verkauf verfügbar) aller Artikel kennen. Es kann für das Vertriebssystem ggf. sogar sinnvoll sein, die ID jedes Artikels und die beiden Bestandsmengenwerte konstant im Arbeitsspeicher vorzuhalten. Wenn dies aufgrund der Anzahl von Artikeln im Bestand nicht möglich ist, kann es dennoch sinnvoll sein, die 20 % des Bestands im Arbeitsspeicher zu halten, die für 80 % der Vertriebsaktivitäten des Unternehmens zuständig sind. Die anderen Bestandsartikel können in einer Art NoSQL-Datenbank gespeichert werden, die auf die Unterstützung von Vertriebstransaktionen ausgelegt ist, ohne dass die Flexibilität geboten werden muss, die z. B. die Betriebsdomäne benötigt. Die Komplexität in der Vertriebsdomäne ist Folge der Notwendigkeit kurzen Antwortzeiten.

Diese Unterschiede bedeuten, dass von der Betriebsdomäne nicht erwartetet werden kann, dass sie weiß, wie die Bestandsmengenwerte in der Vertriebsdomäne aktualisiert werden sollen (und umgekehrt).

Domänen können deshalb eine Datenbank (ihre eigene) abfragen, während Befehle an eine andere Datenbank gesendet werden (alle Domänen senden beispielsweise Bestandsmengenaktualisierungen an die Vertriebsdomäne). Während DDD eine Strategie für das Segmentieren von Domänen mit unterschiedlichen Geschäftsanforderungen bietet, stellt CQRS eine Methodik zum Verwalten von Aktualisierungen unter diesen Domänen bereit (eine ausführlichere Erörterung der Abfrageseite von CQRS finden Sie in der Kolumne von Dino Esposito vom März 2016, „Der Abfragestapel einer CQRS-Architektur“ unter bit.ly/1WzjvPi).

Abwickeln von Befehlen und Ereignissen

Freilich wollen Sie die Anwendungen in diesen Domänen nicht noch dadurch komplizierter machen, dass sie mit der Vielzahl von Domänen zurechtkommen müssen, die bei jeder Transaktion benachrichtigt werden müssen. Anstatt alle Domänen nachzuverfolgen, die aktualisiert werden müssen, sendet jede Anwendung Transaktionen an ein Hilfsprogramm, das für das Benachrichtigen der verschiedenen Domänen zuständig ist (und meist als „Befehlsbus“ bezeichnet wird). Sobald neue Domänen definiert werden (oder vorhandene Domänen ihre Anforderungen ändern), muss nur der Befehlsbus innerhalb der Domäne entsprechend den neuen erforderlichen Benachrichtigungen aktualisiert werden.

Diese Transaktionen können in zwei Kategorien unterteilt werden: Befehle und Ereignisse. Die Unterscheidung zwischen diesen Kategorien ist eher konzeptuell als technisch. Im Wesentlichen sind sowohl Befehle als auch Ereignisse Nachrichten, die die Schlüsselinformationen zu einer Transaktion umschließen. Bei unseren Bestandstransaktionen handelt es sich um die ID des Artikels, die Bestandsänderung und die zusätzlichen von der Transaktion angeforderten Daten (beim Wareneingang können zu diesen zusätzlichen Daten die Händler- und Rechnungsnummer, und bei der Bestandsaufnahme die ID des Mitarbeiters zählen, der die Artikel tatsächlich gezählt hat). Diese Nachrichten können als POCO-Objekte oder XML/JSON-Dokumente (oder beides) codiert werden, was davon abhängt, wie die Daten zwischen Domänen gesendet werden.

Für mich ist ein Befehl laut Definition eine Anweisung, die ein einzelner Empfänger befolgen muss, um eine Aufgabe auszuführen. Ein Befehl ist in der Regel eine Aufgabe, die sofort ausgeführt werden muss und (offensichtlich) gesendet wird, bevor die Aufgabe ausgeführt wird. Ein Befehl kann erwartungsgemäß eine Antwort in Form von Erfolg/Fehler zurückgeben, die die Anwendung nutzen kann, um den Benutzer zu informieren, ob alles funktioniert hat (und möglicherweise die Anwendung aufzufordern, eine Abfrage zum Abrufen der Daten auszuführen, die zeigt, welche Ergebnisse erreicht wurden). Die meisten Aktualisierungen innerhalb der Domäne, die die Transaktion ausgelöst hat, werden wahrscheinlich mithilfe von Befehlen abgewickelt.

Ereignisse erfolgen hingegen, nachdem die Aufgabe ausgeführt wurde, können von mehreren Empfängern verarbeitet werden und müssen (meist) nicht unmittelbar verarbeitet werden. Von Ereignissen wird nicht erwartet, dass sie ein Ergebnis zurückgeben, zumindest nicht sofort. Wenn bei einem Ereignis etwas schief geht, ermittelt die Anwendung in der Regel den Grund dafür mithilfe einer verzögerten zurückgegebenen Nachricht („Wir können Ihren Auftrag leider nicht verarbeiten, weil Ihre Kreditkarte abgelehnt wurde“). Die meisten, jedoch nicht alle Aktualisierungen außerhalb der Domäne, die die Transaktion ausgelöst hat, werden wahrscheinlich mithilfe von Ereignissen abgewickelt.

Und wie bei den meisten konzeptuellen Unterscheidungen ist dies wahrscheinlich ein Kontinuum. Einige Nachrichten sind „offensichtlich“ Befehle, während andere „offenkundig“ Ereignisse sind, und es gibt noch welche, bei den vernünftige Menschen geteilter Meinung sein können.

Eine einzelne Transaktion in einer Domäne kann eine Kombination aus Befehlen und Ereignissen generieren. Nehmen wir z. B. neue Artikel, die im Wareneingang angezeigt werden. Nachdem die Artikel ordnungsgemäß eingegangen sind, sendet der Bus dieser Domäne den Befehl an das Vertriebssystem, die Bestandsmenge dieses Artikels sofort zu erhöhen. Der Bus sendet dabei auch ein Ereignis, sodass die Buchhaltungs- und betrieblichen Systeme benachrichtigt werden können, dass „etwas passiert ist“ und am Monatsende berücksichtigt werden muss. Wenn wir uns die beteiligten Nachrichten ansehen, kann es schwierig sein zu bestimmen, welche das Ereignis und welche der Befehl ist. Einen Anhaltspunkt bietet jedoch der Name der Nachricht. Ereignisse haben meist Namen in der Vergangenheitsform (GoodsReceived), während Befehle eher Anweisungscharakter haben (IncreaseInventory).

Der Bus sendet den ggf. den Befehl an das Vertriebssystem, indem ein RESTful-Dienst in dieser Domäne zur sofortigen Ausführung aufgerufen wird. Das Ereignis wird ggf. in dieselbe Nachrichtenwarteschlange geschrieben, die von anderen Domänen baldmöglichst verarbeitet werden soll (ich habe einige der Optionen in einem Artikel erörtert, den ich für VisualStudioMagazine.com geschrieben habe, „Simplifying Applications by Implementing Eventual Consistency with Domain Events“ unter bit.ly/1qn1wwV).

Doch wer weiß, auch wenn der Befehl an den Webdienst gesendet wird, was hinter diesem Webdienst passiert? Zum Verarbeiten einer großen Anzahl gleichzeitiger Anforderungen schreibt der Webdienst für die Domäne ggf. bloß die Befehlsnachricht in eine Warteschlange und gibt als Antwort „Danke, erhalten“ zurück, wodurch die Antwortzeit kurz gehalten und die Skalierbarkeit verbessert wird. Zusätzlich zum Verbessern der Skalierbarkeit ermöglicht das Schreiben von Befehlen in eine Warteschlange der Domäne bei widrigenfalls katastrophalen Problemen eine Wiederherstellung. Wenn die Datenbank oder das Netzwerk ausgefallen ist, kann das Vertriebssystem beispielsweise geduldig auf die Wiederherstellung des Diensts warten und dann andere Befehle in seiner Warteschlange verarbeiten. Sogar Befehle können also in Warteschlangen gestellt werden.

Wie bereits erwähnt, ist die Unterscheidung zwischen Befehlen und Ereignissen konzeptueller und nicht technischer Natur.

Verarbeiten von Befehlen und Ereignissen

Dank CQRS können Anwendungen nun mit einer Kombination zweier Datenbanken arbeiten: mit einer für Abfragen (wahrscheinlich lokal für die Domäne) und anderen Datenbanken, die als Ziele für Befehle und Ereignisse fungieren. Das Vertriebssystem arbeitet beispielsweise mit einem Datenspeicher mit einer NoSQL-Datenbank, die Bestandsmengendaten enthält. Die Anwendungen der Betriebs- und Buchhaltungsabteilung können mit einem Datenspeicher arbeiten, der gemäß dem Verlauf der Ereignisse/Befehle organisiert ist.

Der Unterschied zwischen den beiden Systemen ist, dass das Vertriebssystem eine Momentaufnahme des aktuellen Status der Bestandsmengen benötigt, um die geforderte Antwortzeit einzuhalten. Die Betriebs- und Buchhaltungsdomäne brauchen einen Verlauf davon, was mit jedem Artikel passiert ist, um eine Analyse zu ermöglichen. Die Betriebs- und Buchhaltungsdomäne können eine andere Methodik einsetzen, das sog. Event-Sourcing. Beim Event-Sourcing durchforstet die Domänenlogik das Überwachungsprotokoll auf Ereignisse, zu denen eine Benachrichtigung erfolgt ist, um eine endgültige Antwort zu bieten (für das Buchhaltungssystem kann diese so lauten: „Basierend auf dem Verlauf der gebuchten Transaktionen ist der aktuelle Wert Ihres Bestands X Euro“).

Event-Sourcing hat Vor- und Nachteile. Beim Event-Sourcing ist es stets möglich, eine Momentaufnahme des aktuellen Status der Daten wiederherzustellen, indem die Liste der Transaktionen erneut verarbeitet wird. Die Buchhaltung schätzt dieses Feature, da es der Liste der Ereignisse Anpassungen hinzufügt. Dank Event-Sourcing ist es auch möglich, die Zukunft zu beschreiben, indem potenzielle Ereignisse (erwartetet Lieferungen und Verkäufe) verarbeitet werden. Betriebsabteilungen schätzen dieses Feature bei der Planung.

Sobald die Liste der Ereignisse größer wird, verlängert sich jedoch die Antwortzeit. Die Buchhaltung kann ein Rollforward von ihrem „letzten als funktionierend bekannten Zustand“ (wahrscheinlich mit den Zahlen des letzten Monatsendabschlusses) ausführen und eine Momentaufnahme generieren, die die Monatsendzahlen darstellt. Diese Momentaufnahme wird als aktueller „letzter als funktionierend bekannter Zustand“ gespeichert und als Monatsendbericht veröffentlicht. Für Vorgänge kann ein Rollforward ab heute bis zu einem unbestimmten Zeitpunkt in der Zukunft ausgeführt werden, ohne dass wahrscheinlich jemals eine Momentaufnahme generiert wird. Sie dienen zum Neuerstellen der Zukunft bei jeder Anforderung. Angesichts der Erwartungen an die Antwortzeit für diese Domänen handelt es sich hierbei um wahrscheinlich plausible Szenarien.

Zum Bestimmen der aktuellen Bestandsmenge für das Vertriebssystem mithilfe von Event-Sourcing muss das Vertriebssystem jedoch ein Rollforward durch alle Transaktionen seit der letzten Bestandsaufnahme ausführen. Da Bestandsaufnahmen sehr arbeitsaufwendig sind, erfolgen sie nicht sehr häufig. Deshalb würde das Verarbeiten all dieser Ereignisse seit der letzten Bestandsaufnahme inakzeptable Antwortzeiten für das Vertriebssystem erzeugen. Stattdessen hält das Vertriebssystem ständig aktualisierte Bestandsmengenzahlen im Arbeitsspeicher vor.

Zusammenfassung

Während für Abfragen verschiedene Grade von Unterstützung in einer Datenbank (und als Folge eine Vielzahl von Indizes und Fremd-/Primärschlüssel) erforderlich sind, ist dies für Aktualisierungen nicht der Fall. Praktisch sämtliche Aktualisierungen werden anhand der IDs der beteiligten Entitäten gesteuert. Die Liste der Bestandsartikel mit den Bestandmengenzahlen wird beispielsweise vollständig von der Artikel-ID gesteuert. Dadurch kann das Datenmodell für die Befehlsseite eines CQRS-Systems drastisch vereinfacht werden. Die Fähigkeit von Entity Framework, eine Auflistung von Auftragsposten („SalesOrderItems“) für einen Auftrag („SalesOrder“) zu generieren, ist irrelevant, wenn die Befehls-/Ereignisnachricht einfach die IDs aller Auftragsposten enthält, die in einer Transaktion geändert wurden.

Die Auswirkungen von Sperren in der Datenbank als Folge dieses Entwurfs sind interessant. Aktualisierungen der Bestandsmenge und reservierten Mengen im Vertriebssystem sehen das Ändern eines oder beider Ganzahlwerte vor. Sperren können minimiert werden. Sperren in einigen der anderen Systeme können verschwinden, wenn diese Systeme dem Event-Sourcing unterliegen. Die Transaktion fügt stets eine bestimmte Transaktion in eine Ereignistabelle ein, weshalb es keine Aktualisierungen gibt.

Das Unternehmen verfügt anschließend effektiv über mehrere unabhängige Prozessoren, die Daten in ihren Domänen aktualisieren und Befehle und Ereignisse verarbeiten. Ohne Sperren ist es möglich, dass dadurch Konflikte erzeugt werden. Beispiel: Ein Befehl zum Kauf von zwei Artikeln wird ggf. gleichzeitig als ein Ereignis angezeigt, das die Bestandsmenge auf null reduziert (jemand hat eine Bestandsaufnahme gemacht und festgestellt, dass nichts im Regal war). Dieses Problem kann interessanterweise mithilfe eines warteschlangenbasierten Event-Sourcing-Ansatzes behoben werden. Der Aktualisierungsprozessor für die Bestandsmenge im Vertriebssystem kann auf Event-Sourcing-Basis arbeiten und dabei alle der zuletzt in einer Warteschlange eingegangenen Befehle (bis zu einem gewissen Grad) durchlaufen und die Bestandsmenge mit der Summe ihrer Ergebnisse aktualisieren. Befehle, die gleichzeitig auftauchen, können zu einer einzelnen Aktualisierung zusammengefasst werden. Alternativ kann es ggf. einfach erforderlich sein zu erkennen, dass dem Unternehmen gelegentlich wie einem Benutzer auch das Stornieren eines Auftrags erlaubt wird.

CQRS ist ein leistungsstarkes Instrument. Es bietet jedoch den größten Nutzen, wenn es für freigegebene Datenspeicher, auf Zusammenarbeit ausgelegte Prozesse und innerhalb der von DDD bereitgestellten Strategie zum Einsatz kommt.


Peter Vogelist Systemarchitekt und Chef von PH&V Information Services. PH&V bietet ein vollumfängliches Beratungsangebot vom Entwurf der Benutzererfahrung über die Objektmodellierung bis zum Datenbankentwurf. Sie erreichen ihn unter peter.vogel@phvis.com.

Unser Dank gilt den folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: Dino Esposito und Julie Lerman
Dino Esposito ist Autor von „Microsoft .NET: Architecting Applications for the Enterprise“ (Microsoft Press, 2014) und „Modern Web Applications with ASP.NET“ (Microsoft Press, 2016). Esposito ist Technical Evangelist für die .NET- und Android-Plattformen bei JetBrains und spricht häufig auf Branchenveranstaltungen weltweit. Auf software2cents.wordpress.com und auf Twitter unter twitter.com/despos lässt er uns wissen, welche Softwarevision er verfolgt.

Julie Lerman ist Microsoft MVP, .NET-Mentorin und Unternehmensberaterin. Sie lebt in den Bergen von Vermont. Sie hält bei User Groups und Konferenzen in der ganzen Welt Vorträge zum Thema Datenzugriff und anderen .NET-Themen. Julie Lerman führt unter thedatafarm.com/blog einen Blog. Sie ist die Autorin von „Programming Entity Framework“ sowie der Ausgaben „Code First“ und „DbContext“ (alle bei O’Reilly Media erschienen). Folgen Sie ihr auf Twitter: @julielerman, und sehen Sie sich ihre Pluralsight-Kurse unterjuliel.me/PS-Videos an.