Frage

Ich kämpfe immer noch mit dem, was Grunde sein muss (und gelöst) Fragen im Zusammenhang mit CQRS Architektur:

Wie setzen wir Geschäftsregeln, die auf einen Satz verlassen von Aggregaten Roots?

Nehmen wir als Beispiel eine Buchungsanwendung. Es können Sie ermöglichen, Tickets für ein Konzert buchen Sitze für einen Film oder einen Tisch in einem Restaurant. In allen Fällen ist es nur sein würde eine begrenzte Anzahl von ‚Artikel‘ zu verkaufen.

Lassen Sie uns vorstellen, dass das Ereignis oder der Ort sehr beliebt ist. Wenn Verkäufe für ein neues Ereignis oder Zeitfenster zu öffnen, starten Reservierungen sehr schnell ankommen - vielleicht viele pro Sekunde

.

Auf der Abfrage Seite können wir massiv skalieren, und Reservierungen werden in eine Warteschlange gestellt durch eine autonome Komponente asynchron behandelt werden. Anfangs, als wir aus Reservation-Befehle aus der Warteschlange ziehen, werden wir sie akzeptieren, aber zu einem bestimmten Zeitpunkt werden wir Ablehnung der Rest beginnen müssen .

Wie können wir wissen, wenn wir die Grenze erreichen?

Für jede Reservierbefehl würden wir irgendeine Art von Geschäft zu Figur abfragen müssen, wenn wir die Anfrage aufnehmen können. Dies bedeutet, dass wir müssen wissen, wie viele Reservierungen haben wir bereits zu diesem Zeitpunkt erhalten.

Wenn jedoch die Domain Speicher haben ein nicht-relationalen Daten speichern, wie z.B. Windows Azure Tabellen Speicher, können wir nicht sehr gut tun, um ein SELECT COUNT(*) FROM ...

Eine Möglichkeit wäre, eine getrennte Aggregate Wurzel zu halten , die einfach den Überblick über die aktuelle Zählung hält, wie folgt aus:

  • AR: Reservierung (?, Wer wie viele)
  • AR: Event / Zeit-Slot / Datum (Gesamtzahl)

Die zweite Aggregat Wurzel wäre eine denormalized Aggregation des ersten sein, aber wenn die zugrunde liegenden Datenspeicher keine Transaktionen unterstützen, dann ist es sehr wahrscheinlich, dass diese synchron heraus können in High-Volume-Szenarien (das, was wir in erster Linie Adresse versuchen).

Eine mögliche Lösung ist auf serialize der Reservation-Befehle Handhabung, so dass nur einer nach dem anderen behandelt wird, aber das geht gegen unsere Ziele Skalierbarkeit (und Redundanz).

Solche Szenarien mich von Standard erinnern „ausverkauft“ Szenarien, aber der Unterschied ist, dass wir nicht sehr gut die Reservierung zurück Ordnung bringen. Sobald ein Ereignis ausverkauft ist, ist es ausverkauft, so kann ich nicht sehen, was eine kompensierende Wirkung wäre.

Wie können wir solche Szenarien umgehen?

War es hilfreich?

Lösung

Nach dem Nachdenken über diesen seit einiger Zeit es mir endlich dämmert, dass das zugrunde liegende Problem weniger auf CQRS verwendet, als es auf die ist nicht trasactional Natur disparaten REST-Service.

Wirklich kocht es für dieses Problem nach unten: Wenn Sie mehrere Ressourcen aktualisieren müssen, wie Sie Konsistenz Sie sicherstellen, wenn die zweite Schreiboperation ausfällt

Lassen Sie sich vorstellen, dass wir Updates zu Ressource A und Ressource B in der Reihenfolge geschrieben werden sollen.

  1. Resource A erfolgreich aktualisiert
  2. Der Versuch Ressource B zu aktualisieren versagt

Der erste Schreibvorgang kann nicht leicht gerollt wird angesichts einer Ausnahme zurück, so etwas können wir tun? Fangen und zur Unterdrückung der Ausnahme eine kompensierende Wirkung gegen Ressource A auszuführen ist kein gangbarer Weg. Zunächst einmal ist es komplex zu implementieren, aber zweitens ist es nicht sicher: was ist, wenn die erste Ausnahme wegen einer gescheiterten Netzwerkverbindung passiert ist passiert? In diesem Szenario können wir eine kompensierende Wirkung gegen Ressource A nicht schreiben auch nicht.

