spring-projects / spring-projects/spring-framework

Add support for Either besides Try in TransactionAspectSupport in spring-tx

Open
#36,128 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

in: data status: waiting-for-triage
Dominant language
Java
Stars
60.2k
Forks
38.8k
Avg merge
5d 2h
Merged PRs (30d)
27

Description

Description

Spring Framework already supports Vavr's Try type for transaction rollback decisions in TransactionAspectSupport. This proposal requests adding equivalent support for Vavr's Either type, which is widely used for railway-oriented programming and functional error handling.

Motivation

Either<L, R> is a common pattern for representing success (Right) or failure (Left) without throwing exceptions. It's the preferred approach for:

  • Railway-oriented programming
  • Explicit error handling in functional pipelines
  • Domain-driven design where errors are domain concepts, not exceptions

Currently, a method like this will commit even when returning a failure:

@Transactional
public Either<PaymentError, Receipt> processPayment(PaymentRequest request) {
    return validateRequest(request)
        .flatMap(this::checkFunds)
        .flatMap(this::executeTransfer);
    // Returns Either.Left(InsufficientFunds) → transaction COMMITS (unexpected!)
}

Users expect Either.Left to trigger a rollback, similar to how Try.Failure does.

Proposed Change

Add Either support in TransactionAspectSupport.VavrDelegate, mirroring the existing Try implementation:

In invokeWithinTransaction() (~line 380):

// Existing Try support
else if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
    retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
// Proposed Either support
else if (vavrPresent && VavrDelegate.isVavrEither(retVal)) {
    retVal = VavrDelegate.evaluateEitherLeft(retVal, txAttr, status);
}

In VavrDelegate inner class:

public static boolean isVavrEither(@Nullable Object retVal) {
    return (retVal instanceof Either<?, ?> either && either.isLeft());
}

public static Object evaluateEitherLeft(Object retVal,
        @Nullable TransactionAttribute txAttr, TransactionStatus status) {
    return ((Either<?, ?>) retVal).peekLeft(left -> {
        if (left instanceof Throwable throwable) {
            if (txAttr != null && txAttr.rollbackOn(throwable)) {
                status.setRollbackOnly();
            }
        } else {
            // Non-Throwable Left: rollback by default
            status.setRollbackOnly();
        }
    });
}
Behavior
Return Value Transaction Outcome
Either.Right(value) Commit
Either.Left(throwable) Rollback if txAttr.rollbackOn(throwable) matches
Either.Left(nonThrowable) Rollback (default)
Scope

This proposal (Phase 1):

  • Basic Either.Left → rollback support
  • Consistent with existing Try.Failure behavior
  • Minimal change (~30-50 lines)

Future enhancement (Phase 2):

  • Custom rollback rules for non-Throwable types (rollbackForWithEither attribute)
  • Fine-grained control over which Left values trigger rollback
Impact
  • No breaking changes - additive only
  • No new dependencies - Vavr is already optional
  • Consistent - matches existing Try support pattern
  • Low risk - isolated to VavrDelegate
Alternatives Considered
  1. Do nothing - Users must wrap Either in Try or throw exceptions (breaks functional style)
  2. Custom TransactionInterceptor - Requires significant boilerplate for each project
  3. AOP around advice - Happens after commit, too late for rollback
References

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in TransactionAspectSupport.invokeWithinTransaction() and read the existing VavrDelegate.evaluateTryFailure() implementation. Compare the requested Either behavior with the existing Try handling and verify the stated Right, Throwable Left, and non-Throwable Left transaction outcomes. Done means Either.Left handling is integrated without new dependencies and remains consistent with the proposed rollback rules.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.