Nauka API - gdzie umieszczać logikę biznesową wykraczającą poza zwykły CRUD?

1

Cześć,
zaczynam właśnie naukę aplikacji webowych z wykorzystaniem API (jeśli to ważne, to .NET Web API + Angular) - do tej pory tworzyłem tylko aplikacje desktopowe.

Powiedzmy, że mam endpoint /Appointments, który zwraca wydarzenia usera w jakimś tam zakresie:

[
  {
    "id": 1,
    "title": "Spotkanie z klientem",
    "dateFrom": "2024-10-08 17:49:58",
    "dateTo": "2024-10-08 17:49:58"
  },
  {
    "id": 2,
    "title": "Badania lekarskie",
    "dateFrom": "2024-10-08 17:49:58",
    "dateTo": "2024-10-08 17:49:58"
  },
  {
    "id": 3,
    "title": "Prezentacja programu POS",
    "dateFrom": "2024-10-08 17:49:58",
    "dateTo": "2024-10-08 17:49:58"
  }
]

Chcę teraz w aplikacji pokazać listę tych rezerwacji (czyli json do tabelki) i na ich podstawie obliczyć wolne terminy, tzn. użytkownik chce zrobić coś co zajmuje x czasu, a ja proponuję mu daty, które nie są jeszcze zajęte.

W aplikacji desktopowej stworzyłbym po prostu dodatkowe okno i, po naciśnięciu przycisku, na podstawie wcześniej pobranych danych jakimś algorytmem załatwił sprawę. Ale co z podejściem API + front end? Czy takie obliczenia powinny być wykonywane u klienta za pomocą JS, czy może powinienem stworzyć jakiś nowy endpoint, np. /AvailableDates?

(Podana wyżej sytuacja to tylko przykład, aby lepiej wytłumaczyć moje pytanie)

Z góry dziękuję

3

Różnie się robi, nie ma jednej konkretnej metody na to. Czasem nawet logikę robi się w dwóch miejscach bo np. w backendzie i tak musisz zwalidować dane a na froncie też chcesz walidować dane żeby nie wysyłać zbędnych requestów itd.

Jak będziesz to miał na backendzie i potem będziesz chciał dodać np. aplikację mobilną to nie będziesz musiał powielać tej logiki tylko zawołasz endpoint

2
bbhzp napisał(a):

Czy takie obliczenia powinny być wykonywane u klienta za pomocą JS, czy może powinienem stworzyć jakiś nowy endpoint, np. /AvailableDates?

Zacznij od tego, co Ci będzie najłatwiej zrobić.

Jeśli napotkasz problem: wtedy spróbuj w tym drugim miejscu. Jeśli nie, to wszystko w porządku.

0
Riddle napisał(a):

Zacznij od tego, co Ci będzie najłatwiej zrobić.

Jeśli napotkasz problem: wtedy spróbuj w tym drugim miejscu. Jeśli nie, to wszystko w porządku.

Na razie jeszcze niczego nie zacząłem robić, pytanie czysto teoretyczne :)

Czyli w API umieszcza się rzeczy, które:

  • wymagają walidacji / dostępu do bazy
  • wymagają bardziej skomplikowanych wyliczeń (np. generowanie faktury - zwracanie JSONa z danymi, a tworzenie PDF na ich podstawie już we froncie)
  • są wrażliwe

a we froncie, oprócz samej prezentacji, prostsze rzeczy, jak np. sumowanie dodawanych pozycji?

Czyli podsumowując, jeśli opisana wyżej przeze mnie funkcjonalność jest używana tylko w jednym miejscu i na potrzeby użytkownika, to dać ją do frontu,
ale, gdy będzie używana w wielu miejscach i przez zewnętrznych integratorów, to dać ją do API? Czy dobrze rozumiem?

1
bbhzp napisał(a):

Czyli podsumowując, jeśli opisana wyżej przeze mnie funkcjonalność jest używana tylko w jednym miejscu i na potrzeby użytkownika, to dać ją do frontu,
ale, gdy będzie używana w wielu miejscach i przez zewnętrznych integratorów, to dać ją do API? Czy dobrze rozumiem?

Nie ma takich zasad. Sam możesz zdecydować gdzie dać jakąś logikę. Umieść ją gdzie Ci wygodniej.

2

To zależy. Co próbujesz optymalizować bądź czego się nauczyć.

Jeśli budowania frontów to logikę na frontach. Jeśli bekend to na bekendzie.

Jeśli chcesz mniej zużywać bekendu to na frontendzie. Jeśli chcesz mieć logikę w jednym miejscu to na bekendzie. Jeśli twoi klienci mają kiepskie maszyny to na bekendzie. I tak dalej i tak dalej.

Pachnie mi ten temat https://www.google.com/search?q=analyzys+paralyzys.

5

To zależy. Co próbujesz optymalizować bądź czego się nauczyć.

Wydaje mi się, że raczej OP pyta o wytyczne/zasady sztuki, czyli jak powinno się ogólnie podchodzić do tematu. Wiadomo, że to jest zależne od sytuacji i w różnych projektach/scenariuszach można przyjąć inne podejścia, ale raczej @bbhzp pyta o ogólne wytyczne. Coś na zasadzie że np. pracując z SQL nie robisz (zazwyczaj) SELECT * FROM a potem wszystko filtrujesz i sortujesz w aplikacji, tylko raczej powinno to wyglądać SELECT a, b, c FROM xxx WHERE xxx ORDER BY xxx - za przetworzenie i przygotowanie danych odpowiada serwer, a apka/front bardziej odpowiada za ich prezentację.

