CQRS Login Command - co powinna zwracac?

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):

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

Command Handler: (funkcja handle):

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.

0

Zamiast Task<loggedusermodel> zwracaj np obiekt Response

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,

1
szydlak napisał(a):

Zamiast Task<loggedusermodel> zwracaj np obiekt Response

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.

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

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.

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.)

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?

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!.

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 ?

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