User Tools

Site Tools


concepts:setbasedvalidation

This is an old revision of the document!


Regeln für Mengen von Aggregates

Es gibt verschiedene Arten von Mengen basierten Regeln. Im einfachsten Fall baut man einfach ein Aggregate für die Menge, mit Entitäten, die die einzelnen Aggregates referenzieren. Wenn z.B. die Projektnummer eindeutig sein muss, hat das Project Aggregate selbst keine Projektnummer. Es gibt ein ProjectKeyRegistry Aggregate, in dem die Projektnummern eindeutig gehalten werden. Jede Projektnummer im Aggregate ist eine Entität, welche ein Project Aggregate referenziert.

Projektionen können nun für das Read Model ein Project aufbauen, indem das Project Aggregate und das ProjectKeyRegistry Aggregate kombiniert werden1).

Problematisch wird es nur, wenn auch das Project Aggregate Regeln bezüglich der Nummer hat. Der Command Handler könnte bei Bedarf die Nummer aus dem Read Model an die Methoden des Project Aggregates übergeben, aber das wäre dann nur noch schließlich konsistent. Zudem möchte man hier und da Änderungen über das Project Aggregate steuern, d.h. wenn der Projektstatus eine Änderung der Projektnummer nicht zulässt, soll man auch nicht über die ProjectKeyRegistry einfach die Nummer ändern können.

Also müsste man ein ProjectSystem Aggregate bauen, durch das alles läuft, und Projekte sind immer nur Entitäten. Das wäre die konsistenteste Lösung. Es geht aber darum, im CAP einen vernünftigen Kompromiss zwischen der Konsistenz2), der Verfügbarkeit3) und der Partitionstoleranz4) zu finden. Wenn man zuviel Gewicht auf die Konsistenz legt, leiden die anderen beiden Punkte.

Wenn die Aussage also ist, dass es auf keinen Fall zwei gleiche Projektnummern geben darf, dann muss man für die Projektnummer die Konsistenz auf 100% halten, also die Projektnummernvergabe für alle Projekte serialisieren. Das heißt aber nicht, dass man alles das Projekt betreffend serialisieren muss. Die Projektnummer wird i.d.R. einmal vergeben, vieles Andere wird öfters geändert.

Ich möchte als Anwender nun auch mehrere Aktionen das Projekt betreffend abschicken, und erwarte, dass entweder alle oder keine durchgeführt wird. Wie geht so was?

Reservation Pattern mit einem kompensierenden Prozess

Das sieht ja erst mal kompliziert aus für solch eine simple Aufgabe. Wie läuft das ab?

public class Project: ProjectBase
{
  public void SetProjectKey(string projectKey, IProjectKeyRegistry registry)
  {
    // eigene Prüfungen
    var reservationNumber = registry.Register(Id, projectKey); // asynchron, wird also gespeichert
    ApplyChange(new ProjectKeySet(reservationNumber, projectKey);
  }
}

Im Erfolgsfall ist das das schönste Modell. Der Anwender schickt einen Befehl, der an das Projekt gerichtet ist 5), und weiß, dass die Projektnummer geändert wurde wenn der Befehl fehlerfrei durchgelaufen ist.

Aus der Reservierung eine Registrierung machen

Wenn ProjectKeyReserved oder ProjectKeySet erhalten wurde6), muss eine SAGA gestartet werden, die für die Project Id läuft. Sobald auch das zweite dieser Ereignisse erhalten wurde, kann die SAGA der ProjectKeyRegistry mitteilen, dass sie aus der Reservierung eine Registrierung machen soll.

Womit wird die Projektnummer im Read Model gesetzt? Mit ProjectKeySet vom Projekt, oder mit ProjectKeyRegistered von der Projektnummer-Registratur?

Die Registratur ist ja im Grunde nur dafür da, eindeutige Projektnummern zu verifizieren. Was alles andere Betrifft, ist die Projektnummer relevant, die im Projekt steht, und nicht in der Registratur. Das Read Model aller Projekte ist nur schließlich konsistent, d.h. es könnten im Read Model doppelte Projektnummern erscheinen7).

Fehlerfall

