Errors as values, the Fusion mechanicsFailIf accumulates; Bind short-circuits
No narration yet
Module 2 · Lesson 213 min

FailIf accumulates; Bind short-circuits

Now the idiom you'll write most. To validate, you chain guards with FailIf, then terminate with OrSuccess. The crucial, easy-to-miss detail: FailIf accumulates. It does not stop at the first failure. If three rules are broken, the Result comes back with all three errors, so the user fixes everything at once instead of playing whack-a-mole.

The guard chain (domain/flight-notes/FlightNoteVo.cs)
public static Result<FlightNoteVo> Create(string? ejdpUniqueFlightKey, string? body)
{
    var trimmedBody = (body ?? string.Empty).Trim();

    return Result<FlightNoteVo>
        .FailIf(string.IsNullOrWhiteSpace(ejdpUniqueFlightKey), MissingFlightKey)
        .FailIf(trimmedBody.Length > MaxBodyLength,
            new EjError(nameof(Body), $"A note body must be at most {MaxBodyLength} characters."))
        .OrSuccess(() => new FlightNoteVo(ejdpUniqueFlightKey!.Trim(), trimmedBody));
}

OrSuccess takes either a value or a factory Func<T>. Use the factory form (() => new ...) when building the value would be wrong or would throw if the guards had failed, since the factory only runs on the success path. Notice ejdpUniqueFlightKey! inside it: the null-forgiving ! is safe precisely because the FailIf above already proved it's non-null on this path.

Sometimes you need the opposite: step B depends on step A's value, so if A fails there's nothing to feed B. That's Bind. It short-circuits: on failure it returns the failure untouched; on success it runs the next step with the value. Use FailIf to gather independent rules, Bind to sequence dependent steps.

Bind sequences dependent steps (FlightDisruptionAggregate.cs)
DisruptionDelayVo.Create(isCancelled, delayMinutes)
    .Bind(delay => Eu261Vo.Create(isExtraordinary, delay, pax, routeClaimRate, weighting));
The Result combinators (domain/Result.cs)
FailIfAccumulate an independent rule; chainable, does NOT short-circuit
OrSuccessTerminal: build the value if no errors accrued
BindSequence a dependent step; short-circuits on failure
MapTransform the success value; passes failure through
TapRun a side-effect on success, return the Result unchanged
TapAsyncAsync Tap: await a side-effect (e.g. save) on success
Practice

Try it yourself

Do

Write a Create with an accumulating chain

Build the muscle: turn two rules into an accumulating factory. Model it on FlightNoteVo.Create above.

Tick every step to confirm you did it.

Quiz

FailIf or Bind

You validate two independent fields on a request, and you want the caller to see BOTH errors if both are wrong. Which do you reach for?

Recall

The two chaining shapes

Lock the distinction in.

In one line each: when do you use FailIf and when do you use Bind?