Mam część wzorca CQRS zaimplementowanego przy użyciu architektury S # arp w następujący sposób:
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;
}
}
Zastanawiam się, dlaczego nie mieć klasy Command
zawierającej zarówno dane, jak i metodę obsługi? Czy jest to rodzaj korzyści z testowalności, gdy trzeba przetestować logikę obsługi poleceń niezależnie od właściwości poleceń? A może jest to częsty wymóg biznesowy, w którym musisz mieć jedno polecenie obsługiwane przez różne implementacje ICommandHandler<MyCommand, CommandResult>
?
c#
design-patterns
architecture
cqrs
rgripper
źródło
źródło
Odpowiedzi:
Zabawne, to pytanie przypomniało mi dokładnie tę samą rozmowę z jednym z naszych inżynierów na temat biblioteki komunikacji, nad którą pracowałem.
Zamiast poleceń miałem klasy Request, a następnie miałem RequestHandlers. Projekt był bardzo podobny do tego, co opisujesz. Myślę, że część zamieszania, które masz, polega na tym, że widzisz angielskie słowo „polecenie” i od razu myślisz „czasownik, działanie ... itd.”.
Ale w tym projekcie myśl o Dowództwie (lub Żądaniu) jak o liście. Lub dla tych, którzy nie wiedzą, czym jest usługa pocztowa, pomyśl o e-mailu. Jest to po prostu treść oddzielona od sposobu, w jaki należy na nią postępować.
Dlaczego miałbyś to zrobić? W najprostszych przypadkach wzorzec poleceń nie ma żadnego powodu, a klasa mogłaby bezpośrednio wykonywać pracę. Jednak wykonanie odsprzęgania jak w twoim projekcie ma sens, jeśli twoje działanie / polecenie / prośba musi przebyć pewien dystans. Na przykład w poprzek gniazd lub potoków lub między domeną a infrastrukturą. A może w twojej architekturze twoje polecenia muszą być trwałe (np. Moduł obsługi poleceń może wykonać 1 polecenie na raz, z powodu niektórych zdarzeń systemowych, przybywa 200 poleceń i po pierwszych 40 procesach zostaje zamknięty). W takim przypadku, mając prostą klasę tylko do wyświetlania komunikatów, bardzo łatwo jest serializować tylko część komunikatu do JSON / XML / binary / cokolwiek i przekazywać ją w potoku, dopóki program obsługi poleceń nie będzie gotowy do przetworzenia.
Kolejną zaletą oddzielenia polecenia od CommandHandler jest to, że teraz masz opcję równoległej hierarchii dziedziczenia. Na przykład wszystkie Twoje polecenia mogą pochodzić z podstawowej klasy poleceń obsługującej serializację. A może masz 4 z 20 programów obsługi poleceń, które mają wiele podobieństw, teraz możesz czerpać je z podstawowej klasy programu obsługi programów. Jeśli miałbyś mieć obsługę danych i poleceń w jednej klasie, ten rodzaj relacji szybko wymknąłby się spod kontroli.
Innym przykładem oddzielania byłby, gdyby twoje polecenie wymagało bardzo niewielkiej ilości danych wejściowych (np. 2 liczb całkowitych i ciągu), ale jego logika obsługi była wystarczająco złożona, gdzie chciałbyś przechowywać dane w pośrednich zmiennych składowych. Jeśli ustawisz w kolejce 50 poleceń, nie chcesz przydzielać pamięci dla całej pamięci pośredniej, więc oddzielasz Command od CommandHandler. Teraz ustawiasz w kolejce 50 lekkich struktur danych, a bardziej złożone przechowywanie danych jest przydzielane tylko raz (lub N razy, jeśli masz N programów obsługi) przez CommandHandler, który przetwarza polecenia.
źródło
Normalny wzorzec poleceń dotyczy danych i zachowania w jednej klasie. Ten rodzaj „wzorca dowodzenia / obsługi” jest nieco inny. Jedyną zaletą w porównaniu do normalnego wzorca jest dodatkowa zaleta polegająca na tym, że twoje polecenie nie zależy od ram. Na przykład twoje polecenie może wymagać dostępu do DB, więc musi mieć jakiś kontekst DB lub sesję, co oznacza, że jest zależne od frameworków. Ale to polecenie może być częścią Twojej domeny, więc nie chcesz, aby zależało ono od ram zgodnie z zasadą odwracania zależności . Oddzielenie parametrów wejściowych i wyjściowych od zachowania i posiadanie dyspozytora do ich połączenia może to naprawić.
Z drugiej strony utracisz przewagę zarówno dziedziczenia, jak i kompozycji poleceń. Myślę, że to prawdziwa moc.
Również drobne nitpick. To, że ma w nazwie polecenie Command, nie czyni go częścią CQRS. Chodzi o coś bardziej fundamentalnego. Tego rodzaju struktura może służyć jednocześnie jako polecenie i zapytanie.
źródło