(No tak!) Racja, niby tak naprawdę wiedziałem, ale jakoś nie chciało to do mnie dotrzeć.
@catom
Prawda, tak bardzo skupiłem się na tym całym Vavr'rze (ah ta odmiana), że wlazłem w NPE - no, ale o tym i tak jeszcze raz za chwile.
@danek
Dzięki za podpowiedź. Tak naprawdę, to podczas nauki Vavr'a to nieco wzoruje się na twoim projekcie Bet'ów oraz postu na mikroblogu o sposobie handlowania błędów razem z Either (*Error oraz ResponseResolver)
Odnośnie samego problemu to mam wrażenie, że nieco krzywo opisałem o co mi chodzi...
Załóżmy, że mamy przypadek gdzie musimy wykonać kilka różnych metod na kilku różnych serwisach które oczywiście (a może jednak nie tak oczywiste?) zwracają różny LeftValue (*Error).
public Either<OperacjaError, UdaloSieHurraDto> doSomething(DoSomethingDto doSomething) {
Either<RedError, RedResponse> red = redService.doSomething(...);
Either<GreenError, GreenResponse> green = greenService.doSomething(...);
Either<BlueError, BlueResponse> blue = blueService.doSomething(...);
return ...;
}
Zakładam, że taka sytuacja jest całkiem niewygodna, bo trzeba by mapować każdy LeftValue na nasz OperacjaError. Pytanie, czy jest to oznaka, że coś robimy nie tak?
Czy w ramach zawołania metody na serwisie powinniśmy się poruszać po tylko i wyłącznie jednym obiekcie błędów?
Np. taki TaskError, który załączyłem w pierwszym poście... posiada on wartość "USER_NOT_FOUND". Właściwie to jest ona powielona, ponieważ UserError również taki błąd posiada (z drugiej strony to najzwyczajnieszych błędem z punktu widzenia dodawaniu nowego taska jest nieistniejący użytkownik, więc 'konceptualnie' chyba jest ok). Zrobiłem tak głównie po to, żeby oszczędzić sobie zbędnego mapowania. Z kolei taka "OperacjaError" może posiadać te same wartości błędów i co Red/Green/Blue, tylko po to, żeby nie mapować, a jeśli już mapować to 1:1.
Nie jestem pewien czy wyraziłem się jasno, ciężko to wszystko opisać, ale jeśli zrobiłem to wystarczająco dobrze - jak radzicie sobie w takich sytuacjach?
Przykład mapowania z projektu @danek który jest tu
PointsError mapLeagueFacadeError(LeagueError error) {
return error == LeagueError.SET_NOT_SET_RESULT ? PointsError.SET_NOT_SET_RESULT : PointsError.MATCH_NOT_FOUND;
}
Osobne pytanie mam co do samego sprawdzania argumentów. Teoretycznie praktycznie zawsze możemy sprawdzać czy argumenty w danej metodzie nie będą przypadkiem null, a czasami nawet to kod nie przechodzi review jeśli takich sprawdzeń nie ma.
Zgodnie z tym, na co zwrócił uwagę catom w komentarzu :
Option.of(createTaskDto)
.toEither(TaskError.NO_TASK_PROVIDED)
Oczywiście abstrahując od tego, że tam jest potworne NPE (mea culpa) to opakowując to w Option'a nie mam tak naprawdę na myśli, że 'może go nie być', tylko to, że jak ktoś mi tego nulla jednak wrzuci to się nie wysypie - a nawet zarzuci mi od razu LeftValue jako dedykowany "NO_TASK_PROVIDED".
Pytanie nieco egzystencjonalne, i może nieco zależne od planowanej architektury - ale czy takie 'sprawdzacze' koniec końców w ogóle mają sens?
Jak to robicie w swoich projektach?