Pytanie z backendu

0

Hej, mam aplikację desktopową, która sobie działa. Są jakieś operacje wykonywane za pomocą komend.
Teraz piszę do tego klienta webowego. I też używam tych komend. I w pewnym momencie pojawia się problem. Jeśli są spełnione specyficzne warunki, użytkownik musi odpowiedzieć na pytanie "TAK/NIE", żeby komenda działała dalej. Na desktopie nie ma z tym oczywiście żadnego problemu, ale jak coś takiego ogarnąć w webie? Dla ułatwienia - wszystko idzie przez interfejsy i DI, czyli coś w rodzaju:

public class MyCommand: BaseCommand
{
    IDialogService dialogs;
    public MyCommand(IDialogService dialogs): base()
    {
        this.dialogs = dialogs;
    }

    public override void Execute()
    {
        //jakaś robota
        var result = dialogs.ShowQuestion("Czy chcesz?", DialogButtons.YesNo);
        if(result.Btn == DialogButtons.Yes)
        {

        }
    }
   
}
1

Przede wszystkim musisz skupić się na oddzieleniu warstwy domenowej od warstwy aplikacji. W aplikacji webowej definiujesz requesty (DTOs) co pozwoli Ci na walidacje danych na etapie obsługi takiego requestu. Np. w ASP możesz użyć atrybutu Required aby zdefiniować wymagane pole, co za tym idzie jeśli klient nie dostarczy w requeście wartości dla pytania to API zwróci BadRequest. Jeśli request jest poprawny to mapujesz go na obiekt command. Ogólnie to poczytaj o architekturach takich jak Onion/Hexagonal oraz o rozdzieleniu warstwy biznesowej od warstwy aplikacji.

Tak na marginesie, to masz jakiś fundamentalny błąd z tym command- jeśli chcesz to robić prawidłowo to command jest niczym innym jak poleceniem do zrobienia czegoś, jest więc takim DTO warstwy biznesowej. Nie powinien mieć żadnej logiki ani wstrzykniętych zależności. To moduł obsługujący command powinien posiadać logikę. Poczytaj sobie o podejściu command-handler. Nie mówiąc już o tym że skoro używasz coś takiego jak IDialogService to jest jasny sygnał że mowa o obsługiwaniu tych rzeczy we froncie, ten konkretny przypadek nie jest więc pytaniem z backendu. Backend nie obsługiwałby elementów widoku.

3

Nie wiem czy interfejsy i DI coś tu akurat ułatwiają, bo one nie idą po HTTP.
W aplikacji webowej to powinny być dwa żądania - jedno na to co tam masz teraz w tym command, drugie na Tak/Nie. Czyli być może dwie komendy w Twojej architekturze.

0

Nie wyraziłem się dobrze.
Ten mój command to właśnie CommandHandler. Mam porozdzielaną całą architekturę, np. od strony WPF (desktop) jest tak:

Widok z guzikiem - user klika guzik - odpala się command w WPFowym ViewModelu:

public void CreateNewComplicatedModel()
{
    var cmd = cmdFactory.CreateCommand<CreateComplicatedModelCommand>();
    cmd.Execute();
}

I ten mój CreateComplicatedModelCommand jest takim command handlerem. On nie zapisuje niczego w bazie, natomiast ma całą logikę potrzebną do utworzenia pewnego obiektu. Ale okazuje się, że w pewnych sytuacjach jest problem, który trzeba ogarnąć, a ogarnąć może tylko user. Więc user musi odpowiedzieć na pytanie, co zrobić dalej:

public class CreateComplicatedModelCommand: BaseCommand
{
    //... konstruktor z DI

    public override void Execute()
    {
        MyDialogResult res = MyDialogResult.Yes;
        if(cos && cosInnego)
            res = dialogs.ShowQuestion("Czy chcesz?", MyButtons.YesNo);

        if(res != MyDialogResult.Yes)
            return;

        //rób dalej    
    }
}

Tak to bardzo mniej więcej wygląda (pominąłem asyncki i wszystko inne zaciemniające obraz). Teraz, z poziomu WebApplication mam kontroler, który wygląda mniej więcej tak:

[HttpPost]
public IActionResult CreateComplicatedModel()
{
    var cmd = cmdFactory.CreateCommand<CreateComplicatedModelCommand>();
    cmd.Execute();
}

Czyli jest dokładnie to samo, co w kliencie WPF.

5

Powinieneś mieć dwa commandy i dwa handlery, oba wywoływane z UI. Nie może być tak, że handler zwraca się po coś do UI. Zauważ, że command to po polsku rozkaz, a rozkazy się wykonuje bez pytania.

1

Właśnie, webdziała trochę inaczej. Jeśli z UI webowego wysyłasz polecenie to już na tym etapie w UI powinienes zadać pytanie. Jeśli coś ma wykonać komendę to ma ją wykonać a nie zadawać jeszcze pytania.

0

No dobra, to teraz jak można mądrze rozwiązać taką sytuację:

  • komenda tworzy kilka obiektów
  • zaszedł jakiś warunek, więc trzeba zapytać użytkownika, w którą stronę idziemy dalej
  • poszła dalej jakaś logika
  • znowu pytanie użytkownika
  • poszło jeszcze trochę logiki

