Najlepsza alternatywa do wyjątków

0

Jaka jest waszym zdaniem najlepsza alternatywa do wyjątków, którymi niby nie powinno się sterować logiką biznesową? Jak modelujecie różnorakie errory? A może nawet trywialne wyjątki jak np. UserNotFoundException to nic złego?

7

Either<SomeDomainError, T> albo Optional<T> jeśli się akurat da.

3

3

Alternatywą jest podejście funkcyjne, gdzie z metody zwracasz obiekt, który ma informację o ewentualnym błędzie i który łatwo się komponuje z innymi podobnymi (Either, Try)

0

@Shalom: @scibi_92: @damianem: Mi bardziej chodziło o modelowanie błędów a nie o konstrukcje typu Either itp. Czym dokładnie jest ten DomainError?

3
Sampeteq napisał(a):

Czym dokładnie jest ten DomainError?

Najfajniej jak to jest jakis sealed class (sealed w sensie java/scala/kotlin).

2

Na przykład enum. Ewentualnie record z samym jakimś kodem błędu (nie mówię o kodzie HTTP) plus jakimś szczegółowym opisem błędu.

0

Jakąś klasą która opakowuje ci błędy w aplikacji? Odpowiednikiem takiego twojego UserNotFoundExcepotion z tą różnicą że nie rzucasz tego jako wyjątku. Reszta w dużej mierze zależy od ciebie i do tego co potem z tym robisz. Tak jak pisze @jarekr000000 czasem fajnie jak to jakieś sealed class bo możesz zrobić eleganckie mapowanie tego na co tam chcesz.

1
damianem napisał(a):

Alternatywą jest podejście funkcyjne, gdzie z metody zwracasz obiekt, który ma informację o ewentualnym błędzie i który łatwo się komponuje z innymi podobnymi (Either, Try)

Ja bym nie przesadzał z tym, że opakowanie danych albo błędu w zunifikowanym typie to jest podejście funkcyjne.

Sampeteq napisał(a):

Czym dokładnie jest ten DomainError?

Typem, jak wszystko.

2

Ja sugeruje zwracać coś rodzaju OperationResponse - po tym można dziedziczyć kolejne klasy. Błędy wynikające z logiki zwracamy przez OperationErrorResponse : OperationResponse. Sukcesy przez zwracanie OperationSuccrssResponse : OperationResponse. Błędy nieprzewidziane jak np. Niespójny JSON czy brał parametru przez wyrzucenie wyjątku jak OperationException. Ofc zwrócenie OperationErrorResponse może potem gdzieś wyrażać wyjątek żeby ASP czy inny framework rzucił 400 czy 500 do HTTP (o ile to REST API)

2

@pieczarek moim zdaniem trochę słabo się tego będzie używaż poza trywialnymi przypadkami. Częściej jednak chcesz takie coś móc "komponować", np. robisz operacje X która może sfailować a potem wynik tej operacji, jeśli nie jest errorem, przekazujesz do kolejnej operacji która też moze sfailować. W efekcie te twoje klasy Response efektywnie musiałyby mieć takie API jak... Either właśnie :)

0

@Shalom: Wiesz co - nie spotkałem się z tym wzorcem w żadnych projektach .net. Wygląda ciekawie - musiał bym poćwiczyć z tym. Zazwyczaj obsługa błędów to szczegół implementacyjny i wspomniane podeście wystarcza. Niemniej to jest kocept. Nie widzę problemu, żeby te obiekty to były drzewa OperationResponse i żeby w środku była referencja na korzeń, czy listę liści. Niemniej faktycznie to trzeba jakoś wygodnie opakować, ale koncepcje Either chyba też trzeba samemu napisać w C#, chyba, że się mylę? Prawda niestety... że tak czy siak, źle bo trzeba to stworzyć, no i teraz pytanie czy my chcemy pisać logikę w dobrej jakości narzędziach i frameworkach, czy może chcemy tworzyć i utrzymywać framework? Mieszanie tego nigdy nie wychodzi na dobre.