Odpowiadając na pytanie główne z tego wątku:

  • jak napisał @anonimowy: lepiej jest trzymać takie operacje i logikę po stronie serwera, bo potem ułatwia to rozbudowę (np. wspomniana apka mobilna, albo nawet aplikacja desktopowa)
  • podstawowa walidacja powinna być na froncie, żeby nie zaśmiecać i zamulać serwera głupotami, ale i tak ostateczna decyzja po stronie backendu
  • poszerzając poprzedni punkt: front dostał informację, że termin 15-10-2027 godz. 15:30 jest dostępny. Klient dostał takie info, ale potem zadzwonił telefon/poszedł na kawę albo do łazienki. Do rezerwacji wrócił 10 minut później. Front nadal ma informację, że ten termin jest dostępny, ale ktoś inny go w tym czasie zajął. Jeśli podejmujemy działania tylko w oparciu o front, to mamy problem - konieczna jest jednak obsługa zamówień/logiki po stronie serwera
  • kwestia bezpieczeństwa: im więcej danych i algorytmów/logiki masz po stronie klienta to (zwłaszcza w aplikacjach webowych) dajesz większe możliwości podglądnięcia jak to działa, jakie dane masz na serwerze, jak działają mechanizmy apki - a co za tym idzie, jeśli któś ma złe intencje, to mu ułatwiamy zadanie.

pokazać listę tych rezerwacji (czyli json do tabelki) i na ich podstawie obliczyć wolne terminy,

Ja bym zrobił to po stronie serwera - tak, jak mówisz: osobny endopoint, który przyjmie jakieś dane wejściowe w stylu daty albo zakresów dat, potrzebny czas lub inne elementy, które są potrzebne do złożenia propozycji terminu (coś na zasadzie daj mi propozycje kiedy mogę zrobić rezerwację 2 godziny we wtorek albo czwartek pomiędzy godz. 13 a 18), a potem zwróci jakiegoś JSONA czy cokolwiek innego z dostępnymi terminami. Możesz to zrobić tak, że pobrać wszystkie terminy dostępne i potem to obrobić na froncie, ale:

  • niepotrzebnie generujesz ruch i przewalasz nadmiarowe dane. Oczywiście - kwestia skali, jeśli to się będzie działo 10 razy na dobę to nie ma o czym gadać, ale co do zasady - lepiej nie robić takich rzeczy
  • podejrzewam, że jednak sprawniej pójdzie ustalenie tych terminów silnikowi SQL, który wypluje je z bazy w oparciu o odpowiednio przygotowane zapytanie (oraz ewentualnie np. dobrze zrobione indeksy na bazie), niż Ty to ręcznie sobie przefiltrujesz
  • jeśli masz to zrobione po stronie aplikacji, a np. z tym serwerem się łączy apka webowa, coś desktopowego i jeszcze apka mobilna, to mechanizm filtrujący musisz poprawić we wszystkich tych miejscach
  • kwestia integracji z innymi usługami/aplikacjami: jeśli jest opcja, że np. będziesz integrować system zamówień/rezerwacji z innym systemem księgowym (np. do fakturowania) to logika musi być po stronie serwera TWOJEJ aplikacji, chyba że dogadasz się z producentem tego drugiego softu. Ale to znowu wracamy do poprzedniego punktu: mamy kilka miejsc, gdzie mamy ten sam mechanizm, robiący to samo. A to stwarza dwa zagrożenia: po pierwsze to masz więcej pracy z utrzymaniem i aktualizowaniem 3-4 miejsc, gdzie ta sama logika występuje, a po drugie - może pojawić się jakaś rozbieżność w działaniu - jedno poprawisz, drugiego nie, w którymś się pomylisz i nagle apka mobilna będzie pokazywać inne dostępne terminy od apki webowej.
2
cerrato napisał(a):

Odpowiadając na pytanie główne z tego wątku:

  • jak napisał @anonimowy: lepiej jest trzymać takie operacje i logikę po stronie serwera, bo potem ułatwia to rozbudowę (np. wspomniana apka mobilna, albo nawet aplikacja desktopowa)

Warto kierować się tym, co będzie łatwiejsze w przyszłości; ale żeby wiedzieć co będzie łatwiejsze a co nie, trzeba być w tym projekcie. My nie jesteśmy, OP jest. Więc to on powinien stwierdzić co mu będzie łatwiej a co nie.

Poza tym, trzeba też uważać, bo zbyt dosadne zwracanie uwagi na to "co będzie łatwiejsze w przyszłości" może prowadzić do przeinżynierowania. Designować aplikacje warto - ale warto to robić "na teraz", a nie "na przyszłość".

4

My nie jesteśmy, OP jest. Więc to on powinien stwierdzić co mu będzie łatwiej a co nie.

OK, ale on tego wcześniej nie robił, uczy się, pyta o wytyczne i porady. Jakby wiedział co jest łatwiejsze to by nie zakładał wątku.
Poza tym - nie zawsze to, co jest łatwiejsze, jest lepsze - po co dzielić projekt na klasy/moduły/osobne pliki, po co tworzyć jakieś struktury danych i funkcje, skoro można wszystko wrzucić do main(), a sterowanie przepływem oprzeć o GOTO i if/else. Przecież tak jest łatwiej - zmienne globalne, jeden plik itp 😛

zbyt dosadne zwracanie uwagi na to "co będzie łatwiejsze w przyszłości" może prowadzić do przeinżynierowania.

