Hallo,
Vielen Dank, dass Sie diesen Artikel über CQRS geschrieben haben. Ich versuche zu verstehen, wie die Implementierung aussieht, und habe die Beispiele durchgearbeitet. Der im Beispiel bereitgestellte Beispiel-Repository-Code sieht sehr ähnlich aus, wie normalerweise ein Repo mit Ausnahme der Namenskonvention geschrieben wird. Zum Beispiel denke ich, dass das unten angegebene Repository dasselbe ist wie ProductsCommandHandler
und wahrscheinlich wäre ein Unterschied, dass es hier kein GetProduct
gibt, das wir normalerweise hinzufügen würden. Könnten Sie bitte erklären, was ich hier nicht bekomme?
public class ProductRepository {
void AddNewProduct(Product newProduct) {
...
}
void RateProduct(int productId, int userId, int rating) {
var product = repository.Find(productId);
if (product != null)
{
product.RateProduct(userId, rating);
repository.Save(product);
}
}
}
⚠ Bearbeiten Sie diesen Abschnitt nicht.
@martinmthomas Vielen Dank für Ihre Frage! Wir werden dies überprüfen und gegebenenfalls ein Update bereitstellen.
@ MikeWasson irgendwelche Gedanken hier?
AB # 160217 - Vielen Dank für derzeit geprüft
@martinmthomas Tatsächlich ist der Befehlshandler kein Repository. Es verwendet ein Repository.
Der Befehlshandler soll "gegebenenfalls die eigentlichen Befehle verarbeiten". Im Beispiel ist die ProductsCommandHandler
erhält Klasse ein IRepository<Product>
in seinem Konstruktor.
Angenommen, der Benutzer bewertet das Produkt 5555 mit 4 Sternen.
{"product":"5555","stars":4}
RateProduct
und füllt das Produkt 5555 mit den Sternen 4 aus. Im Beispiel wird auch angegeben, wer bewertet wird.CommandHandler
in Ihrem Controller (wahrscheinlich durch Abhängigkeitsinjektion) und platzieren einfach den Befehl dort: h.Handle (c); Dabei ist c der Befehl RateProduct.Also 3 Optionen: a) Holen Sie sich den Handler, b) Holen Sie sich die Warteschlange, c) Holen Sie sich einen Bus und lassen Sie ihn entscheiden, den Befehl an den Handler oder an die Warteschlange zu senden.
Was auch immer Sie tun ... Der Hauptunterschied zu "einem klassischen Repository" besteht darin, dass Sie NIEMALS das Repository selbst im Controller aufrufen. Der Controller weiß nicht, wie Entitäten verwaltet werden (klassisches Repository), aber er weiß, wie man Entitäten verwaltet, um Entitäten Dinge zu tun (Befehlshandler).
Dann ist es der Befehlshandler, der - wie der Name schon sagt - den Befehl "verarbeitet", der von irgendwoher kam (ein HTML-Webcontroller, ein API-Controller, eine Befehlszeile, was auch immer), der als Absicht gesendet wurde, und es ist der CommandHandler (die zur Schreibseite gehört), die entscheidet, was mit diesem Befehl geschehen soll.
Beispielsweise kann der CommandHandler ein Repo verwenden, um das Produkt abzurufen, den Status festzulegen und zu speichern (wie im Beispiel), aber er kann auch in ein Ereignisprotokoll schreiben, Elemente zum Aktualisieren der Leseseiten auslösen, externe Konnektoren auslösen oder was auch immer.
Die aufgabenbasierte Benutzeroberfläche des Benutzers, um das Produkt und seinen Controller als AGNOSTISCH zu bewerten, "wo" das Sterne- / Bewertungssystem platziert oder gespeichert ist. Stellen Sie sich vor, Sie entwerfen ein System von Grund auf neu und die Produktklasse enthält bereits die RateProduct () -Methode wie im Beispiel. Gut.
Aber ... was ist, wenn Sie dort ein Legacy-System mit einem "alten" Ansatz für das Produkt haben? Im "Modell" (dh: Business Mind) gibt es keine "Bewertung" des Produkts. Stattdessen möchte der Marketing-Mitarbeiter das vorhandene Produkt "bewerten", aber das gesamte Unternehmen stimmt zu, dass dies eine "externe Sache" ist. Würden Sie das Produkt-Repository verwenden? Oder vielleicht ein anderer Hilfsspeicher, damit Sie das bereits funktionierende und vollständig getestete Produkt und ProductRepository nicht "berühren"?
Wenn Sie Befehle verwenden, macht es dem Schreibcontroller nichts aus. Das Web, die API und die CLI senden den "Befehl" einfach an den Befehlshandler (entweder direkt, über die Warteschlange oder über einen Befehlsbus, der wiederum zum Handler oder zu einer Warteschlange weitergeleitet wird), und sie vergessen. Anschließend entscheidet der Befehlshandler, was mit dem "RateProductCommand" an einem zentralen und kleinen Punkt Ihres Quellcodes zu tun ist. Dadurch wird die Art und Weise, wie dies behandelt wird, vom Anwendungscode entkoppelt, wodurch die Benutzerfreundlichkeit erhöht wird.
Dann entscheidet der Handler, ob es geeignet ist, das ProductRepository oder einen anderen Ansatz zum Speichern der "Bewertung eines Produkts" zu verwenden.
Also, um zu antworten:
CommandHandler => hat nichts mit den Berechtigungen zu tun. Es behandelt die "Absichten des Benutzers" (die letztendlich darin bestehen könnten, Entitäten zu ändern; daher verwendet der Befehlshandler höchstwahrscheinlich ein Repository).
Repository => der tatsächliche Speicher für eine bestimmte Entität.
Ich hoffe zu helfen.
Xavi.
Wie @xmontero erwähnt, haben ProductsCommandHandler und ProductRepository nur eine Beziehung "has". ProductsCommandHandler fungiert als Schnittstelle zwischen ProductApi / Controller und ProductRepository. Diese Implementierung hilft dabei, Befehle von Abfragen getrennt zu halten. Beim herkömmlichen Ansatz verwenden wir direkt das Repository des Controllers, um Befehle und Abfragen zusammen auszuführen, sodass keine andere Schnittstelle erforderlich ist.
Sie haben das CQRS-Entwurfsmuster in Ihrer Anwendung angewendet, wenn Sie Befehle von Abfragen getrennt halten. Es führt Sie in die Richtung, alle in den Artikeln genannten Vorteile von CQRS zu nutzen.
Hilfreichster Kommentar
@martinmthomas Tatsächlich ist der Befehlshandler kein Repository. Es verwendet ein Repository.
Der Befehlshandler soll "gegebenenfalls die eigentlichen Befehle verarbeiten". Im Beispiel ist die
ProductsCommandHandler
erhält Klasse einIRepository<Product>
in seinem Konstruktor.Angenommen, der Benutzer bewertet das Produkt 5555 mit 4 Sternen.
{"product":"5555","stars":4}
RateProduct
und füllt das Produkt 5555 mit den Sternen 4 aus. Im Beispiel wird auch angegeben, wer bewertet wird.CommandHandler
in Ihrem Controller (wahrscheinlich durch Abhängigkeitsinjektion) und platzieren einfach den Befehl dort: h.Handle (c); Dabei ist c der Befehl RateProduct.Also 3 Optionen: a) Holen Sie sich den Handler, b) Holen Sie sich die Warteschlange, c) Holen Sie sich einen Bus und lassen Sie ihn entscheiden, den Befehl an den Handler oder an die Warteschlange zu senden.
Was auch immer Sie tun ... Der Hauptunterschied zu "einem klassischen Repository" besteht darin, dass Sie NIEMALS das Repository selbst im Controller aufrufen. Der Controller weiß nicht, wie Entitäten verwaltet werden (klassisches Repository), aber er weiß, wie man Entitäten verwaltet, um Entitäten Dinge zu tun (Befehlshandler).
Dann ist es der Befehlshandler, der - wie der Name schon sagt - den Befehl "verarbeitet", der von irgendwoher kam (ein HTML-Webcontroller, ein API-Controller, eine Befehlszeile, was auch immer), der als Absicht gesendet wurde, und es ist der CommandHandler (die zur Schreibseite gehört), die entscheidet, was mit diesem Befehl geschehen soll.
Beispielsweise kann der CommandHandler ein Repo verwenden, um das Produkt abzurufen, den Status festzulegen und zu speichern (wie im Beispiel), aber er kann auch in ein Ereignisprotokoll schreiben, Elemente zum Aktualisieren der Leseseiten auslösen, externe Konnektoren auslösen oder was auch immer.
Die aufgabenbasierte Benutzeroberfläche des Benutzers, um das Produkt und seinen Controller als AGNOSTISCH zu bewerten, "wo" das Sterne- / Bewertungssystem platziert oder gespeichert ist. Stellen Sie sich vor, Sie entwerfen ein System von Grund auf neu und die Produktklasse enthält bereits die RateProduct () -Methode wie im Beispiel. Gut.
Aber ... was ist, wenn Sie dort ein Legacy-System mit einem "alten" Ansatz für das Produkt haben? Im "Modell" (dh: Business Mind) gibt es keine "Bewertung" des Produkts. Stattdessen möchte der Marketing-Mitarbeiter das vorhandene Produkt "bewerten", aber das gesamte Unternehmen stimmt zu, dass dies eine "externe Sache" ist. Würden Sie das Produkt-Repository verwenden? Oder vielleicht ein anderer Hilfsspeicher, damit Sie das bereits funktionierende und vollständig getestete Produkt und ProductRepository nicht "berühren"?
Wenn Sie Befehle verwenden, macht es dem Schreibcontroller nichts aus. Das Web, die API und die CLI senden den "Befehl" einfach an den Befehlshandler (entweder direkt, über die Warteschlange oder über einen Befehlsbus, der wiederum zum Handler oder zu einer Warteschlange weitergeleitet wird), und sie vergessen. Anschließend entscheidet der Befehlshandler, was mit dem "RateProductCommand" an einem zentralen und kleinen Punkt Ihres Quellcodes zu tun ist. Dadurch wird die Art und Weise, wie dies behandelt wird, vom Anwendungscode entkoppelt, wodurch die Benutzerfreundlichkeit erhöht wird.
Dann entscheidet der Handler, ob es geeignet ist, das ProductRepository oder einen anderen Ansatz zum Speichern der "Bewertung eines Produkts" zu verwenden.
Also, um zu antworten:
CommandHandler => hat nichts mit den Berechtigungen zu tun. Es behandelt die "Absichten des Benutzers" (die letztendlich darin bestehen könnten, Entitäten zu ändern; daher verwendet der Befehlshandler höchstwahrscheinlich ein Repository).
Repository => der tatsächliche Speicher für eine bestimmte Entität.
Ich hoffe zu helfen.
Xavi.