Wzorzec mediator - zależność handlerów

0

Cześć, czy we wzorcu mediator (korzystajac z biblioteki MediatR) handlery mogą zależeć od samych siebie?
Tzn chodzi mi o to, czy powinny odpytywać inne handlery o jakieś dane im potrzebne?

4

Generalnie nie, w obiekcie command powinieneś dostać wszystkie dane konieczne do wykonania komendy.
Jeżeli chodzi o query to nie był bym aż tak pewien. Oczywiście w idealnym świecie każde query jest oddzielne, ale czasami aż się prosi żeby np. złożyć wyniki z 3 innych query i w mojej opinii, o ile się z tym nie przesadza, jest to OK.

Problemy mogą się pojawić jeżeli masz jakieś PipelineBehaviour (aka aspekty) zapięte na handlerach. Jeżeli uwspólniać to być może za pomocą wydzielenia osobnego obiektu Query który nie będzie implementował żadnych interfejsów z MediaR'a.

Z drugiej strony w Query można też pozwolić sobie od czasu do czasu na duplikacje. To też nic złego (o ile z tym nie przesadzamy).

4

Bez sensu by to było, a już na pewno łamałoby SRP.
Jeśli potrzebujesz w handlerze dodatkowych danych, to niech je dostarczy jakaś klasa do dostarczania tych danych, a nie inny handler.

2

Nie. Jeśli handler FooHandler ma zrobić(lub potrzebuje danych) ABC, a BarHandler ma zrobić(lub potrzebuje) BCD, to wydziel sobie do osobnej klasy np BC i zawołaj w każdym z nich oddzielnie.
Oczywiście może będzie lepiej wydzielić A, B i C do soobnych klas itp, ale to już zależy od tego co tam się dzieje dokładnie.
U ciebie pewnie potrzebujesz jakiegoś dostępu do danych, repozytorium itp wydzielonego poza handler.

0

Ogólnie jeśli chodzi o logikę znajdującą się w takich handlerach, to co tam najczęściej powinno się znajdować?
Np. Pobranie czegoś z bazy danych, puszczenie to do wykonania przez logikę biznesową i zwrócenie do kontrolera jakiegoś DTO, albo innego wyniku?

2

To zależy od architektury Twojej aplikacji. Taki Handler może być zarówno skryptem transakcji, który łączy się z jakimś źródłem danych, przetwarza i zapisuje wynik gdzieś, jak wywoływać jakąś grubszą logikę w agregacie DDD.

2

Chyba nie do końca rozumiem o co konkretnie chodzi. Coś takiego?

public MyHandlerOne(IMediator mediator)
{
  _mediator = mediator;
}

public async Task Handler(DoSomething request)
{
  // ...
  var somethingElseResult = await _mediator.Send(new DoSomethingElse(request.SomeParam));// Handled by MyHandlerTwo
  // Do further processing using the request as well as somethingElseResult
  // ...
}

Jeśli tak to wyjdę chyba przedmówcom na heretyka ale nie widzę w tym nic złego. Paradoksalnie to właśnie może pomóc w utrzymaniu SRP, bo każda klasa (handler) będzie miała swoją odpowiedzialność, chociaż kwestia SRP to chyba jedno z najbardziej niejednoznacznych zagadnień w inżynierii oprogramowania, i tam gdzie ktoś widzi SRP ktoś inny widzi jego łamanie, a jeszcze ktoś widzi przerost formy nad treścią.

Tak czy inaczej jeśli DoSomethingElse jest odpowiedzialny za "odpalanie" jakiejś logiki która jest wywoływana z niejednego miejsca (np. z różnych części aplikacji) to ja nie widzę nic złego w takim podejściu. Mowa tutaj o sytuacjach gdzie ten drugi handler wykonuje bardziej zaawansowaną logikę, a nie przypadku gdzie dla każdego wyciągnięcia encji z bazy danych wywołujemy oddzielny handler, bo wtedy lepiej po prostu wstrzyknąć wymagane queries/repositories i użyć je bezpośrednio.