Oczywiście, pełna zgoda, ale z drugiej strony - od strony technicznej/ilości kodu do wklepania (albo poziomu jego skomplikowania czy kosztów wdrożenia) nie widzę większej różnicy między implementacją logiki po stronie serwera a frontu, za to zrobienie tego na backendzie znacznie zwiększa możliwości późniejszego rozwoju. Także - za wiele nie tracisz, a (potencjalnie) dużo zyskujesz. Może nie skorzystasz, ale skoro masz dodatkowe opcje prawie za free, to czemu nie skorzystać?

Poza tym przypominam, że OP się uczy tego tematu, więc dając porady nie tyle powinniśmy się kierować podanym jako przykład przypadkiem, ale raczej ogólnie przyjętymi dobrymi praktykami. A one raczej sugerują, że lepiej niewielkim lub prawie zerowym kosztem umieścić logikę na serwerze i przez to mieć możliwość łatwiejszej rozbudowy (np. dodanie wspomnianej aplikacji mobilnej) niż za jakiś czas robic rewolucję/duże przemeblowanie lub pisać od nowa.

0
cerrato napisał(a):

My nie jesteśmy, OP jest. Więc to on powinien stwierdzić co mu będzie łatwiej a co nie.

OK, ale on tego wcześniej nie robił, uczy się, pyta o wytyczne i porady. Jakby wiedział co jest łatwiejsze to by nie zakładał wątku.

Chodziło mi o to że lepiej mu dać radę "zrób to co dla Ciebie będzie łatwiejsze", niż powiedzieć mu co nam się wydaje że będzie dla niego łatwiejsze.

cerrato napisał(a):

Poza tym - nie zawsze to, co jest łatwiejsze, jest lepsze - po co dzielić projekt na klasy/moduły/osobne pliki, po co tworzyć jakieś struktury danych i funkcje, skoro można wszystko wrzucić do main(), a sterowanie przepływem oprzeć o GOTO i if/else. Przecież tak jest łatwiej - zmienne globalne, jeden plik itp 😛

Tak, i tutaj wkracza mój drugi punkt, z pierwszego postu:

Riddle napisał(a):

Zacznij od tego, co Ci będzie najłatwiej zrobić.

Jeśli napotkasz problem: wtedy spróbuj w tym drugim miejscu. Jeśli nie, to wszystko w porządku.

Dokładnie od tego każdy zaczynał, od wielkich metod i goto, wtedy często okazuje się że to może i było łatwe do napisania, ale nie jest łatwe do pracy, i wtedy się zmienia podejście i próbuje coś innego.

5

Chodziło mi o to że lepiej mu dać radę "zrób to co dla Ciebie będzie łatwiejsze", niż powiedzieć mu co nam się wydaje że będzie dla niego łatwiejsze.
[...]
Dokładnie od tego każdy zaczynał, od wielkich metod i goto, wtedy często okazuje się że to może i było łatwe do napisania, ale nie jest łatwe do pracy, i wtedy się zmienia podejście i próbuje coś innego.

Tak, ale skoro my już przez to przechodziliśmy to czemu każesz OP'owi samodzielnie też się męczyć i dochodzić do rozwiązania metodą prób i błędów? Przecież pyta o porady/wytyczne osób bardziej doświadczonych, także fajnie jest coś poradzić, a nie "kombinuj to sam dojdziesz do tego, do czego ja doszedłem X lat temu".

To trochę jakby ktoś np. uczył się blacharstwa i lakiernictwa samochodowego. Pyta o to, jak podejść do tematu. I teraz mamy 2 scenariusze:

  • mówisz, że (bazując na swoim doświadczeniu oraz ogólnych zasadach wykonywania tego typu prac) powinno się najpierw nałożyć podkład, potem niech wyschnie, potem farba właściwa, na końcu klar, szpachlu nie nakładamy grubiej niż 3mm w jednej warstwie, podczas cięcia metalu zakładamy okulary ochronne, do lakierowania załóż maskę, podczas spawania ustaw migomat na określoną moc itp.
  • skoro się uczysz to rób jak uważasz i jak będzie Ci łatwiej. Wprawdzie pewnie pierwsze 10 robót spartolisz, stracisz oko i 2 palce, a lakier razem ze szpachlem odpadnie na sprawie za tydzień, ale ja też w ten sposób zaczynałem.
0
cerrato napisał(a):

Tak, ale skoro my już przez to przechodziliśmy to czemu każesz OP'owi samodzielnie też się męczyć i dochodzić do rozwiązania metodą prób i błędów? Przecież pyta o porady/wytyczne osób bardziej doświadczonych, także fajnie jest coś poradzić, a nie "kombinuj to sam dojdziesz do tego, do czego ja doszedłem X lat temu".

No tylko widzisz, ja dałem mu radę. Rada "zrób to co jest najłatwiejsze" to jest właśnie ta rada.

Mogłem mu polecić:

  • zrób na froncie
  • zrób na backendzie
  • zrób tak jak libka mówi
  • zrób tak jak framework mówi
  • zrób tak żeby było najbardziej otestowane
  • zrób tak żeby było najszybciej
  • zrób tak żeby było najłatwiej
  • zrób tak żęby było najlepiej zrefaktorowane
  • zrób tak żeby jaknajmniej ruchu sieci zajmowało

Ale mogłem też powiedzieć "zrób tak, żeby Tobie było najwygodniej", i to jest ta rada którą mu dałem. Żeby dać lepszą radę (i powiedzieć konkretnie co mógłby zrobić żeby było łatwiej), musiałbym wejść do jego projektu i zobaczy to co on widzi.