Der Schlüssel liegt in expliziten Idempotenz . Während Windows Azure Queues nicht garantieren genau einmal Semantik, sie garantieren mindestens einmal Semantik. Dies bedeutet, dass angesichts der intermittierenden Ausnahmen, später die Meldung wird wiedergegebenen .

Im vorherigen Szenario, das ist, was passiert, dann:

  1. Resource A aktualisiert wird versucht. Jedoch ist die Wiederholung so den Zustand eines erfassten nicht beeinträchtigt wird. Allerdings ist die 'Write' Operation erfolgreich.
  2. Resource B erfolgreich aktualisiert.

Wenn alle Schreiboperationen idempotent sind, Eventual Consistency mit Nachricht Replays erreicht werden .

Andere Tipps

Interessante Frage und mit dem man eine der Schmerzpunkte in CQRS nageln.

Die Art und Weise Amazon dies ist der Umgang ist durch das Geschäftsszenario mit einem Fehlerzustand zu bewältigen haben, wenn die angeforderten Artikel ist ausverkauft. Der Fehlerzustand wird einfach an den Kunden per E-Mail benachrichtigen, dass die Einzelteile auf Lager und die geschätzte Tag für den Versand ist nicht angefordert.

Allerdings -. Dies nicht in vollem Umfang Ihre Frage beantworten

Denken an einem Szenario Tickets zu verkaufen, ich sicher, dass der Kunde dem machen würde sagen, dass die Anfrage gaben sie waren eine Reservierungsanfrage . Dass die Reservierungs-Anfrage würde so schnell wie möglich erhalten und verarbeitet, dass sie dann die endgültige Antwort in einer Mail später wieder zu beleben. Durch alowing dies einige Kunden möglicherweise eine E-Mail mit einer Ablehnung, um ihre Anfrage bekommen.

Jetzt. Könnten wir diese rejestion machen weniger schmerzhaft? Bestimmt. Durch einen Schlüssel in unserem verteilten Cache mit dem Prozentsatz oder die Menge an Einheiten auf Lager Einsetzen und Erniedrigen dieses Zählers, wenn je ein Artikel verkauft wird. So können wir den Benutzer warnen konnte, bevor der Antrag auf Reservierung gegeben wird, sagen wir mal, wenn nur 10% der ursprünglichen Anzahl der Elemente gelassen wird, dass der Kunde nicht in der Lage sein könnte, die Artikel in Frage zu kommen. Wenn der Zähler auf Null steht würden wir einfach verweigern mehr Reservierungsanfragen zu akzeptieren.

Mein Punkt ist:

1) lassen den Benutzer wissen, dass es eine Anforderung, dass sie machen und dass dies verweigert bekommen 2) informieren den Benutzer, dass die Erfolgschancen für das Produkt in der Frage immer niedrig

Nicht gerade eine präzise Antwort auf Ihre Frage, aber das ist, wie ich ein Szenario, wie damit umgehen würde, wenn sie mit CQRS handelt.

Der ETAG ermöglicht Parallelität, die Sie anstelle von transaktionalen Sperr verwenden können, ein Dokument zu aktualisieren und sicher potentielle Rennbedingungen handhaben. Siehe Bemerkungen hier http://msdn.microsoft.com/en-us/library/ dd179427.aspx für weitere Informationen .

Geschichte könnte etwa so gehen: Benutzer A erstellt ein Ereignis E mit max Tickets von 2, ETAG 123. Aufgrund der hohen Nachfrage 3 Benutzer versuchen, die Tickets in fast der gleichen Zeit zu kaufen. Benutzer B erstellt eine Reservierungsanfrage B. Benutzer C schafft eine Reservierungsanfrage C. User-D schafft eine Reservierungs-Anfrage D.

System S empfängt Reservierungsanfrage B liest Ereignis ETAG 123 und ändert das Ereignis 1 verbleibende Ticket haben, trägt S das Update mit ETAG 123, die original ETAG passt so das Update erfolgreich ist. ETAG ist jetzt 456. Reservierungsanfrage genehmigt wird und Benutzer erfolgreich angemeldet.

