CQRS Basics

CQRS ist ein Schlagwort das man im Moment überall  hört. Doch was verbirgt sich eigentlich unter dieser Abkürzung. Und wieso wird EventSourcing meistens im gleichen Atemzug genannt ?

Wir schauen es uns an. Um das Thema besser erklären zu können,werde ich bewusst einige Themen zunächst ignorieren. Diese Themen werden dann in den nächsten Posts näher angeschaut.

Zuerst schauen wir uns an, was CQRS entgegen mancher Mythen nicht ist:

  • CQRS ist kein Tool oder Werkzeug
  • Kein Framework
  • Kein Applikations Pattern
  • Nicht EventSourcing
  • Nicht für alle Software geeignet
  • Nicht die Lösung für alle Probleme

CQRS steht für  Command Query Responsibility Segregation und ist zunächst einmal nur ein Pattern.

Der Fokus liegt auf dem Trennen von Commands und Queries. Das bedeutet zunächst nicht mehr, als dass das Lesen und Schreiben  von Daten zwei verschiedene Belange sind.

Dies drückt sich zunächst einmal darin aus das die Modelle für das Schreiben ein anderes ist als das für das Lesen.

In klassischen Anwendungen gibt es für jede Entität aus der Domaine eine Implementierung. In einem CRM System gibt so z.B. einen Kunden. Der kann dann aus der Datenbank gelesen und geschrieben und gelöscht wierden  = CRUD.

Es stellt sich nun immer wieder heraus das ein einziges Modell bei komplexen Systemen Probleme mit sich bringt.

Unter anderem hängt das damit zusammen, dass eine komplexe Anwendung aus mehreren Bereichen besteht.  Ein CRM könnte aus folgenden Bereichen bestehen.

  • Stammdaten-Verwaltung
  • Fremdsystem-Synchronisation (z.b. mit Outlook)
  • Compliance Management
  • Terminplanung

Sehen wir uns nun an wie ein Kunde in den verschiedenen Bereichen geschaffen sein muss.

Stammdaten-Verwaltung

Hier haben wir ein sehr vollständiges Bild des Kundens, mit all seinen Daten die er im echten Leben hat. Man kann ihn sehr gut mit einer CRUD Strategie handeln. Man kann die Daten in Relationen oder Dokument basierten Datenbanken speichern . (SqlServer,MongoDb). Der Focus liegt hier bei auf den eigentlichen Datenfeldern.

Outlook-Synchronisation

Hier gibt es unterumständen weniger Businessdaten, pot. auch keine Historie, dafür aber viele technische werde, wie correlation ids, outlook ids usw.. Auch können nicht unbedingt alle Daten aus dem CRM hier im gleichen Format gespeichert werden, da das Format vorgegeben ist. Die Speicherung erfolgt in der Outlook / Exchange Datenbank. Hier liegt der Focus auf Id’s und flags ob und wann der Kunde zuletzt übertragen wurde.

Compliance Management

Hier kommt es weniger auf die Daten des Kundens als auf seine Verbindungen zu anderen an. Möchte man eine effiziente Lösung so können GraphDatenbanken wie neo4j oder Orientdb eine gute Lösugn sein.

Zusammengefasst

Nun bekommt man bei einer einzigen Entitäten eine riesige Anzahl von Datenfeldern. Von denen immer nur Teile in den einzelnen Bereichen interessant sind. Schlimmer noch teilweise sind Daten doppelt aber anders Belegt. Z.b. eine Hat eine Relations Id zu einem Ansprechpartner in Outlook eine andere Id als in unserem  entwickelten System.

Daraus folgende technologische Hürden:

Es könnte notwendig werden den Kunden in verschiedenen Systemen speichern zu können. in der SQL Datenbank brauchen wir den Kunden mit seinen angehängten Entitäten normalisiert, in MongoDb wäre eine denormalisierte Struktuir vorteilhaft. Outlook hard sehr harte Anforderung wie genau der Kunde gespeichert werden muss.

Dafür bekommt die Kunden Entität u.U. aber einige werte die nur für den Sync interessant sind wie die OutlookId. Kein Endanwender möchte eine Outlook id sehen…

GraphDatenbanken benötigen gar nicht die Informationsvielfalt die wir an den anderen Stellen haben.

Für  relationale Datenbanken brauchen wir tendenziell einzelne Entitäten, die über Relationen verbunden sind. Während wir für Dokumentenorientierte Datenbanken wir eher denormalisierte pot. redundante Dokument Strukturen  designen würden.