Nawiązując do Twojego przykładu z blacharą, niektóre rzeczy po prostu nie są czarno-białe i nie da się dać one-advise-fits-all. Czasem są, jak np. "Jeśli melko kipi, to ściągnij z ognia". Ale czasem trzeba to zrobić na czuja, np. "Jeśli potrawa jest za mętna to przypraw do smaku". I pytanie OP nt tego gdzie umieścić kod, należy moim zdaniem do tej drugiej kategorii - na czuja, zrób tak jak Ci jest wygodniej. To jest ta rada.

Wiem, że ja bym tak zrobił, gdybym miał napisać feature ten który OP próbuje napisać, to dodałbym go tam gdzie mi jest wygodniej - jeśli wszystko działa, to w porządku. Jeśli jest jakiś problem, wtedy przeniósłbym go w inne miejsce.

4

"Jeśli potrawa jest za mętna to przypraw do smaku". I pytanie OP nt tego gdzie umieścić kod, należy moim zdaniem do tej drugiej kategorii - na czuja, zrób tak jak Ci jest wygodniej. To jest ta rada.

Ok, to inaczej - trzymając się analogii do gotowania: doświadczony kucharz czy piekarz/cukiernik wie, kiedy potrawa jest gotowa, jak długo trzymać w piekarniku, kiedy podkręcić temperaturę albo wcześniej wyjąć (bo np. do ciasta dałeś mniej wody i szybciej może się przypalić).

Ale osoba która uczy się raczej tego nie wyczuje/nie będzie wiedziała kiedy powinna wyjąć potrawę z pieca - więc takim się daje wytyczne w stylu "w piekarniku na 180 stopni trzymaj przez 20 minut". O działaniu na wyczucie, eksperymentach itp. można mówić w sytuacji, w której masz już pojęcie o tym, co robisz. Tak samo jak np. drift autem - możesz robić kontrolowane poślizgi jak się nauczysz dobrze jeździć. Dawanie osobie na kursie na prawko porad w stylu "kierownica w bok, jedziesz ślizgiem, jak poczujesz że za mocno ucieka to kontra i przyhamowanie ręcznym, potem gaz - ale to wszystko tak na czuja" jest bez sensu, bo on i tak z takich porad nie skorzysta.

0
cerrato napisał(a):

Ale osoba która uczy się raczej tego nie wyczuje/nie będzie wiedziała kiedy powinna wyjąć potrawę z pieca - więc takim się daje wytyczne w stylu "w piekarniku na 180 stopni trzymaj przez 20 minut".

Okej, "przez 20 minut". Co w sytuacji kiedy początkujący kucharz pyta doswiadczonego kucharza co zrobić; ale doświadczony kucharz umiałby odpowiedzieć na to pytanie tylko patrząc na potrawę jak jest w piekarniku? Bo np. nie da się dać rady która zadziała zawsze, dobry czas da się ocenić tylko patrząc na nią. Jaką radę wtedy dać?

Bo dokładnie teraz znajdujemy się w takiej sytuacji. Jak mówiłem, według mnie - najlepsza rada jaką mogę dać OP, to jest po prostu: "zrób tak żeby Tobie było wygodniej", to moim zdaniem przyniesie najlepszy efekt. Jeśli jemu jest wygodniej zrobić na froncie, to ja nie chcę mu mówić "zrób na backendzie bo mi by było wygodniej", właśnie o to chodzi. Jeśli "zgodnie ze sztuką" byłoby dodać liczenie czegoś na backendzie, ale OP robiłby to godzinę dłużej niż zrobiłby to na froncie; to moim zdaniem powinien to właśnie na froncie zrobić.

Moja rada dla OP w pseudo kodzie wygląda tak:

const easiestForHim = op.whatIsTheQuickestForYou();
if (easiestForHim == Front) {
  op.DoItInFront();
}
if (easiestForHim == Backend) {
  op.DoItInBackend();
}

To nie jest brak rady, i to nie jest mówienie "zrób byle jak" albo "nie obchodzi mnie", albo "wiem ale nie powiem". To jest konkretna rada, która mówi OP żeby na początku preferować to co jest dla niego najprostsze i niepotrzebnie nie przeinżynierowywać i nie robić czegoś w trudniejszym miejscu bez powodu.

Oczywiście, może się okazać, że to co OP preferuje okaże się gorsze, jak najbardziej. Ale moim zdaniem to jest korzystny trade-off. Mając ryzyko że zrobi coś w złym miejscu; vs ryzyko przeinżynierowania problemu ponad miare, to ja dużo bardziej wolałbym to pierwsze.

1
Riddle napisał(a):

Okej, "przez 20 minut". Co w sytuacji kiedy początkujący kucharz pyta doswiadczonego kucharza co zrobić; ale doświadczony kucharz umiałby odpowiedzieć na to pytanie tylko patrząc na potrawę jak jest w piekarniku? Bo np. nie da się dać rady która zadziała zawsze, dobry czas da się ocenić tylko patrząc na nią. Jaką radę wtedy dać?

No taką jaką my dajemy czyli. "Ciężko powiedzieć, ale około 20 minut i musisz patrzeć czy już jest gotowa mniej więcej wierz musi zacząć się rumienić" czy coś

2