Jakby to przełożyć na język typowo ludzki, załóżmy, że jest remont w mieszkaniu. Wykonawca pyta się o płytki, co ma gdzie być itd. I zaczyna robić (wykonuje rozkaz). Nagle okazuje się, że po skuciu starych płytek elektryka jest do położenia jeszcze raz. Więc pytanie - kłaść nową, czy zostawiamy tą co jest? I kontynuujemy robotę. Chciałbym uniknąć sytuacji, że po pytaniu użytkownika, muszę jeszcze raz "skuć stare płytki", zobaczyć, że elektryka do poprawy i już bez pytania poprawiać. Do tego mniej więcej się to sprowadza.
Więc jak sobie z czymś takim poradzić? Jedyne, co mi przychodzi do głowy, to zapisywanie stanu wykonania polecenia gdzieś w bazie danych, w jakiejś sesji może... Ale tutaj też mamy nadmiarową robotę. Jak można podejść do takiego problemu?

3

Wieloetapowy formularz na froncie?

0

i cyk websocketem usera zapytać

a jak odpowie to wznowić proces :D

0
szydlak napisał(a):

Wieloetapowy formularz na froncie?

Użytkownik nie ma aż tylu danych do wprowadzenia. Tak naprawdę większość zależy od tego, co siedzi w bazie danych.

0

komenda tworzy kilka obiektów

Już było wyjaśnione że komenda takich rzeczy nie robi. Mieszasz komendę z handlerem, a to dwa oddzielne byty

zaszedł jakiś warunek, więc trzeba zapytać użytkownika, w którą stronę idziemy dalej
poszła dalej jakaś logika
znowu pytanie użytkownika
poszło jeszcze trochę logiki

To jest tak zwane task-based UI. Problem polega na tym że mieszasz backend z frontendem. Backendu nie powinno obchodzić że po zrobieniu X należy spytać użytkownika o Y a następnie zrobić Z. To powinno być w gestii frontu. Możesz rozbić swoje API na właśnie task-based endpoints. Wtedy front wyśle request, jeśli ten się powiedzie to front wyświetli coś dla użytkownika i wyśle odpowiedź w kolejnym requeście. Albo najpierw zbierze wszystkie dane (pytania itp) i dopiero na koniec wykona request do API.

0

To jest tak zwane task-based UI. Problem polega na tym że mieszasz backend z frontendem.

No właśnie na początku to był front. Tzn. handler robił to wszystko. Ale w związku z tym, że to był klient desktopowy, to nie było żadnego problemu. Tutaj handler był tym frontem. Niestety, tworząc klienta webowego do tego, napotkałem na ścianę. Bo okazało się, że to, co w desktopie było frontem, w webie jest backendem :/

1
Juhas napisał(a):

Bo okazało się, że to, co w desktopie było frontem, w webie jest backendem :/

Niezupełnie, po prostu od początku miałeś wymieszaną logikę prezentacji z biznesową. Na desktopie można tak zrobić, z webem się nie uda, bo prezentacja to front, a biznes to backend.

0

OK, to jak podejść do takiego tematu. Dla zobrazowania, wyobraźcie sobie, że robicie prostego CADa. Coś, co potrafi narysować polilinię. Zapisuje do bazy każdy narysowany segment, no i pyta się użytkownika o każdy segment. Czyli backend musi pracować z frontem. Cała logika rysowania polilinii jest zrobiona na backendzie. Jak byście podeszli do tematu? Ktoś tu pośmieszkował, że sockety się do tego nadadzą. Przeszło mi SignalR przez myśl. Dlaczego to byłoby złe rozwiązanie?

0

No opcje są dwie, albo każdy segment front wysyła na backend oddzielnie, albo cała polilinia jest wysyłana w jednym requeście. O co tu pytać użytkownika?

0

Użytkownik może np. chcieć wstawić segment za pomocą podania kąta i długości. Albo np. punktu. Czyli ma tu już dwie opcje.

0

A jakie to ma znaczenie dla backendu? Backend ma dostać dane do przechowania. Obsługą tego, co robi i jak dane podaje użytkownik zajmuje się frontend, i nawet powinien to chyba w tym przypadku przekształcać na format zrozumiały dla backendu.

0

Znaczenie ma takie, żeby nie dublować kodu. Mam całą logikę działającą (pobierającą dane) dla desktopu. I teraz musiałbym dodać dokładnie to samo w jakimś JSie. A chcę wykorzystać to, co już działa i co jest testowane.

1

Czyli, jeśli cokolwiek posyłasz z frontendu na backend, ten ostatni powinien zwrócić jakiś status, może nim być żądanie wykonania jakiegoś potwierdzenia na frontendzie.

0

No właśnie to wymaga ode mnie mocnego przeprojektowania. Bo tak mam jednego commanda, który robi określoną rzecz, a będę musiał zrobić z tego kilka. Właśnie dlatego też zaczynam myśleć o SignalR. Co Wy na to?

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