Diese Varianzen bestehen primär für das Lesen und interpretieren von Daten.Bei CRUD findet das Lesen und schreiben aber immer mit dem gleichen Modell statt.
Deswegen haben wir automatisch diese Komplexität auf beiden Seiten.

Man spürt schon wie unangenehm kompliziert das werden kann.

Der CQRS Ansatz

Schreiben

Hier bietet CQRS folgenden Ansatz.Man hat ein Schreibmodell und ein oder mehrere LeseModelle.

In der Regel ist das Schreiben nur eine Aneinanderreihung von Events. Hier kommt EventSourcing ins Spiel.

Wenn ein Kunde neu angelegt wird, so  wird ein „CustomerCreatedEvent“ persistiert.
Bekommt ein Kunde einen neuen Ansprechpartner so gibts ein „ContractAddedToCustomer“ Event.

Die Events sollen so nahe am UseCase sein wie möglcih. Kann man einen Kunden zum beispiel auf verschiedene Weisen anlegen, so gibt es auch verschiedene Events.

Es kann also auch ein „CustomerCreatedByOutlookImportEvent“ geben.

Lesen

Das ist nun der interessante Teil. Niemand möchte in einer Anzeige eine Liste von Events sehen! Also was machen wir ?

Aus diesen Events werden sogenannte Facts erzeugt. Hierfür arbeitet ein Hintergrundprozess diese Events ab. Kommt ein CustomerCreated Event an, so wird z.B. in der Kunden Tabelle ein Insert gemacht.

Kommt ein CustomerRemoved event wird in der Tabelle ein Delete gemacht. usw..

Der Interessante punkt ist dass aus einem Customer nun mehrere Facts generiert werden können.

Der Hintergrund prozess kann sowohl in in eine SQL Datenbank, als auch in eine Graph Datebank , in outlook oder was auch immer wir wollen schreiben. Und das simultan !

Aus den Events erzeugen wir also eine Vielzahl von Abbildungen die genau Passend für unsere Views sind.

So können wir auch viele Werte im Hintergrund voraggregieren oder formatieren. Häufig sind die Queries für eine Anzeige nur noch ein einzelnes Statement. Das Joinen von Daten aus mehreren Quellen wird erstmal nicht mehr benötigt.

Das Bringt aber auch ein Themen auf unseren Zettel den wir bei simplen CRUD anwendung nicht hatten.

Was sind Hürden beim CQRS?

  1. Eventual Consistency
    Geschriebene Daten sind u.U. nicht direkt für das Lesen vorhanden
  2. Komplexität
    Bereiche die mit CQRS implementiert werden sind zumindest anfänglich komplexer

Warum also könnte CQRS trotzdem interessant sein?

1.Skalierung
Man kann besser Skalieren, da read und write getrennte Prozesse sind die man getrennt skalieren kann.

2. Performance

Generell profiteirt die Performance stark von den Gretrennten prozessen. Es müssen nicht mehr viele WErte on Demand ermittelt und aufbereitet werden. Es wird i.d.R. angezeigt was da ist. Die Modelle sind falch und in ihrer Datenhaltung passend vor indiziert.

Ausserdem kann man für jede Anforderung die beste passende Datenhaltung nehmen uind pot. einfach austauschen.

3. Audit Log

Alle sichtbaren Daten / Facts wurdenb aus dem EventLog generiert. Dieses Eventlog ist append only. Es kann also zu jedem Wert jeder Entität genau geagt werden wie er zustande kam.

Manipuliert jemand einen Wert in der Datenbank. Wird er mit dem nächstne Event oder der nächsten Generiung behoben.  Das ist übrigens ein System das sehr an Banken erinnert…

4. Busines Orientierung

Das Schreiben nur aus Events besteht. Sind diese Events sehr nach an dem was aus Businesssicht passiert. Man kann diese Events Businessentscheinder einfach vorlesen. „Kunde ist Verzogen“, „Kunde wurde angelegt“ usw..

Daraus entwickelten sich z.b. Ideen wie das EventStorming

5. Konflikt Management

Entstehen Änderungskonflikte (z.B. in hierarchischen Systemen ) kann man gezielter reagieren. Man erkennt dass nicht nur 2 Felder sich wiedersprechende Änderungen haben sondern man erkennt die Intention hinter der Änderung..

Man hat nur nur die Information dass 2 Bearbeiter das Feld Straße wiedersprüchlcih geändert haben. Würde zum beispiel folgendes sehen:

„Kunden-AusHandschrift-Notiz-erfasst“ event vs. „Kunden-Daten-Aus-Meldebehörde“ und kann nun business rules für genau diesen Fall erstellen und einen Teil der Konflikte automatisch handeln.

 

Resume

Es gibt noch viel mehr zu sagen. Zum Beispiel das in einem Eventlog niemals etwas geändert oder gelöscht werden darf. Es ist Appendonly.

Oder das die Eventual Consistency meist gar kein Problem ist.

Oder das CQRS sich sehr gut mit DDD , insbeosndere mit den BoundContextes vereinen lässt

Oder das man mit einem Entsprechenden CommonDomain Model eine sehr hohe und sehr einfache Testbarkeit bekommt. usw usw….

Doch für jetzt soll es erstmal genug sein und wirken können.

Wer sich mal im EventSourcen direkt probieren möchte dem möchte ich GregYoungs EventStore ans Herz legen. Man kann mit allem EventSourcing betreiben, selbst mit einer reihe von JsonFiles😉 Aber man hat einen wesentlcih leichteen und besseren Einstieg wenn man sich bestehende Lösungen ansieht.

In diesem Zusammenhang möchte ich zum Abschluss noch einen inspirierenden Webcast von Greg Young sehr empfehlen:

 

Und hier einen Rückblick auf 10 Jahre CQRS

See also

5 Schritte zum Hello World mit TypeScript & VS2015

Nachfolgend der einfachste Weg um mit TypeScript zu einem schnellen Hello World zu gelangen.

Es sei noch angemerkt dass es durchaus streitbar ist ob man die TypeScript Compile Optionen der IDE nutzen sollte oder ob eigene Tasks (z.b. mit Gulp o.ä. ) in einer produktiven Umgebung nicht mehr Sinn machen.

  1. VS 2015 Installations Package Installieren > https://www.typescriptlang.org/#download-links
  2. Nach der Installation kann man für ein einfaches HelloWorld Programm das TypeScript Project Template wählenTypeScriptProject
  3. Auf den Projekt Eigenschaften findet man nun einen TypeScript reiter mit diversen Einstellmöglichkeiten. Für einen einfachen Start kann man die „Compile on Save“ Option aktivieren damit aus aus den TypeScript (*.ts) JavaScript Files (*.js) erzeugt werdenTypeScriptProjectOptions.png
  4. In dem angelegten Projekt findet man nun die app.ts welche einen einfachen TypeScript Code für das Ermitteln der Uhrzeit beinhaltet.

app_ts_starter

 

5. Wenn man nun das Projekt ausführt sieht man die Seite mit der aktuellen forlaufenden Uhrzeit über JavaScript

TypeScriptHelloWorld

 

Greg Young about Over-Engeneering

Bei Youtube bin ich über eine Keynote von Greg Young gestoßen. Link findet ihr darunter.

Die Session trifft einen der wichtigsten Punkte der Softwareentwicklung.

Nachfolgend einige Gedanken aufgegriffen, die mich schon länger beschäftigen und in dieser Session zur Sprache kamen.

Software ist Teil eines Ökosystems. Software wird implementiert um Problem lösen, aber nicht jedes!

90% Aufwand für ein 0,1% Feature ist Ineffizient. Software wird entwickelt um produktive Ergebnisse zu haben, und nicht zuletzt um Geld zu verdienen.

Nur allzu leicht wird vergessen, dass der Motivator eines SoftwareProjektes ein Business Case ist. Und Business bedeutet Riskmanagement. Kein Business ohne Risiko. Business bedeutet auch das man mehr Braucht als eine laufende Software. Wenn eine Software für Edge Cases manuelle  Eingriffe braucht ist das richtig, wenn sie wirtschaftlich sind.

Wenn der CEO einmalig Daten in einem bestimmten Format aus der Datenbank braucht und unsere Standardtools das Format zu   95% erfüllen ist es ok, wenn jemand diese Daten nachbearbeitet.

Es wird nicht in Tagelangen Aufwand ein Tool entwickelt, dasss nur  die restlichen 5 Minuten Arbeit automatisiert.

Das bedeutet nicht dass wir unsichere instabile Software schreiben sollen, sondern viel mehr dass  es Fälle geben kann und darf bei denen ein User , Administrator… eingreifen darf.

Hätten die GitEntwickler versucht ein MergeConflict freies DVCS zu entwickeln, dann hätten wir heute noch keins. So schön so ein System auch wäre, dieser anspruch hätte im Ergebnis nur zum Scheitern geführt.

