Operacje w monadach?

0

Co sądzicie o takim wykorzystywaniu Monad? Chodzi mi o wykorzystanie metody send(String id) {...} i chainowanie razem wszystkich operacji wykonywanych w niej.

  1. Szukam elementu po id
  2. Predicate czy można na nim działać
  3. Operacja "główna"
  4. Zmiana EmailStatus z PENDING na SENT
  5. .save() na repository

Chciałbym zwracać tutaj Either zamiast Email. Da rade jakoś to tak zorbić żeby left było rzucane tam gdzie się coś wywaliło?

  • na findById
  • isEmailPending
  • na emailSenderService::send

Czy może jakoś inaczej do tego podejść?

@RequiredArgsConstructor
@Service
public class EmailFacadeServiceImpl implements EmailFacadeService {
    private final EmailPersistenceService emailPersistenceService;
    private final EmailSenderService emailSenderService;

    private final Predicate<Email> isEmailPending =  email -> email.getStatus().equals(EmailStatus.PENDING);
    private final Function<Email, Email> setEmailStatusToSent = email -> new Email(email.getId(), email.getHeader(), email.getMessage(), EmailStatus.SENT);

@Override
    public Email send(String id) {
        return emailPersistenceService.findById(id)
                .filter(isEmailPending)
                .map(emailSenderService::send)
                .map(setEmailStatusToSent)
                .map(emailPersistenceService::save)
                .getOrElseThrow(() -> new RuntimeException()); // ???
    }
1

Da rade jakoś to tak zorbić żeby left było rzucane tam gdzie się coś wywaliło?

Tak. W tym celu nalezy zwracać left tam gdzie coś sie wywaliło.

  1. I prawie wszystkie operacje pozmieniasz na flatMap.

  2. Końcówka z throw Runtime mocno bez sensu. Zwracasz Eithera.

  3. Zamodeluj sobie jak wygląda bład (enum? klasy jakieś)

0

Dobra, skleiłem coś takiego. Jestem z siebie dumny :D

    @Override
    public Either<Error, Email> send(String id) {
        return emailPersistenceService.findById(id)
                .filterOrElse(isEmailPending, x -> Error.SENT)
                .flatMap(emailSenderService::send)
                .map(setEmailStatusToSent)
                .map(emailPersistenceService::save);
    }
    @Override
    public Either<Error, Email> findById(String id) {
        return Option.ofOptional(emailRepository.findById(id))
                .toEither(() -> Error.NOT_FOUND);
    }
public interface EmailSenderService {

    Either<Error, Email> send(Email email);

}

Na poziomie controllera mam jeszcze generator response entity bazujący na either itp.

0

Mam jeszcze jedno pytanie. Jak rozwiązać sprawę ze zwracaniem JSON'a w ResponseEntity? Jak Zwracam Vavrową listę w ReponseEntity dostaje strasznie dziwną odpowiedź. Powinienem mieć 4 elementy, dostaje jeden zagnieżdżony pod "orNull". Nie mogę znaleźć odpowiedzi jak postąpić w takiej sytuacji.
Coś takiego:

{
    "empty": false,
    "lazy": false,
    "traversableAgain": true,
    "async": false,
    "sequential": true,
    "ordered": false,
    "distinct": false,
    "singleValued": false,
    "orNull": {
        "id": "5f00f2a2dc619338d33b67e3",
        "subject": "Welcome",
        "text": "z drugim mailem"
    },
    "memoized": false
}

Przerobiłem Liste vavrową na widok listy javovwej .asJava() i jest ok:

    @GetMapping
    public ResponseEntity<java.util.List<Email>> findAll() {
        return new ResponseEntity<>(emailFacadeService.findAll().asJava(), HttpStatus.OK);
    }

List vavrowych nie powinno się bezpośrednio zwracać?

2
Korges napisał(a):

List vavrowych nie powinno się bezpośrednio zwracać?

Można zwracać o ile podepniesz
https://github.com/vavr-io/vavr-jackson

Podpina sie to pod ObjectMappera
Ty chyba z jakiegoś Springa korzystasz, więc musisz liczyć na magię - (ale chyba nie ma z tym problemu).

1

Co do serializacji typów z vavra: https://stackoverflow.com/questions/46285615/serializer-deserializer-for-vavr-objects

Edit: ups za późno :)

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