CQRS Login Command - co powinna zwracac?

CQRS Login Command - co powinna zwracac?
RoboCat
  • Rejestracja:prawie 7 lat
  • Ostatnio:prawie 3 lata
  • Postów:32
1

Witam,

Szukałem na forum i nie mogę nigdzie znaleźć satysfakcjonujacego mnie rozwiązania.
Mianowicie mam aplikacje (bardziej początek aplikacji). używam MediatR do obsługi CQRS.
Co powinna zwracać komenda logowania użytkownika?
Początkowo zwracałem z komendy model LoggedUserModel do kontrolera który zawierał jwt token + dodatkowe informacje potrzebne we front-end'e (front end na angular2 - osobna alikacja), ale co w przypadku kiedy logowanie się nie powiedzie? Może dodać w modelu pozycje "status" etc? Z kontrolera do klienta zwracam IActionResult.
Czy może z komendy zwrocic jeden model a następnie w zależnosci od "statusu" zwracać inne IActionResult? Zwracanie ActionResult z komendy raczej odpada?
Moj kod "so far..." :
Controller: (kawałek logowania):

Kopiuj
[HttpPost("login")]
public async Task<ActionResult<LoggedUserModel>> Login([FromBody] LoginUserCommand c)
{
    return Ok(await _mediator.Send(c));
}

Command Handler: (funkcja handle):

Kopiuj
public async Task<LoggedUserModel> Handle(LoginUserCommand request, CancellationToken cancellationToken)
{
    var user = await _context.Users.Where(u => u.Email == request.Email).SingleOrDefaultAsync(cancellationToken);
    if (user == null)
              =>   ; // co tutaj zwrocic?
    
    var salt = user.Salt;
    var hash = _encrypter.GetHash(request.Password, salt);
    if(user.Password == hash)
    {
        var token = _tokenHandler.GetToken(user.Email, user.Role);
        var LU = new LoggedUserModel
        {
            // testowy loggedusermodel
            Id = user.Id,
            Token = token,
        };
        return LU;
   } else {
       // co zwrocic w przypadku blednego logowania?
   }
}

Prosiłbym o wyrozumiałość z powodu że jestem początkujący :D
Myślałem nad kilkoma rozwiązaniami ale chciałbym usłyszeć jakieś rady od bardziej doświadczonych żeby to napisać "dobrze".

Dzięki.

SZ
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 8 godzin
  • Postów:1494
0

Zamiast Task<LoggedUserModel> zwracaj np obiekt Response

Kopiuj
public class Response
    {
        private readonly IList<string> _messages = new List<string>();

        public IEnumerable<string> Errors { get; }
        public object Result { get; }

        public Response() => Errors = new ReadOnlyCollection<string>(_messages);

        public Response(object result) : this()
        {
            Result = result;
        }

        public Response AddError(string message)
        {
            _messages.Add(message);
            return this;
        }
    }

I swój model usera trzymaj w Result,

edytowany 1x, ostatnio: szydlak
E9
  • Rejestracja:ponad 13 lat
  • Ostatnio:12 miesięcy
  • Postów:395
1
szydlak napisał(a):

Zamiast Task<LoggedUserModel> zwracaj np obiekt Response

Kopiuj
public class Response
    {
        private readonly IList<string> _messages = new List<string>();

        public IEnumerable<string> Errors { get; }
        public object Result { get; }

        public Response() => Errors = new ReadOnlyCollection<string>(_messages);

        public Response(object result) : this()
        {
            Result = result;
        }

        public Response AddError(string message)
        {
            _messages.Add(message);
            return this;
        }
    }

I swój model usera trzymaj w Result,

To jest łamanie idei CQRS'a. Poza tym jak to będzie asynchroniczna komenda (na poziomie infrastruktury) to i tak tego nie zwrócisz.

edytowany 1x, ostatnio: error91
TA
uzywanie cqrs do logowania sie to jest lamanie idei cqrs :)
SZ
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 8 godzin
  • Postów:1494
0

Zgadzam się. Tylko jak weryfikować czy te komendy się powiodły czy nie? Exceptiony też raczej nie bardzo. Nie mówię już nawet o samym logowaniu. Ale inne operacje. Dodawanie nowego usera itp

Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
2

Tylko jak weryfikować czy te komendy się powiodły czy nie?

