Ich habe einen Teil des CQRS-Musters mit der folgenden S # arp-Architektur implementiert :
public class MyCommand
{
public CustomerId { get; set; }
// some other fields
}
public class MyCommandHandler<MyCommand> : ICommandHandler<MyCommand, CommandResult>
{
Handle(MyCommand command)
{
// some code for saving Customer entity
return CommandResult.Success;
}
}
Ich frage mich, warum es nicht einfach eine Klasse gibt, Command
die sowohl Daten als auch Handhabungsmethoden enthält. Ist dies eine Art Testbarkeitsvorteil, bei dem Sie die Befehlsverarbeitungslogik getrennt von den Befehlseigenschaften testen müssen? Oder handelt es sich um eine häufige Geschäftsanforderung, bei der ein Befehl von verschiedenen Implementierungen von verarbeitet werden muss ICommandHandler<MyCommand, CommandResult>
?
c#
design-patterns
architecture
cqrs
Greifer
quelle
quelle
Antworten:
Witzig, diese Frage erinnerte mich nur an genau dasselbe Gespräch, das ich mit einem unserer Ingenieure über die Kommunikationsbibliothek geführt hatte, an der ich arbeitete.
Anstelle von Befehlen hatte ich Request-Klassen und dann RequestHandler. Das Design war sehr ähnlich zu dem, was Sie beschreiben. Ich denke, ein Teil der Verwirrung, die Sie haben, ist, dass Sie das englische Wort "command" sehen und sofort "verb, action ... etc" denken.
Stellen Sie sich Command (oder Request) in diesem Entwurf jedoch als Buchstaben vor. Oder für diejenigen, die nicht wissen, was ein Postdienst ist, denken Sie an E-Mail. Es ist einfach Inhalt, entkoppelt davon, wie auf diesen Inhalt reagiert werden soll.
Wieso würdest du das machen? In den meisten einfachen Fällen gibt es für Command Pattern keinen Grund, und Sie könnten diese Klasse direkt arbeiten lassen. Die Entkopplung wie in Ihrem Entwurf ist jedoch sinnvoll, wenn Ihre Aktion / Ihr Befehl / Ihre Anforderung eine gewisse Strecke zurücklegen muss. Zum Beispiel über Sockets oder Pipes oder zwischen Domäne und Infrastruktur. Oder vielleicht müssen Ihre Befehle in Ihrer Architektur persistent sein (z. B. kann der Befehlshandler aufgrund einiger Systemereignisse jeweils 1 Befehl ausführen, 200 Befehle treffen ein und nach dem Herunterfahren der ersten 40 Prozesse). In diesem Fall ist es mit einer einfachen Nur-Nachrichten-Klasse sehr einfach, nur den Nachrichtenteil in JSON / XML / binary / whatever zu serialisieren und über die Pipeline weiterzuleiten, bis der Befehlshandler bereit ist, ihn zu verarbeiten.
Ein weiterer Vorteil der Entkopplung von Command von CommandHandler besteht darin, dass Sie jetzt die Möglichkeit haben, eine parallele Vererbungshierarchie zu erstellen. Beispielsweise könnten alle Ihre Befehle von einer Basisbefehlsklasse abgeleitet werden, die die Serialisierung unterstützt. Und vielleicht haben Sie 4 von 20 Befehlshandlern, die eine große Ähnlichkeit aufweisen, jetzt können Sie die von der Came-Handler-Basisklasse ableiten. Wenn Sie Daten- und Befehlsverarbeitung in einer Klasse haben würden, würde diese Art von Beziehung schnell außer Kontrolle geraten.
Ein weiteres Beispiel für die Entkopplung wäre, wenn Ihr Befehl nur sehr wenige Eingaben erfordert (z. B. 2 Ganzzahlen und eine Zeichenfolge), die Verarbeitungslogik jedoch so komplex ist, dass Sie Daten in den Variablen der Zwischenelemente speichern möchten. Wenn Sie 50 Befehle in die Warteschlange stellen, möchten Sie nicht den gesamten Zwischenspeicher belegen. Trennen Sie daher Command von CommandHandler. Jetzt stellen Sie 50 leichte Datenstrukturen in die Warteschlange, und komplexerer Datenspeicher wird vom CommandHandler, der die Befehle verarbeitet, nur einmal (oder N-mal, wenn Sie N-Handler haben) zugewiesen.
quelle
Bei einem normalen Befehlsmuster geht es darum, Daten und Verhalten in einer Klasse zu haben. Diese Art von 'Command / Handler-Muster' unterscheidet sich geringfügig. Der einzige Vorteil gegenüber dem normalen Muster ist der zusätzliche Vorteil, dass Ihr Befehl nicht von Frameworks abhängt. Beispielsweise benötigt Ihr Befehl möglicherweise DB-Zugriff, sodass er über einen DB-Kontext oder eine DB-Sitzung verfügen muss, was bedeutet, dass er von Frameworks abhängig ist. Dieser Befehl kann jedoch Teil Ihrer Domain sein, sodass Sie nicht möchten, dass er von den Frameworks gemäß abhängt Dependency Inversion Principle entsprechen . Dies kann behoben werden, indem die Eingabe- und Ausgabeparameter vom Verhalten getrennt werden und ein Dispatcher sie verkabelt.
Auf der anderen Seite verlieren Sie den Vorteil sowohl der Vererbung als auch der Zusammensetzung von Befehlen. Was ich denke, ist es wahre Macht.
Auch kleiner Nitpick. Nur weil es Command im Namen hat, ist es nicht Teil von CQRS. Das ist etwas viel grundlegenderes. Diese Art von Struktur kann gleichzeitig sowohl als Befehl als auch als Abfrage dienen.
quelle