DEV Community

Machine coding Master
Machine coding Master

Posted on

Ditch Vavr: Native Java 21 Sealed Result Pattern Matching Is All You Need

Ditch Vavr: Native Java 21 Sealed Result Pattern Matching Is All You Need

Stop bloating your Spring Boot microservices with third-party FP libraries like Vavr just to avoid runtime exceptions. Java 21's sealed interfaces and record patterns give you type-safe, zero-dependency railway-oriented error handling built straight into the JDK.

Why Most Developers Get This Wrong

  • Importing Vavr or Arrow purely for Either or Try, dragging unnecessary transitive dependencies into modern microservices.
  • Throwing runtime exceptions for predictable domain failures, which destroys performance through stack trace generation and hides errors from method signatures.
  • Building custom Result wrappers that rely on clunky instanceof checks or deeply nested lambda callbacks instead of native language features.

The Right Way

Model domain outcomes as a generic sealed hierarchy and consume them using exhaustive record pattern matching in switch expressions.

  • Define a generic sealed interface Result<T, E> restricted strictly to Success<T> and Failure<E> records.
  • Return Result explicitly from service methods to force caller handling at compile time.
  • Deconstruct record payloads directly inside switch expressions without explicit casts or getter clutter.
  • Eliminate fallback default cases so the compiler catches unhandled error states instantly during refactoring.

Show Me The Code

public sealed interface Result<T, E> {
    record Success<T, E>(T value) implements Result<T, E> {}
    record Failure<T, E>(E error) implements Result<T, E> {}
}

// Consuming domain results via record deconstruction
public String processPayment(Result<Transaction, PaymentError> result) {
    return switch (result) {
        case Result.Success(var tx) -> "Paid: " + tx.id();
        case Result.Failure(PaymentError.InsufficientFunds e) -> "Low balance: " + e.amount();
        case Result.Failure(PaymentError.GatewayTimeout e) -> "Retry later";
    };
}
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  • Zero-Dependency FP: Ditch legacy functional libraries; JDK 21 provides all the language primitives you need for robust error handling.
  • Compiler-Enforced Safety: Exhaustive switch expressions eliminate unhandled edge cases at compile time without runtime guard clauses.
  • Explicit Domain Flow: Deconstructing records in switch arms produces clean, readable code that treats errors as first-class domain concepts rather than unexpected disruptions.

Shameless plug: javalld.com has full LLD implementations with step-by-step execution traces — free to use while prepping.

Top comments (0)