Po stronie aplikacji powinieneś stworzyć event (UserLoggedIn, UserCreated itd.) na który klient powinien nasłuchiwać (za pomocą webhooków, kolejek, czegokolwiek); w evencie możesz już mieć flagę czy operacja się powiodła.


E9
  • Rejestracja:ponad 13 lat
  • Ostatnio:12 miesięcy
  • Postów:395
3
Patryk27 napisał(a):

Tylko jak weryfikować czy te komendy się powiodły czy nie?

Po stronie aplikacji powinieneś stworzyć event (UserLoggedIn, UserCreated itd.) na który klient powinien nasłuchiwać (za pomocą webhooków, kolejek, czegokolwiek); w evencie możesz już mieć flagę czy operacja się powiodła.

Stroniłbym od takich flag w evencie UserCreated. Raczej lepiej byłoby zrobić 2 eventy. Jeden dla sukcesu drugi dla porażki. Ogólnie taki prawdziwy CQRS niesie ze sobą dużo pozytywów, ale wdrożenie go i przekonanie biznesu do eventual consistency to nie jest łatwy kawałek chleba. W większości projektów kolejki, websockety dla komend przy operacjach typu CRUD itp to overengineering, a niestety 80% aplikacji to CRUDy. Tylko programiści lubią utrudniać sobie życie podążając za modą z konferencji (microserwisy itd.)

edytowany 1x, ostatnio: error91
PO
Oj tutaj się zgadzam z kolegą w 100%. Mam dokładnie takie same odczucia i niestety u mnie w zespole strasznie wciskają te eventy a nie do końca jest to przemyślane i niestety potencjalne zyski są znacznie mniejsze niż koszty utrzymania tego.
SZ
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 8 godzin
  • Postów:1494
0
Patryk27 napisał(a):

Tylko jak weryfikować czy te komendy się powiodły czy nie?

Po stronie aplikacji powinieneś stworzyć event (UserLoggedIn, UserCreated itd.) na który klient powinien nasłuchiwać (za pomocą webhooków, kolejek, czegokolwiek); w evencie możesz już mieć flagę czy operacja się powiodła.

A masz gdzieś przykładową implementację z wykorzystaniem MediatR?

Patryk27
Sam nie tworzyłem jeszcze nic wartego uwagi opartego o CQRS, więc niestety nie.
E9
  • Rejestracja:ponad 13 lat
  • Ostatnio:12 miesięcy
  • Postów:395
0
szydlak napisał(a):
Patryk27 napisał(a):

Tylko jak weryfikować czy te komendy się powiodły czy nie?

Po stronie aplikacji powinieneś stworzyć event (UserLoggedIn, UserCreated itd.) na który klient powinien nasłuchiwać (za pomocą webhooków, kolejek, czegokolwiek); w evencie możesz już mieć flagę czy operacja się powiodła.

A masz gdzieś przykładową implementację z wykorzystaniem MediatR?

Tutaj nie ma magii. Publikujesz event w handlerze i coś się na niego subsrkybuje. To coś operuje na websocketach i przesyła potrzebne informacje do klienta.

0

Bez sensu. Informacja stanie przetwarzania żądania nie jest zasobem w sensie CQRSa.

Czyli każda komenda będzie miała oddzielny event ze statusem aplikacji, super!.

E9
  • Rejestracja:ponad 13 lat
  • Ostatnio:12 miesięcy
  • Postów:395
0
Błękitny Terrorysta napisał(a):

Bez sensu. Informacja stanie przetwarzania żądania nie jest zasobem w sensie CQRSa.

Czyli każda komenda będzie miała oddzielny event ze statusem aplikacji, super!.

Jak potrzebujesz to publikujesz. Jak wsadzisz taką komende na kolejke to jak chcesz dostać wynik ?

edytowany 1x, ostatnio: error91
0

Jak wsadzisz taką komende na kolejke to jak chcesz dostać wynik ?

Takie decyzje projektowe podejmuje się z powodu skalowalności, a nie samego CQRSa, który jest głównie podziałem obiektów na Q/R.

0

C/Q :P

E9
  • Rejestracja:ponad 13 lat
  • Ostatnio:12 miesięcy
  • Postów:395
0

Podziałem na C/Q jest CQS a nie CQRS. A wracajac to tutaj został podany przykład zwracania użytkownika po wyslaniu LoginCommand wiec to jest zasob. Można to robić ale to jest łamanie CQRSa i tyle.