Pojęcie zwykły CRUD jest trochę bez sensu bo w praktyce w dobrze zrobionym API prawie nigdy nie korzysta się z takich "czystych CRUD'ów".
Ogólnie zawsze lepiej mieć więcej logiki po stronie serwera zwracając jednak uwagę na to by czy dany fragment tej po stronie backend nie realizował rzeczy stricte front-endowych.
Często pojedyncze wywołania API pracują na kilku encjach jednocześnie w sposób zaprogramowany po stronie back-end i to już wymyka się idei "zwykłego CRUD".
Interfejs API powinien odzwierciedlać logikę biznesową nie zmuszając Frontu do generowania niepotrzebnych wywołań API jeśli te dane mogły być przygotowane na BE.

Dobre API w odpowiedzi powinno zwracać wszystko co potrzebuje FE do prezentacji danych związanych z daną funkcją API (oczywiście z zachowaniem zdrowego rozsądku).
Czyli jeśli potrzebne są jakieś słowniki związane z daną encją (listy dat o których pisałeś) to lepiej aby te już przyszły w odpowiedzi nie zmuszając FE do osobnego wołania o dane.

Tu nie ma żadnych ogólnych zasad co i kiedy lepsze. Ważniejsze jest to by w ramach jednego projektu / jednego produktu być konsekwentnym.

  • Walidacje zdecydowanie powinny być po stronie API (nawet jeśli są zdublowane z tymi po stronie FE) ponieważ zawsze musisz się zabezpieczyć przez spreparowaniem danych np. w konsoli przeglądarki.
1

doświadczony kucharz umiałby odpowiedzieć na to pytanie tylko patrząc na potrawę jak jest w piekarniku? Bo np. nie da się dać rady która zadziała zawsze, dobry czas da się ocenić tylko patrząc na nią. Jaką radę wtedy dać?

Oczywiście, ale masz jakieś ogólne zasady w stylu:

  • pizza 10 minut w 200 stopniach
  • jajka na twardo 12 minut w gotującej się wodzie
  • biszkopt pól godziny w 160 stopniach
  • itp (powyższe dane są z tyłka, więc jeśli napisałem głupoty to nie korygujcie mnie ;) ).

I tutaj ja widzę podobną sytuację: uczeń z gastronomika pyta o to, ile czasu piec jagodziankę. Ty/my jako doświadczeni kucharze mówimy, że ogólnie to taką typową jagodziankę na cieście drożdżowym to najlepiej piec 10 minut w 190 stopniach.
Być może w tym konkretnym przypadku lepiej zwiększyć temperaturę, bo dał za dużo wody, albo trochę krócej bo dałeś więcej mąki. Być może tak, ale nie znając szczegółów tego konkretnego przypadku, ciężko jest dawać wytyczne, więc trzymamy się ogólnej zasady/przepisu. Jestem pewien, że jak powiesz początkującemu kucharzowi piecz to na wyczucie, temperaturę i czas ustaw tak, żeby było dobrze to on to spieprzy. A poza tym - taka porada nie jest poradą, nic nie wnosi, tylko stwarza pozory przydatności.

na początku preferować to co jest dla niego najprostsze

Niekoniecznie najprościej oznacza nalepiej/dobrze.
Prościej jest pomalować istniejącą ścianę - bez usuwania warstw dawnej farby, bez szpachlowania dziur, bez gruntowania. Ale czy to jest dobrze wykonana robota?

0
cerrato napisał(a):

Oczywiście, ale masz jakieś ogólne zasady w stylu:

  • pizza 10 minut w 200 stopniach
  • jajka na twardo 12 minut w gotującej się wodzie
  • biszkopt pól godziny w 160 stopniach
  • itp (powyższe dane są z tyłka, więc jeśli napisałem głupoty to nie korygujcie mnie ;) ).

W gotowaniu faktycznie, więcej jest ogólnych zasad które pasują wszędzie. Ale w programowaniu, co do miejsca w którym warto coś umieścić, nie sądzę że tak jest. Weź pod uwagę, że gdyby było rozwiązanie które fits-all (jit, locki, optymalizacja, gc), typu "zawsze rób tak, bo jest najlepsze", to zaraz to by się stało częścią systemu operacyjnego, języka, albo biblioteki. To co zostawia się nam, programistom, to właśnie decyzje które są kontekstowe.

Warto się kierować wiedzą i informacjami od bardziej doświadczonych programistów, jasne. Jako ogólna zasada, na 100%.

Ale w tym konkretnym przypadku, czy liczenie jakiejś wartości na backendzie czy na froncie, moim zdaniem, najlepsze będzie jak OP się będzie kierował tym co mu wygodniej, a nie ogólnymi zasadami.

Gdyby postawione pytanie brzmiało inaczej (dotyczyło innego problemu), moja odpowiedź pewnie też byłaby odpowiednio inna.

1

Weź pod uwagę, że gdyby było rozwiązanie które fits-all

Nie ma idealnego - zgadzam się.
ALE
bazując na doświadczeniu życiowym, można przewidzieć, że w ogólnym przypadku PRAWDOPODOBNIE najlepiej będzie podejść do tematu w jakiś określony sposób. Gdy pojawią się okoliczności, które pokażą, że taki generyczny scenariusz tutaj nie pasuje - wtedy możemy zmieniać. Ale można przyjąć takie ogólne wytyczne, na których możemy bazować startując z projektem, a potem najwyżej rewizja i tuning założeń.

To co zostawia się nam, programistom, to właśnie decyzje które są kontekstowe.