1
pieczarek napisał(a):

@Shalom: Wiesz co - nie spotkałem się z tym wzorcem w żadnych projektach .net.

No to prawda, w dotnecie jest generalnie dzicz i łupanie kamieni, ale od paru lat wprowadzam to rozwiązanie do różnych firm i generalnie ludziom się podoba.

Zazwyczaj obsługa błędów to szczegół implementacyjny i wspomniane podeście wystarcza.

Właśnie przez to, że wielu traktuje to jako nieważny szczegół, zamiast zaprojektować od początku do końca spójnie we wszystkich warstwach aplikacji, to soft często wygląda, jak wygląda, a potem jest płacz i debugowanie, bo nie wiadomo co się dzieje.

Niemniej to jest kocept. Nie widzę problemu, żeby te obiekty to były drzewa OperationResponse i żeby w środku była referencja na korzeń, czy listę liści. Niemniej faktycznie to trzeba jakoś wygodnie opakować, ale

Też przez przesady, wystarczy jeśli taki OperationResponse<TData> zawiera albo prawidłowe dane, albo hierarchię błędów dziedziczących po jakimś ErroBase. Przykłady takich błędów to: ValidationError, ExceptionError, BusinessError - i dopiero z tego mogą dziedziczyć bardziej specyficzne błędy - ale tylko jeśli gdzieś są inaczej traktowane, bo jeśli mają tylko wrócić jako komunikat do użytkownika, to nie ma co rozbudowywać hierarchii.

koncepcje Either chyba też trzeba samemu napisać w C#, chyba, że się mylę? Prawda niestety... że tak czy siak, źle bo trzeba to stworzyć, no i teraz pytanie czy my chcemy pisać logikę w dobrej jakości narzędziach i frameworkach, czy może chcemy tworzyć i utrzymywać framework? Mieszanie tego nigdy nie wychodzi na dobre.

Istnieje trochę przerośniętych paczek do tego, zazwyczaj próbujących robić z C# język funkcyjny, co wygląda trochę jak przerabianie jamnika na ośmiornicę przy pomocy przybitych desek. Jeśli nie chcesz mieszać kodu biznesowego z infrastrukturą, to zawsze przecież można wyrzucić tych kilka klas do oddzielnej paczki.

0

Nie chodzi o podział kodu Po prostu jestem zwolennikiem używania tego co daje MS w ramach .net, c# i ASP plus ew. popularne nugety. Unikam do minimum pisania infrastruktury i mikroframeworkow. Nie chodzi o podział kodu, ale takie frameworki są zawsze niedorobione, zabuggowane i zamiast walczyć z logiką i problemem walczymy z biblioteką naszego autorstwa do rzeczy x. Tak mówi moje doświadczenie. Niemniej jak są paczki z implementacja, które używa już xx projektów to warto spróbować.

2
pieczarek napisał(a):

Po prostu jestem zwolennikiem używania tego co daje MS w ramach .net, c# i ASP plus ew. popularne nugety. Unikam do minimum pisania infrastruktury i mikroframeworkow. Nie chodzi o podział kodu, ale takie frameworki są zawsze niedorobione, zabuggowane i zamiast walczyć z logiką i problemem walczymy z biblioteką naszego autorstwa do rzeczy x. Tak mówi moje doświadczenie. Niemniej jak są paczki z implementacja, które używa już xx projektów to warto spróbować.