edytowany 3x, ostatnio: error91
0
error91 napisał(a):

Podziałem na C/Q jest CQS a nie CQRS. A wracajac to tutaj został podany przykład zwracania użytkownika po wyslaniu LoginCommand wiec to jest zasob. Można to robić ale to jest łamanie CQRSa i tyle.

W CQS chodzi o metody, a nie obiekty.

https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf

Command and Query Responsibility Segregation uses the same definition of Commands and Queries
that Meyer used and maintains the viewpoint that they should be pure. The fundamental difference is
that in CQRS objects are split into two objects, one containing the Commands one containing the
Queries.

A rzucenie wyjątku to, też zasób?

leggo
nie wiem gdzie to skomentować, ale wydaje mi się, że w wielu firmach, dla ułatwienia pracy przyjmuje się, że taki CommandHandler zwraca defaultowy response z wynikiem. Ważne, żeby mentalnie się trzymać zasady do czego używamy Query a do czego Command i pilnować porządku. Bo to wrzorzc trochę architektoniczny, masz prawo go modyfikować. Takie jest moje odczucie. Jeśli chodzi o rozwiązania podręcznikowe, to jeszcze spotkałem się z mechanizmem Job i publikowania Jobów, a których wynik pobierasz z kontrolera. Ale nie implementowałem nigdy sam od zera.
E9
  • Rejestracja:ponad 13 lat
  • Ostatnio:12 miesięcy
  • Postów:395
0
Błękitny Terrorysta napisał(a):
error91 napisał(a):

Podziałem na C/Q jest CQS a nie CQRS. A wracajac to tutaj został podany przykład zwracania użytkownika po wyslaniu LoginCommand wiec to jest zasob. Można to robić ale to jest łamanie CQRSa i tyle.

W CQS chodzi o metody, a nie obiekty.

https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf

Command and Query Responsibility Segregation uses the same definition of Commands and Queries
that Meyer used and maintains the viewpoint that they should be pure. The fundamental difference is
that in CQRS objects are split into two objects, one containing the Commands one containing the
Queries.

A rzucenie wyjątku to, też zasób?

Gdzie tak napisałem? W tym dokumencie który podesłałeś jest wyraźnie że client dostaje ACK/NAK Response. Dla Ciebie zwrócenie zalogowanego usera to jest ACK ? Przy dodawaniu produktu też zwrócisz Id ?

RoboCat
  • Rejestracja:prawie 7 lat
  • Ostatnio:prawie 3 lata
  • Postów:32
0

Ale się narobiło :D
A co jakbym np zwrocil do controllera loggedUserModel ( jakeis dane i np status wykonania operacji) a w kontrolerze w zaleznosci od statusu zwrocic inny IActionResult z modelem|? Albo dopisac jakas logike ktora to zautomatyzuje.
Męczy mnie to jak to rozwiązać, tak samo np z dodawaniem postu lub innymi operacjami - jak zwrocic czy to sie udalo/nie udalo/rozsypalo etc itp itd..

SZ
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 8 godzin
  • Postów:1494
0

Za bardzo się przejmujesz. Każdy programista ma swoje racje i nie ma jednej poprawnej drogi. Ja z reguły robię tak jak Ci pokazałem na początku. I nie stosuje mediatR, żeby mówić, że mam CQRS, tylko uważam to za ciekawe rozwiązanie. Wcześniej robiłem np service, który szybko się rozrastał i ciężko była nad tym zapanować.Możesz to zrobić na eventach jak tu podpowiadają tylko czy twój projekt na pewno tego wymaga ?

0

Gdzie tak napisałem? W tym dokumencie który podesłałeś jest wyraźnie że client dostaje ACK/NAK Response. Dla Ciebie zwrócenie zalogowanego usera to jest ACK ? Przy dodawaniu produktu też zwrócisz Id ?

Tak dla mnie to jest ACT, ponieważ jest to informacja o dokonanej zmianie.
Poza tym ORM może zwrócić błąd, że w pamięci posiada obiekty z tym samym Id on wcale niemusi czytać czegoś w runtime z DB.
A to, że aplikacja wie o tym, kiedy ściągnąłeś coś ze stack'a też jest problemem?.

The Application Server will return to the client either an Acknowledgement (Ack)
that the change has been persisted or it will return a series of errors as to why it was unable to persist
the changes

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.