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 3 godziny
  • Postów:1473
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:10 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 3 godziny
  • Postów:1473
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:10 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 3 godziny
  • Postów:1473
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:10 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:10 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:10 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:10 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 3 godziny
  • Postów:1473
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

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)