Czyli jak rozumiem:

  1. napisanie (bool success, string error) (bo tak wygląda wielokrotnie quasi-either w C#) jest OK, bo tuple są wbudowane w język, mimo, że później lecimy z drabinką if-ów (które w sumie też są wbudowane w język
  2. napisanie własnego Either/Result (mówimy o jednej klasie i parę extensionów) jest złe, bo trzeba utrzymywać micro-framework i pewnie jest zabugowane
  3. użycie dużych bibliotek typu language-ext jest OK, bo są popularne, mimo, że to masa kodu, która nie jest nam potrzebna

W ogólności to co napisałeś ma sens, ale nie w tym konkretnym przypadku, gdzie koszt jest minimalny, a zysk ogromny.

0

@Saalin:

no a gdybyś sobie przekopiował najważniejsze klasy z language-ext?

i pewnie jest zabugowane

niesamowite jest ze ludzie potrafią walnąć projekty po >100k LoC, ale gdy trzeba napisać coś nawet małego, ale jest już na to biblioteka, to trzeba jej użyć bo pisząc samemu pewnie będą "jakieś bugi"

4

Dość się natrzepałem z bugami, wyciekami i problemami z wydajnością przy rzeczach, które już ktoś wymyślił i setki tysięcy ludzi używa. Mój kod, mając nawet sztab 50 testerów nie będzie tak przetestowany jak framework działający w milionach instancji. Po co pisać to samemu? Co z tego, że dociągamy wielką paczkę? Wielką - kilkaset KB czy nawet kilkadziesiąt MB - no i co z tego? Czy mój projekt ma 100MB czy 900MB DLL'i - szczerze nie obchodzi mnie to. To czy przy zwiększonym ruchu biblioteka okaże się bardziej dopracowana niż mój kod, bo nawet nie założyłem takiego ruchu - owszem bo to generuje realne straty.

Wole dociągnąć dużego nugeta niż wyciagać część klas - jak to potem aktualizować? Po to jest nuget, żeby łatwo podmieniać liba na nowego.

Dla mnie im mniej kodu powstaje tym lepiej, napatrzyłem się na to jak świat płonie przez pseudo frameworki. Żeby tworzyć bezpiecznie technologię trzeba mieć budżet większy niż projekty biznesowe, a biblioteka powinna działać w setkach projektów - wtedy można stwierdzić, że technologia jest przetestowana i się nadaje.

1
1a2b3c4d5e napisał(a):

no a gdybyś sobie przekopiował najważniejsze klasy z language-ext?

pieczarek napisał(a):

Wole dociągnąć dużego nugeta niż wyciagać część klas - jak to potem aktualizować? Po to jest nuget, żeby łatwo podmieniać liba na nowego.

Ale wcale nie chodzi o wielkość binarek czy cokolwiek takiego. Im większa biblioteka tym więcej błędów. Twierdzenie, że jeśli biblioteka jest popularna ma mało błędów jest fałszywe, a być może na odwrót - im większa biblioteka tym więcej okazji do błędów. Dziwne, że trzeba o tym przekonywać po ostatnich cyrkach z log4j.

Dociąganie zależności za każdym razem, gdy wydaje nam się, że to rozwiązuje nasz problem to potencjalnie dodawanie problemu. Szczególnie jeśli chcemy rozwiązać względnie prostą rzecz (np. jakąś prostą maszynę stanów), a bierzemy do tego wielki framework do workflowów, bo jest third-party i nie musimy go utrzymywać.

5

Dziwne, że trzeba o tym przekonywać po ostatnich cyrkach z log4j.

@Saalin

W ogóle bym nie mieszał do tego sytuacji z log4j, paradoksalnie. Spróbuj napisać własnego loggera ;)

W świecie, w którym ciągle wymyślamy koło na nowo (1) daleko nie zajedziemy, bo to nie jest żaden postęp pisać swojego loggera (2) wladujemy się w kłopoty i będziemy latać po nocach jakieś podatności. Log4j miał fatalna podatność, ale zadaj dobie pytanie, ile podatności wcześniej załatał, przed iloma fakapami Cię uchronił :) survivor bias to się chyba nazywa.

Ja rozumiem, że fajnie się pisze kod, ale lepiej ten czas spożytkować na pisanie sensownego kodu, który daje wartość, a przynajmniej wnosi coś nowego. Nikt nie potrzebuje 1535 implementacji loggera (na przykład).

1