Tak, zgadzam się
ALE
nie mając kontekstu można przyjąć jakieś ogólne zasady/wytyczne. To jak w medycynie: ogólnie na wysypkę daje się określoną maść/pigułki i to jest ogólnie zalecane w większości przypadków postępowanie. Ale jak się trafi pacjent z chorymi nerkami (czyli wprowadzamy elementy wspomnianego przez Ciebie kontekstu) to te pigułki mogą zaszkodzić, także stosujemy inne leczenie. Póki nie ma informacji o przeciwwskazaniach, jedziemy zgodnie z dobrymi praktykami i ogólnymi zasadami. Jak się pojawia konkretny problem/sytuacja do rozwiązania, to wtedy stosujemy alternatywne ścieżki.

Ale w tym konkretnym przypadku, czy liczenie jakiejś wartości na backendzie czy na froncie, moim zdaniem, najlepsze będzie jak OP się będzie kierował tym co mu wygodniej, a nie ogólnymi zasadami.

No to biorąc tą konkretną kwestię na tapet: zróbmy rachunek zysków i strat:

  • czy napisanie tego na froncie albo serwerze powoduje problemy techniczne? moim zdaniem pracy jest mniej-więcej tyle samo, więc tutaj mamy remis
  • która wersja jest bardziej rozwojowa/łatwiejsza do zmian albo rozszerzania? Zdecydowanie ogarnięcie tego na serwerze
  • i tak trzeba zrobić walidację na serwerze, także i tak musi być tam jakaś logika

Podsumowując 3 powyższe myślniki: robiąc to na serwerze nic nie tracimy (a jeśli jednak coś tracimy - proszę o informację i wady takiego podejścia), za to trochę zyskujemy. Więc chociażby z tego powodu - dobrze jest dać pytającemu konkretną radę (jak ja to robię - czyli że trzymaj i obliczaj na serwerze) a nie zrób jak Ci wygodniej.

1

@cerrato Mówisz o ogólnym podejściu początkującego pytającego o coś.

Ale ja mówię o tym konkretnym problemie od OP, czyli w którym miejscu napisać logikę. I tak jak się zgadzam z Tobą odnośnie ogólnych problemów; tak w tym konkretnym przypadku, uznaję że lepiej będzie mu polecić kierowanie się tym co jemu wygodnie, niż przyjęcie ogólnych zasad w tym konkrentmy case'ie.

Jak mówiłem, gdyby pytanie od OP brzmiało jak któreś z tych:

To wtedy byłbym z Tobą, i poleciłbym OP ogólnie przyjęte rady i zasady. Sam z resztą w tych wątkach takie polecałem, sugerowałem standardowe sposoby.

Ale w tym konkrentmy przypadku, w tym wątku, kiedy OP pyta o to gdzie powinien napisać logikę, uważam że ogólnie przyjęte rady są spoko, są okej; ale to żeby zrobił to gdzie jemu jest wygodnie, będzie lepsze.

cerrato napisał(a):

No to biorąc tą konkretną kwestię na tapet: zróbmy rachunek zysków i strat:

  • czy napisanie tego na froncie albo serwerze powoduje problemy techniczne? moim zdaniem pracy jest mniej-więcej tyle samo, więc tutaj mamy remis
  • która wersja jest bardziej rozwojowa/łatwiejsza do zmian albo rozszerzania? Zdecydowanie ogarnięcie tego na serwerze
  • i tak trzeba zrobić walidację na serwerze, także i tak musi być tam jakaś logika

Podsumowując 3 powyższe myślniki: robiąc to na serwerze nic nie tracimy (a jeśli jednak coś tracimy - proszę o informację i wady takiego podejścia), za to trochę zyskujemy. Więc chociażby z tego powodu - dobrze jest dać pytającemu konkretną radę (jak ja to robię - czyli że trzymaj i obliczaj na serwerze) a nie zrób jak Ci wygodniej.

Jasne. Ale ja bym jeszcze dodał zysk i straty napisanie tego na froncie czy na backendzie dla Ciebie będzie prostsze?

I dałbym średnią ważoną, 0.166, 0.166, 0.166 dla pierwszych trzech, oraz 0.5 dla tego co dla OP będzie prostsze.

Poza tym:

  • czy napisanie tego na froncie albo serwerze powoduje problemy techniczne? Moim zdaniem remis
  • która wersja jest bardziej rozwojowa/łatwiejsza do zmian albo rozszerzania? Tego bym nie polecał OP, bo to zachęca do przeinżynierowania.
  • i tak trzeba zrobić walidację na serwerze, także i tak musi być tam jakaś logika Nie wiesz tego, są aplikacje w których to co przyjdzie od klienta nie wymaga walidacji.
3

Dziękuję wszystkim za odpowiedzi, już rozumiem :) Obliczenia umieszczam w backendzie dla wygody (w dużym uproszczeniu, proszę nie cytować ;D), a we froncie tylko te mniejsze (jak np. pokazywanie sumy dodawanych w locie pozycji) - to dla mnie najbardziej logiczne rozwiązanie.

Cały wątek założyłem dlatego, że w aplikacjach desktopowych, czyli to co robiłem do tej pory, nie ma takiego podziału (ponownie, w dużym uproszczeniu, pomijam MVVM, Solid, itp.), jak w podejściu API + jakiś front. Wszystko robiło się w jednym miejscu.

PS: Mówiąc "umieszczam", mam na myśli będę umieszczać, bo dopiero będę zabierał się za swój pierwszy ćwiczeniowy projekt :)