EDIT: Proszę jeszcze zwrócić uwagę że w zasadzie nie ma tutaj mowy o bezpośredniej zależności do innych handlerów. Paradoksalnie jawnych zależności mamy mniej bo nie musimy wstrzykiwać dodatkowych serwisów- tym zajmuje się inny handler. Ale handler wywołany przez mediator. Z perspektywy pierwszego handlera, (zakładając że mamy dobrze nazwane requesty) kod jest bardziej ekspresywny i skupia się na tym co ma być zrobione, a nie jak. Jak dotyczy jakiejś konkretnej odpowiedzialności tego konkretego handlera, zostawiając resztę pracy innemu handlerowi.

1

Handlery tworzą warstwie abstrakcji zwaną logiką aplikacji, czyli zarządzają przetwarzaniem żądań wysłanych do aplikacji, zarządzają realizacją logiki biznesowej, a następnie zwracają jakieś wyniki.
Pytanie zatem - dlaczego obiekty z jednej warstwy abstrakcji miałyby się ze sobą komunikować? Takie podejście zazwyczaj prowadzi do rozmycia warstw, czyli powstania kodu spaghetti.

0

Nigdzie nie jest powiedziane że handlery należą tylko do warstwy aplikacji, to już zależy od programisty. Jeśli stosuje się takie podejście to faktycznie, można użyć argumentu że nie powinno być dalszego przepływu danych między innymi handlerami. Specjalnie używam tutaj sformułowania "przepływ danych" bo mówienie o zależności od innych handlerów może być mylące, o czym pisałem wcześniej. Rzecz tylko w tym że jeśli masz handlery odpowiadające za warstwę aplikacji to należało by być sumiennym i takie handlery nie powinny zawierać logiki biznesowej/domenowej. Co za tym idzie każdy handler bez wyjątku powinien posiadać co najmniej jeden odpowiadający mu serwis domenowy, a moim zdaniem to już przerost formy nad treścią bo przy zastosowaniu wzorca mediator to właśnie handlery mogą spełniać rolę serwisów, poza pewnymi wyjątkami gdzie faktycznie jest sens mieć dedykowany serwis.

Podsumowując- handlery mogą, a wręcz moim zdaniem powinny należeć do warstwy logiki biznesowej/domenowej, a co za tym idzie każdy handler powinien mieć logike odpowiedzialną za konkretną operację biznesową. Idąc dalej tym tokiem myślenia wysyłanie requestów z handlerów do innych handlerów jest czymś normalnym, promującym właśnie SRP i nie za bardzo widzę gdzie tu spaghetti kod. Owszem, to również ma swoje wady bo sprawdzając jakiś workflow trzeba również sprawdzić gdzie dany request jest obsługiwany, ale to nie świadczy o kodzie spaghetti. W przeciwnym razie można powiedzieć że chociażby każdy system oparty o mikroserwisy i asynchroniczną komunikację sieciową to jedno wielkie spaghetti, bo tam również różne części systemu reagują na polecania i/lub zdrzenia wysyłane przez inne części. Mając dobrze wydzielone handlery unikamy wręcz kodu spaghetti bo każdy zajmuje się konkretnym elementem logiki biznesowej, w swoim własnym zakresie, delegując resztę pracy do innego handlera jeśli jest taka potrzeba. Tylko znów chcę zaznaczyć że wysyłanie/delegowanie do handlera jest sformułowaniem mylącym, bo handler wysyłający nowy request nic nie wie o innym handlerze, nie ma więc bezpośredniej komunikacji handler -> handler. Jedyne co takiego handlera interesuje to wysłać request i otrzymać wynik- a co, jak i gdzie jest obsługiwane to nie jego sprawa. Rozbijamy więc jeden większy workflow na kilka małych pod-systemów (handlerów), każdy odpowiedzialny za swój ograniczony element logiki biznesowej.

1 użytkowników online, w tym zalogowanych: 0, gości: 1