Niepotrzebnie pojechałem z tematem log4j, sorry. Skupmy się na tym konkretnym - Either i jego implementacja w C#. Ja wcale nie jestem za pisaniem własnego kodu, kompilatorów, bibliotek - niepotrzebna ironia @pieczarek . Wręcz przeciwnie - lubię pisać jak najmniej kodu składając go z dostępnych klocków, bo w ten sposób zarabiam nie męcząc się. Gdyby naprawdę tym co rozważamy była alternatywa: weźmy bibliotekę, która spełnia nasze oczekiwania (tj. dostarcza Option i Either) lub piszmy to samemu to bym się rękami i nogami podpisał pod dodaniem zależności. Problem polega na tym, że najczęściej wtedy pada argument, że po co, skoro to dodatkowa zależność, która jest niekonieczna i już nawet w tym wątku padł ten argument (trochę niewprost):

pieczarek napisał(a):

Po prostu jestem zwolennikiem używania tego co daje MS w ramach .net, c# i ASP plus ew. popularne nugety.

I w ten sposób zamiast używać dobrych rozwiązań (bo takim jest zdecydowanie koncept Option i Either) kończymy z jakimiś ulepami (i wracając do tych nieszczęsnych tupli, którymi rzygam przy każdej okazji: przecież Tuple (success, error) jest izomorficzny z Either, jeśli to owrapujemy i dodamy do niego 2 extensiony, ale tego nie wolno nam robić, bo to już biblioteka wewnątrz projektu...)

Więc podsumowując:
Gotowe biblioteki > pisanie kodu samemu > rakowe rozwiązania wbudowane w język

2
pieczarek napisał(a):

Nie chodzi o podział kodu Po prostu jestem zwolennikiem używania tego co daje MS w ramach .net, c# i ASP plus ew. popularne nugety.

No ale niestety MS jest tak zajęty pisaniem swojego szóstego ORMa, że w 2022 wciąż nie dostarcza podstawowych typów.

Unikam do minimum pisania infrastruktury i mikroframeworkow.

Tylko tu nie ma mowy o żadnym frameworku, lecz o biblioteczce złożonej z kilku klas, która ma mniej niż 50 linii realnego kodu, i do której napisanie testów jednostkowych też nie jest wyzwaniem.

pieczarek napisał(a):

Co z tego, że dociągamy wielką paczkę? Wielką - kilkaset KB czy nawet kilkadziesiąt MB - no i co z tego?

No to, że w ten sposób nie użyjesz 95% kodu, za to przy okazji dociągniesz 95% błędów.
Wszystko jest kwestią kompromisu, mi w przypadku czegoś tak małego nie chciałoby się używać cudzego overengineeringu.

Wole dociągnąć dużego nugeta niż wyciagać część klas - jak to potem aktualizować? Po to jest nuget, żeby łatwo podmieniać liba na nowego.

Przecież swoją libkę też możesz trzymać w firmowym nugecie.

0

Nie odniosę się do całości bo nie ma sensu i to tylko strzępienie języka. Fakt MS jeszcze tego nie dostarczył - nie wiem czy zamierza. Nie interesuje się też jaki aktualnie ORM robi - używam kilku i mi starcza jak na razie. To że nugety i nasze małe klasy to zawsze kompromis, podejmowany w kontekście danego problemy, więc akurat tutaj przy abstrakcyjnym problemie - jak zwracać błędy będzie to tylko [CIACH!] po próżnicy. Jak zawsze - to zależy.

somekind napisał(a):

Wole dociągnąć dużego nugeta niż wyciagać część klas - jak to potem aktualizować? Po to jest nuget, żeby łatwo podmieniać liba na nowego.

Przecież swoją libkę też możesz trzymać w firmowym nugecie.