Es kann passieren, dass im Methodenaufruf von oben die Projektnummer reserviert wird, aber nie verwendet wird8). Die reservierte Projektnummer muss in dem Fall storniert werden, damit sie wieder verwendet werden darf - im Normalfall wird derselbe Befehl nochmals versucht.

Dafür ist die SAGA da. Sie wurde mit ProjectKeyReserved gestartet9). Die SAGA wartet nun nicht einfach auf ProjectKeySet, sondern hat einen eingebauten Nerv Faktor10). Sie sendet in dem Fall den Befehl ConfirmProjectKeyReservation an das Projekt. Im Projekt passiert folgendes:

public class Project: ProjectBase
{
  public void ConfirmProjectKeyReservation(string reservationNumber)
  {
    if (!ProjectKeySetWithReservation(reservationNumber))
      ApplyChange(new ProjectKeyReservationNotUsed(reservationNumber));
  }
}

Wenn das Projekt die Reservierungsnummer verwendet hat, macht die Methode nichts. In dem Fall wurde bereits ProjektKeySet mit der Reservierungsnummer durchgeführt, und die SAGA wird das Ereignis früher oder später erhalten, um aus der Reservierung eine Registrierung zu machen. Wenn das Projekt die Reservierungsnummer aber nicht verwendet hat, wird es der SAGA über ProjectKeyReservationNotUsed mitteilen, dass es diese Reservierungsnummer nicht verwendet hat. Daraufhin kann die SAGA der Registratur den Befehl erteilen, die Reservierung zu stornieren.

Fehler in der Projektanlage

Was aber, wenn die Projektnummer während der Projektanlage reserviert wurde, das Projekt aber nicht angelegt wurde? ConfirmProjectKeyReservation läuft dann ins Leere. In dem Fall kann die Reservierung storniert werden, das Projekt wurde ja nicht angelegt.

FALSCH!

Es könnte sein, dass der Handler für die Projektanlage einfach noch nicht fertig ist, zu dem Zeitpunkt, da ConfirmProjectKeyReservation von einem anderen Handler abgearbeitet wird. Wenn jetzt die Reservierung storniert wird, die Projektanlage dann aber doch statt findet, wurde sie fälschlicherweise gelöscht.

Wenn der Handler also ConfirmProjectKeyReservation behandeln soll, muss er das Projekt anlegen, wenn er es nicht findet! Nur so kann verhindert werden, das die Reservierung gleichzeitig storniert wird, und verwendet wird. Entweder schlägt die Anlage des Handlers für ConfirmProjectKeyReservation fehl, weil der andere Handler mittlerweile das Projekt mit der Id gespeichert hat, oder die Anlage klappt, und der Handler für die Projektanlage läuft auf einen Fehler.

Handler für die Frage nach der Bestätigung einer Reservierung

public class ProjectCommandHandler: CommandHandler, Handles<ConfirmProjectKeyReservation>
{
  public void Handle(ConfirmProjectKeyReservation message)
  {
    var rep = new Repository<Project>();
    var proj = rep.Get(message.Id);
    if (proj == null) 
  }
1) bzw. die Ereignisse aus diesen beiden Aggregates
2) Consistency
3) Availability
4) Partition tolerance
5) und nicht an die ProjectKeyRegistry
6) Das erste Ereignis kommt von der Registry, das zweite vom Projekt. Die Reihenfolge, in der sie erhalten werden, ist also nicht fest
7) Wenn Projekt 1 erst die Nummer 123 hat, die dann in 321 geändert wird, und Projekt 2 danach die Nummer 123 erhält - vorausgesetzt die Registratur befreit Projektnummern, die geändert werden. Nun könnte Projekt 1 im Read Model theoretisch noch nicht aktuell sein und wie Projekt 2 auch die 123 anzeigen –> doppelte Projektnummer
8) z.B. Infrastrukturfehler
9) Nicht mit ProjectKeySet, da das Ereignis im Fehlerfall ja gar nicht stattgefunden hat
10) “Sind wir schon da?”, “Sind wir schon da?”, “Sind wir schon da?”
concepts/setbasedvalidation.1363186496.txt.gz · Last modified: 2013/03/13 15:54 by rtavassoli