1

Although the following example will be written in Kotlin syntax, the question is not Kotlin specific and could be also implemented in Java in a similar fashion.

Assume we have an entity called Person which persists person data such as firstname, lastname, username. Now assume we have a PersonService and we want a function to update the username. A naive and perfectly-fine approach would be the following:

@Transactional
fun updateUsername(id: UUID, username: String): Person? {
    return personRepository.findByIdentityId(id)
        ?: throw NotFoundException()
        .apply { this.userName = username }
        .let { personRepository.save(it) }
}

Note the @Transactional annotation to wrap the database operations performed by the personRepository in a database transaction which will rollback if any Exception occurred in our code, such as the NotFoundException if we cannot find a record with the corresponding id in the database.

Now comes my problem: I want to rewrite this function in a more functional way instead of throwing exceptions. In this regard, I use the Either (https://arrow-kt.io/docs/apidocs/arrow-core/arrow.core/-either) type of the arrow library (https://arrow-kt.io/docs/patterns/error_handling) to indicate success or failure. Let's look at the updated method signature (the function code does not matter for the question).

@Transactional
fun updateUsername(id: UUID, username: String): Either<NotFoundException, Person>

So instead of my function throwing an NotFoundException where consumers have to know about by looking into the body of the function, we explicitly tell the consumer what could go wrong and they are forced to handle the response in some way. Obviously now we get a problem with Spring declarative transaction management which relies on my code to throw exceptions if something went wrong so it can rollback the transaction.

Therefore my question is: Is there a way to somehow interfere the transaction at it's very end and check if the return value is of type Either.Left (indicating failure) and then marking the transaction as rollbackOnly? I wrapped my head around Spring transaction management but could not find a way to do so. If there is no way to interfere a transaction right before it returns the value of the annotated invoked method, maybe a solution could be to provide a custom annotation like @EitherTransactional?

Important: If you got confused by the functional programming stuff or the arrow library cited above, here is another very simplistic demonstration of my question.

@Transactional
fun rollbackForReturnValue(): String {
    // do some database updates
    return "failure"
}

I want to rollback the transaction if the return value equals "failure" like in the example. How would you achieve that behavior? Is is possible to intercept the transaction right before the return value will be returned to the invoking consumer?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • 2
    You would need to manually manage the transaction as the default transaction support is based on exceptions (in case of exception rollback else commit). – M. Deinum Jul 15 '21 at 10:41
  • 1
    Does this answer your question? [Kotlin's Arrow Either and transactions](https://stackoverflow.com/questions/67299007/kotlins-arrow-eitherexception-x-and-transactions) – JasonB Aug 13 '21 at 16:50
  • https://stackoverflow.com/questions/67299007/kotlins-arrow-eitherexception-x-and-transactions – JasonB Aug 13 '21 at 16:50

0 Answers0