Ein weiteres System S2 empfängt Reservierungsanfrage C zur gleichen Zeit als System S Anfrage B verarbeitet wurde, so ist es auch Ereignis das Ereignis ETAG 123 Änderungen auf 1 verbleibende Ticket und versucht, liest das Dokument zu aktualisieren. Dieses Mal jedoch die ETAG 123 stimmt nicht überein, so dass die Aktualisierung mit einer Ausnahme fehlschlägt. System S2 Versuche, den Betrieb durch erneute Lesen des Dokuments zu wiederholen, die jetzt hat ETAG 456 und eine Anzahl von 1, so dass es diese dekrementiert auf 0 und erneut sendet ETAG 456.

Leider ist für Benutzer C Benutzer gestartet System S Verarbeitungsanforderung Benutzer D's unmittelbar nach dem Benutzer B und auch Dokument mit ETAG 456 lesen, sondern weil das System S ist schneller als System S2 es in der Lage war die Veranstaltung mit ETAG 456, bevor das System S2 zu aktualisieren, so auch Benutzer D erfolgreich sein Ticket reserviert. ETAG ist jetzt 789

So-System S2 nicht wieder, gibt es noch einen Versuch, aber dieses Mal, wenn er das Ereignis liest ETAG 789 sieht es, dass es keine Tickets verfügbar und damit Benutzer C die Reservierungsanfrage verweigert.

Wie Sie Benutzer benachrichtigen, dass ihre Reservierungsanfragen erfolgreich waren oder nicht, ist Ihnen überlassen. Sie könnten nur den Server alle paar Sekunden abzufragen und warten, bis die Reservierungsstatus aktualisiert werden.

Lassen Sie uns einen Blick auf die Business-Perspektive (I beschäftigen in ähnliche Dinge - Anmeldung Termine auf freie Slots) ...

Das erste, was in Ihrer Analyse, die mich schlägt, wie aus zu sein, ist, dass es keine Vorstellung von einem reservierbar Ticket / Sitz / Tisch. Diese werden die Ressourcen gebucht werden.

Bei Transaktions ist, können Sie irgendeine Form von Einzigartigkeit verwenden, um sicherzustellen, dass eine doppelte Buchung nicht für das gleiche Ticket / Sitz / Tisch passiert (weitere Informationen unter http://seabites.wordpress.com/2010/11/11/consistent-indexes-constraints ). Dieses Szenario erfordert synchrone (aber immer noch konkurrierend) Befehlsverarbeitung.

Bei nicht transaktions zu sein, können Sie nachträglich den Ereignisstrom überwachen und den Befehl kompensieren. Sie können auch vom Endkunden für die Buchungsbestätigung einer Erfahrung des Wartens geben, bis das System sicher weiß - also nach dem Stromanalyse Ereignis -, dass der Befehl abgeschlossen und war oder nicht kompensiert (die „nach unten kocht wurde die Buchung? ja oder Nein?"). Mit anderen Worten könnte Entschädigung Teil des Bestätigungszyklus vorgenommen werden.

Lassen Sie uns Schritt zurück einige mehr ...

Wenn die Abrechnung als auch (zum Beispiel Online-Ticket-Verkauf) beteiligt ist, ich denke, das ganze Szenario entwickelt sich zu einer Saga sowieso (Reserve Ticket + Rechnung Ticket). Auch ohne Abrechnung, dann würden Sie eine Saga (Reserve Tabelle + bestätigen Reservierung) müssen die Erfahrung glaubhaft zu machen. Also auch wenn Sie nur in ein Ticket / Tisch / Sitz nur einen Aspekt sind das Zoomen der Buchung (dh es ist immer noch vorhanden), die „long-running“ Transaktion ist erst abgeschlossen, wenn ich dafür bezahlt oder bis ich es bestätigt . Die Entschädigung wird passieren sowieso, das Ticket wieder zu befreien, wenn ich die Transaktion für je nachdem, was Grund abbrechen. Der interessante Teil wird jetzt, wie das Unternehmen will damit umgehen: vielleicht einige andere Kunde die Transaktion abgeschlossen haben würde hatten wir ihn / sie mit dem gleichen Ticket gegeben. In diesem Fall könnte die Rückerstattung wird interessanter, wenn doppelte Buchung ein Ticket / Sitz / Tisch - auch einen Rabatt auf einer nächste / ähnliche Veranstaltung bietet für die Unannehmlichkeiten zu kompensieren. Die Antwort liegt in dem Geschäftsmodell, nicht das technische Modell.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top