Vavr - Try i Either, a obejście pewnej sprawy.

0

Cześć, powstała mi taka o to magia z Either i Try. Ogólnie chodzi o to, że Retry ma reagować tylko na jeden exception i zwracać jeden ApiError, pozostałe mają być łapane i opakowywane w inny ApiError. Musiałem ten FeignAdapter opakować, bo inaczej zanim AOP zadziałało i Retry wszedł to Try łapał już FeignException.InternalServerErrror i niestety zwracało mi INTERNAL_ERROR. Doszedłem do takiej wiązanki gdzie na końcu muszę zrobić .flatMap(t -> t), a jestem pewny że da się to zrobić ładniej.. Już chyba try catch wygląda lepiej. Dlatego wzywam specjalistów od FP i Vavra. Ewentualnie może widzi ktoś w ogóle jakieś rozwiązanie bez tych wrapperów... Z góry dzięki za pomoc wszystkim.

class FeignAdapter {
    @Retry(name = "sample", fallbackMethod = "retryResponse")
    public Either<ApiError, CommandResult> get(Command command) {
        return Either.right(feignClient.getCalculations(command))
    }

    public Either<ApiError, CommandResult> retryResponse(Command command, FeignException.InternalServerError exc) {
        return Either.left(ApiError.builder()
                .errorType(ErrorType.SERVICE_UNAVAILABLE)
                .build());
    }
}

class Clazz {       
    private final FeignAdapter feignAdapter;

    public Either<ApiError, CommandResult> get(Command command) {
       return Try.of(() -> feignAdapter.get(command))
              .toEither(
                       ApiError.builder()
                               .errorType(ErrorType.INTERNAL_ERROR)
                               .build()
               )
              .flatMap(t -> t);
    }   
}
2
  1. Straszne jest to mieszanie AOP i Vavr - ale będzie działać
  2. flatMap(t->t) jest ładne - to konsekwencja tego, że nie ma operacji flatten - ale IMO spoko
  3. Niewiele się da poprawić - problem jest tak naprawdę u źródła, operacje niższego poziomu rzucają wyjątkami, opakowywanie tego zawsze będzie troche słabe
    Logicznie powinno być w drugą stronę - najpier Either- a jak nie wiemy co z nim zrobić to Exception. (Podobnie jak z API, które zwraca null, którego opakowujemy w Optionala. Czasem to naprawde średnio sensownie wygląda, ale de facto, to nie do końca problem Optionala tylko tego, że niżej jest null).
  4. Try catch też jest dla ludzi - o ile dotyczy wyjątków. U Ciebie kluczowe jest pytanie jak fajny/niefajny jest kod klienta korzystający z Clazz.get. (Bo Eithery są po to, żeby klient (konsument API) był ładny )
0

Ogólnie ten kod, który korzysta z tego Clazz jest nowy, więc stara się być ładny - temu chciałem skorzystać z FP. Tam nie ma jakiejś wielkiej logiki, głównie to polega na mapowaniu i pytaniu dalej. Ten Clazz jest adapterem do portu, który trafia sobie tam do domeny i obrabia te dane dalej. Potem wysyła to na resta.
No największym problemem jest chyba to AOP z Resilience4j, które niszczy marzenia. Kłopotem nie jest Either czy Try, tylko że to tak trzeba wykombinować.
Czyli jak najlepiej to rozwiązać?

1

Jak chcesz uniknąć flatMap(t->t) to chyba możesz użyć getOrElse lub getOrElseGet:

 return Try.of(() -> feignAdapter.get(command))
                  .getOrElse(
                          Either.left(ApiError.builder()
                                          .errorType(ErrorType.INTERNAL_ERROR)
                                          .build())
                  );

i po lekkim uprzątnięciu:

return Try.of(() -> feignAdapter.get(command))
                  .getOrElse(Either.left(ApiError.internalError()));

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