ForkAttempt

Overview

ForkAttempt<T> records the outcome of a single fork step inside a larger bulk operation. It’s a record with three slots:

public record ForkAttempt<T>(
    Command cmd,        // the command that drove this step
    T result,           // populated on success
    Problem problem     // populated on failure
) { }

Exactly one of result and problem is non-null on a well-formed instance. The accompanying cmd lets the caller correlate the outcome with the input that produced it — useful for partial-success error reporting in the response.

ForkAttempt.from

The static factory ForkAttempt.from(supplier, invoker) is the workhorse. It composes:

  1. a command supplier — produces the command for this step (the place validations might fail), and

  2. a command invoker — actually runs the command (the place the service might fail).

ForkAttempt<Ninja> outcome = ForkAttempt.from(
    () -> generator.generateNinjaCommand(ctx, originalNinja),
    cmd -> ninjaService.create(cmd)
);

Any ThrowableProblem raised by either lambda is caught, converted to a Problem, and packaged into the returned ForkAttempt. Other exception types continue to propagate — only domain problems are converted, so genuine bugs still surface.

This keeps the bulk-fork loop simple: each step is independent, the outer loop never has to handle exceptions, and the caller gets a structured list it can render into a partial-success response (see Bulk Forking).