Eine große Veränderung die stillschweigend passierte ist, dass wir SoftwareEntwickler, Architekten, CTO’s, CEOS,…. miteinander reden müssen.
Die besten Lösungen sind manchmal außerhalb der Softwareentwicklung.

Hieraus erfolgt auch die Verantwortung bewusst und nicht leichtfertig Features und Bugs auch mal zu streichen. Es ist verführerisch unliebsame Bugs oder Anforderungen als Ineffizient abzustempeln.

Erfolgreiche Firmen gehen bewusst und rational überlegt Risiken ein. Es werden bewusst ineffiziente Features und Probleme gestrichen und auch Lösungen ausserhalb der SoftwareEntwicklung gesucht.

Frei nach dem Zitat:

Perfektion ist nicht dann erreicht, wenn es nichts mehr hinzu zu fügen gibt, sondern wenn man nichts mehr weglassen kann.– Antoine de Saint-Exupéry, Terre des Hommes

 

 

Cancelation Token in ASP Core Middleware

In klassischen ASP MVC / Web Api Projekte konnte man als Parameter ein CancellationToken definierne, welches dann beim Aufruf automatisch injiziiert wurde.

Das Token wurde dann in den CancelState versetzt falls der Request abgebrochen wurde.

In ASP Core hat das nicht funktioniert . Bei StackOverflow wurde mir geholfen.

Über context.RequestAborted kann man sich ein Token ableiten.

CancellationToken CancellationToken => context?.RequestAborted ?? CancellationToken.None;

 

RX geänderte NuGet Pakete

Wer bereits Rx genutzt hat und sich wundert, kann sich gerade wundern, dass die Pakete nicht mehr verfügbar sind:

Auf der Nuget Seite steht:

The owner has unlisted this package. This could mean that the package is deprecated or shouldn’t be used anymore.

Allerdings gibt das Github repo Aufschluss. Das Paket ist nun als System.Reactive verfügbar.

The NuGet packages have changed their package naming in the move from v2.x.x to v3.0.0

  • Rx-Main is now System.Reactive
  • Rx-Core is now System.Reactive.Core
  • Rx-Interfaces is now System.Reactive.Interfaces
  • Rx-Linq is now System.Reactive.Linq
  • Rx-PlatformServices is now System.Reactive.PlatformServices
  • Rx-Testing is now Microsoft.Reactive.Testing

Lt. Dokumentation sind keine Breaking changes bekannnt.

Die Projekte werden wegen des entfernten NugetListings nicht mehr im VS NugetPackageManager angezeigt.

Allerdings kann man sie immer noch über die NugetPackage Console installieren:

Install-Package Rx-Core

Owin Middleware Cultures .net 4.6.1

In einem Projekt nutzen wir eine selbst geschriebene Owin Middleware, welche anhand der RequestUrl eine Culture ermittelt und diese setzt.

So ist z.B. MyDomain.de auf die culture de-de  und MyDomain.fr auf fr-fr gemappt.

Der Code ist sehr trival

 public override async Task Invoke(IOwinContext context)
    {
        var culture cultureFromUrl(context)
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;

        await Next.Invoke(context);
    }

Das hat auch sehr gut funktioniert bis der Code auf .net 4.6.1 geupdatet wurde.Seit diesem Moment wirkt sich die kultur änderung nicht mehr auf den restlichen Owinstack aus.

Ursache ist ein Feature, welches CultureÄnderungen eines Threads von anderen isoliert.

Sofern man nicht eine neue Strategie entwickeln will muss dieses Feature deaktiviert werden.

Um das zu erreichen kann man das Feature über ein Compatibility  Switch deaktivieren

   <add key=“appContext.SetSwitch:Switch.System.Globalization.NoAsyncCurrentCulture“ value=“true“ />

Hier noch der link zu dem Microsoft Connect Ticket und meinem SO post

Dieser Breaking Change ist etwas versteckt unter dieser URL Dokumentiert: https://github.com/Microsoft/dotnet-apiport/blob/master/docs/BreakingChanges/146%20CurrentCulture%20flows%20across%20Tasks.md

 

Artikel über ServiceStack

Auch wenn es hier im BLog leider etwas länger ruhig war, so war ich jedoch nicht untätig.

Um hier mal wieder etwas in Gang zu kommen nachfolgend ein Link zum Artikel Ein Stapel Wunder von Roberto Bez und mir in der dotnetpro. Dies ist  mein erster Artikel für eine Fachzeitschrift und bin schon sehr gespannt ob er den versierten Erwartungen der dotnetpro Lesern gerecht wird.

Weiterlesen