Oczywiście, @Riddle też ma rację - nie należy być w tych kwestiach za bardzo sztywnych - to tak samo jak np. z DTO - nie każda encja potrzebuje trzech takich samych modeli dla dodawania, edycji, itd. Robi się je tylko wtedy, kiedy są one potrzebne - czyli gdy są różnice w modelach

Jeszcze raz bardzo dziękuję za wkład wszystkich osób :)

1
bbhzp napisał(a):

Czy takie obliczenia powinny być wykonywane u klienta za pomocą JS, czy może powinienem stworzyć jakiś nowy endpoint, np. /AvailableDates?

  • podejście ekonomiczne: Obliczenia wykonujemy tam gdzie nas to mniej kosztuje (w tym wypadku klient)
  • podejście bezpieczne: Obliczenia wykonujemy tam gdzie mamy pełną kontrolę nad środowiskiem (bekend)
  • odpowiedź rzeczywista: Ustalasz granicę tak żeby cię to nie zabiło finansowo i jednocześnie nie pogrążyło systemu. W tym wypadku jedynym twoim ratunkiem jest TDD i dobrze pokryty kod.
3
Riddle napisał(a):
bbhzp napisał(a):

Czy takie obliczenia powinny być wykonywane u klienta za pomocą JS, czy może powinienem stworzyć jakiś nowy endpoint, np. /AvailableDates?

Zacznij od tego, co Ci będzie najłatwiej zrobić.

Jeśli napotkasz problem: wtedy spróbuj w tym drugim miejscu. Jeśli nie, to wszystko w porządku.

@bbhzp Nie czytaj tego bełkotu. Odpowiedzią jest to zależy, a zależy od wymagań biznesowych. Nie ma uniwersalnego rozwiązania, a już metoda prób i błędów to najgorsze co możesz zrobić.

Poczytaj merytoryczne posty @cerrato który trafnie opisał jak profesjonalnie się podchodzi do takich pytań jak Twoje.

4

Im dłużej tu siedzę i przeglądam podobne tematy tym dziwie się czemu programiści mają problem z babami, skoro zachowują się tak samo. OP zadaje konkretne pytanie jak się robi, zamiast dostać konkretną odpowiedź zrób A, uważaj na B, jak nie wychodzi zrób C, tyle koniec. To dostaje całą stronę wykładów i niedopowiedzeń z których nic nie wynika, jak z babami pomęcz się i domyśl …

Potem siedzisz na takim daily w korpo, nie jesteś programistą ale coś tam dłubiesz w domu i słuchasz rozmowy managera z programistą. Słyszysz jak z prostych rzeczy do wytłumaczenia robi się lanie wody na 10 min, chyba ta praca tak lansuje głowę

0
markone_dev napisał(a):

@bbhzp Nie czytaj tego bełkotu. Odpowiedzią jest to zależy, a zależy od wymagań biznesowych. Nie ma uniwersalnego rozwiązania, a już metoda prób i błędów to najgorsze co możesz zrobić.

@bbhzp Metoda prób i błędów to właśnie nie jest najgorsze co możesz zrobić, powiedziałbym że to jest lepsza z metod, zwłaszcza jak zaczynasz i się uczysz.

Bo jeśli nie próby i błędy, to zostaje napisanie programu względem zasad i informacji posiadanych na początku projektu. Prawie żadne sensowne projekty nie powstały w ten sposób. Każda aplikacja i każdy projekt to jest ciągłe uczenie się, odkrywanie czegoś nowego, do każdego projektu wchodzimy z nieco innym mindsetem i nowymi informacjami. Nawet pisanie tej samej aplikacji drugi raz nie jest takie same, bo mamy nowe informacje i umiejętności. Są branże w których trzeba się raz czegoś nauczyć, a potem praca to jest wykorzystanie tej wiedzy. Programowanie takie nie jest, każda nowa aplikacja to jest nowa informacja; jak zdobywanie ekwipunktu w RPG, na każdym kroku zmieniamy jeden item na inny.

@bbhzp Moja rada dla Ciebie. Próbuj i popełniaj błędy. Zacznij od tego co jest najprostsze i najłatwiejsze. Jak utkniesz na czymś, wtedy warto się kierować dobrymi praktykami i zasadami. Przyjdź wtedy z konkretnym problemem na forum.

8

Moje podejście jest takie, że logika biznesowa powinna trafić na backend, bo:

  1. Jest bezpieczna i poufna
  2. Trzeba przesyłać mniej danych
  3. W pojedynczym miejscu, którym zajmuje się jedna osoba/zespół masz całość funkcjonalności, w dodatku owiniętą w REST API, które w jasny sposób komunikuje czego można wymagać od serwisu.

Front, również w moim prywatnym przekonaniu powinien zajmować się prezentacją. Czyli nie powinien np. szukać wolnych terminów, ale dostać ją z backendu, ale odpowiada już za ich sortowanie np. po dacie, albo po fryzjerze, dodatkowe filtrowanie (bo szybciej). Podstawowa walidacjadanych wprowadzonych przez użytkownnika, np. numeru telefonu, kodu pocztowego, daty również powinna być wykonana po stronie FE, ale musi być powtórzona po stronie BE. W sensie, nawet jeżeli FE wyśle jakąś bzdurę (np. nieistniejącą datę), to BE nie może się milcząco na to zgodzić. Backend ma swój stan (w bazie danych) i jest wyłącznie odpowiedzialny za integralność tego stanu. Po stronie FE walidacja jest jedynie pomocnicza, żeby użytkownik nie musiał wysłać całego formularza, poczekać 2 sekundy na otrzymanie błędu i dostać na twarz komunikat o konieczności ponownego wprowadzenia całości, bo zapomniał o kresce w kodzie pocztowym.

