javaspring-bootvalidationvavr

How to map left side of Either with two different types erros with Validation and DomainError


I need help with Vavr in Java. I have a business process to update a book entity and I want to make validations before every update.

Steps:

  1. Request from RestController with Data to make Update as BookUpdateDto.
  2. Try to find BookById; return Book if exists or return Either with BOOK_NOT_FOUND.
  3. I make mapping from BookUpdateDto to BookCreateDto and validate.
  4. My validation return Validation<Seq<String>, BookCreateDto> and that's my problem.
  5. My question is: How I could pack my errors from validation with business error when a book does not exist?
public Either<BookError, BookCreateDto> updateBookById(final Long bookId, final BookUpdateDto toUpdate) {
        return findBookById(bookId)
                .toEither(BOOK_NOT_FOUND)
                .map(Book::response)
                .map(bookValidation::validate)
                
     

                .map(book -> book.mapToUpdate(toUpdate))
                .map(book -> book.update(toUpdate))
                .map(book -> bookRepository.save(book).response());
    }

I do not know how to map my errors from validation and domain error that BOOK_NOT_FOUND in empty space in code. Or maybe my architecture to solve my problem is wrong.


Solution

  • It looks like Validation.toEither combined with a flatMap could do the trick.

    The toEither changes the Validation into an Either<List<String>, BookCreateDto> and the mapLeft on that either changes the left side of the either from List<String> to a BookError.

    public Either<BookError, BookCreateDto> updateBookById(final Long bookId, final BookUpdateDto toUpdate) {
        return findBookById(bookId)
            .toEither(BOOK_NOT_FOUND)
            .map(Book::response)
            .map(bookValidation::validate)
    
            // This should perform the desired conversion
            .flatMap(validation -> validation.toEither().mapLeft(this::validationErrorsToBookError))
    
            .map(book -> book.mapToUpdate(toUpdate))
            .map(book -> book.update(toUpdate))
            .map(book -> bookRepository.save(book).response());
    }
    

    The validationErrorsToBookError would be a function that is responsible for turning a list of validations into a BookError. Probably something along these lines:

    BookError validationErrorsToBookError(Seq<String> validationErrors) {
        // Use logic that would fit your specific needs for BookErrors
        return new BookError(validationErrors.mkString(";"));
    }