Tak, ale chodzi mi o fakt - że jeśli wycinam już część liba to chciałbym korzystać z tego i aktualizować chociaż tą część struktur. Jeśli spójne to pół biedy. Jak nie to problem. No i jak to robić- WinMergem, czy może forkować i patchować z innego repo? Wszystko można ale to wkład pracy naszej. Można też odciąć kilka klas i wdrożyć do swojego liba (o ile licencja pozwala), ale wtedy zupełnie na nas spada utrzymanie i otrzymujemy tylko (i aż) gotowy produkt, nad którym przejmujemy kontrolę. Tu nie chodzi o medium, ale o pracę którą musimy wkładać. Używając pełnej libki można zakładać przynajmniej, że upgrade nie będzie się wiązał z dodatkową pracą czy dostosowaniem kodu.

1
pieczarek napisał(a):

Tak, ale chodzi mi o fakt - że jeśli wycinam już część liba to chciałbym korzystać z tego i aktualizować chociaż tą część struktur. Jeśli spójne to pół biedy. Jak nie to problem. No i jak to robić- WinMergem, czy może forkować i patchować z innego repo?

Nie wiem. Dla mnie pomysł wycinania kawałków cudzego liba do takiej banialuki jest nieco szalony.
Mowa o kilku bazowych klasach i interfejsach, raz napisane praktycznie nie będą potrzebowały aktualizacji.

Tu nie chodzi o medium, ale o pracę którą musimy wkładać. Używając pełnej libki można zakładać przynajmniej, że upgrade nie będzie się wiązał z dodatkową pracą czy dostosowaniem kodu.

Nigdy nie spotkałeś się z niekompatybilnymi wstecznie zmianami albo bugami w używanych libkach?

0

Dla mnie też pomysł wycinania części liba jest niedorzeczny co ciągle powtarzam...

Co do liba - tak zdarzyło się. Niemniej zdarzyło mi się też aktualizować bibliotekę, któej kod ktoś wciągnął do projektu i zamiast jak człowiek robić PR na githubie postanowił sam sobie naprawić i rozwinąć - po co inni mają korzystać za darmo z jego pracy... Aktualizacja tej chimery semi open source było katorgą. Dlatego ostatecznie albo coś piszę sam albo wciągam z dobrodziejstwem inwentarza mając w nosie dodatkowe nieużywane klasy.

0

@Charles_Ray:

Log4j miał fatalna podatność, ale zadaj dobie pytanie, ile podatności wcześniej załatał, przed iloma fakapami Cię uchronił :)

prawdopodobnie 0

Moja libka się nie zmienia ani nie rozwija, a duże OSSy pewnie mają milion requestów aby dodać yet another integracje z czymkolwiek, a później pojawia się zgrzyt.

Możesz się upierać przy wydajności, lepszym API, cost efficency, ale że security, to już nie kupuje.

3

To, czy pisać samemu, czy użyć gotowej biblioteki, zależy od tego, jak dużo ficzerów potrzebujemy. Jak chcemy mieć jedynie Resulta z jakimś Map i Tap, no to zaciąganie zewnętrznych bibliotek jest pewnie niepotrzebne, bo to jakieś 100 linii kodu. Ale jak chcemy pisać funkcyjnie, to raczej bez sensu pisać samemu te wszystkie extensiony do eitherów / resultów, tak jak tu.

3

Między pisaniem własnego kodu do dawno opracowanych rozwiązań, a dodawaniem zależności po każdą pierdołę warto zachować zdrowy rozsądek.

Pisanie własnych eitherów to przesada. Potencjalne zagrożenia, które takie biblioteki wprowadzają są znikome. W najgorszym przypadku trzeba będzie zmienić ich implementację na coś innego. Często żmudne, ale wykonalne.
Większym problemem jest gdy persystencję i obsługę logiki aplikacji oddajemy w ręce jakiejś "genialnego" rozwiązania 3rd party. np. zamiast zamodelować sensownie proces w kodzie to dodaje się bibliotekę BPMNową, która nie rozwiązuje nic, a gdy takie g**no wejdzie na produkcję to już się z tego praktycznie nie da zejść.

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