Jeszcze jeden praktyczny aspekt - w produkcyjnych systemach FE jest zmieniany dużo częściej niż BE, dlatego w przypadku tworzenia nowej lepszej stronki, lepiej nie mieć po jej stronie logiki i nie być zmuszonym do zmian o potencjalnie dużym wpływie.

1
piotrpo napisał(a):

Jeszcze jeden praktyczny aspekt - w produkcyjnych systemach FE jest zmieniany dużo częściej niż BE, dlatego w przypadku tworzenia nowej lepszej stronki, lepiej nie mieć po jej stronie logiki i nie być zmuszonym do zmian o potencjalnie dużym wpływie.

To byłaby prawda gdyby backend i frontend był jedynym sposobem na modularność w aplikacji; ale chyba nie jest? na froncie możesz mieć różne moduły, i na backendzie również. Mając modularność, możesz zmienić "logikę frontu" bez zmiany "logiki widoku". To nie jest tak że cały front to widok, front też ma swój stan, swoją logikę i swój widok.

1

@Riddle Nie o to chodzi. W wielu produkcyjnych aplikacjach funkcjonalność (więc i logika biznesowa) jest względnie stała w czasie. Natomiast w czasie całego cyklu życia systemu FE jest cyklicznie odświeżany, bywa, że pisany od początku, z powodów czysto estetycznych. W takich przypadkach zamiast przenosić logikę z Angulara do Reacta, czy odwrotnie, lepiej nie mieć czego przenosić.
Zakładasz też, że FE będzie jedynym klientem backendu, a przecież nie musi tak być, bo mogą się jeszcze pokazać np. aplikacje mobilne, albo integracje z innymi systemami.

@loza_prowizoryczna Serio porównujesz szyfrowanie asymetryczne do zaciemnonego kodu JS?
Tak, mogą wystąpić szczególne przypadki, w których np. nie chcemy danych przekazywać na BE, albo chcemy je zaszyfrować już po stronie FE. Może też być tak, że system wykonuje jakąś kosztowną obliczeniowo operację i zamiast płacić taczkę monet za serwery rozpraszamy te obliczenia na stacje klienckie. Nadal są to jednak przypadki szczególne, które nie zmieniają faktu, że w przytłaczającej większości zastosowań FE jest i powinien być jedynie widokiem.

1
bbhzp napisał(a):

Dziękuję wszystkim za odpowiedzi, już rozumiem :) Obliczenia umieszczam w backendzie dla wygody (w dużym uproszczeniu, proszę nie cytować ;D), a we froncie tylko te mniejsze (jak np. pokazywanie sumy dodawanych w locie pozycji) - to dla mnie najbardziej logiczne rozwiązanie.

Cały wątek założyłem dlatego, że w aplikacjach desktopowych, czyli to co robiłem do tej pory, nie ma takiego podziału (ponownie, w dużym uproszczeniu, pomijam MVVM, Solid, itp.), jak w podejściu API + jakiś front. Wszystko robiło się w jednym miejscu.

W aplikacjach desktopowych jest podział. Wiem że "się robiło" niestety widziałam takie kwiatki w Delphi gdzie wszytko na TForm, logika bezpośrednio pod buttonem :( zamiast użyć TDataModule jak należy, albo chociaż osobnej funkcji wywoływanej pod tym przyciskiem

Co do meritum to @cerrato i inni mają rację a @Riddle się myli.
Nie wiesz co Tobie będzie wygodniej zrobić jak baza się rozrośnie bo pewnie nawet nie pomyślałeś ze to będzie jakiś problem.
Logika na backend, a później to najwyżej ewentualnie można się zastanowić czy dane wyliczenie to kod w na backendzie (w modelu, nie w kontrolerze) czy kod w bazie danych na tym backendzie

PS: Mówiąc "umieszczam", mam na myśli będę umieszczać, bo dopiero będę zabierał się za swój pierwszy ćwiczeniowy projekt :)

Oczywiście, @Riddle też ma rację - nie należy być w tych kwestiach za bardzo sztywnych - to tak samo jak np. z DTO - nie każda encja potrzebuje trzech takich samych modeli dla dodawania, edycji, itd. Robi się je tylko wtedy, kiedy są one potrzebne - czyli gdy są różnice w modelach

Jeszcze raz bardzo dziękuję za wkład wszystkich osób :)

0
piotrpo napisał(a):

@Riddle Nie o to chodzi. W wielu produkcyjnych aplikacjach funkcjonalność (więc i logika biznesowa) jest względnie stała w czasie. Natomiast w czasie całego cyklu życia systemu FE jest cyklicznie odświeżany, bywa, że pisany od początku, z powodów czysto estetycznych.

Tak, ale jeśli masz odpowiednią modularność na froncie, to możesz mieć jedną część która jest cycklicznie odświeżana, i jedną która jest względnie stała. Kein problem.

piotrpo napisał(a):

Zakładasz też, że FE będzie jedynym klientem backendu, a przecież nie musi tak być, bo mogą się jeszcze pokazać np. aplikacje mobilne, albo integracje z innymi systemami.

Jasne, i kiedy tak się stanie, wtedy można dorobić odpowiednie struktury pod to. Ale z szacunkiem sugeruję żeby nie robić designu "bo może się okazać że". Pamiętajmy o YAGNI.

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.