TurboMediator
Resilience

Fallback

Define fallback strategies when handlers fail

The fallback behavior provides alternative responses when the primary handler fails. You can chain multiple fallback handlers with priority ordering and exception type filtering.

IFallbackHandler

public interface IFallbackHandler<in TMessage, TResponse>
    where TMessage : IMessage
{
    ValueTask<TResponse> HandleFallbackAsync(
        TMessage message,
        Exception exception,
        CancellationToken cancellationToken);
}

Using the Attribute

[Fallback(typeof(CachedProductFallback), Order = 1)]
[Fallback(typeof(DefaultProductFallback), Order = 2)]
public record GetProductQuery(Guid Id) : IQuery<Product>;

Attribute Properties

PropertyDescription
typeof(HandlerType)The fallback handler type
OrderExecution priority (lower runs first)
OnExceptionTypesOnly trigger for specific exception types

Fallback Handlers

// Primary fallback: try cache
public class CachedProductFallback : IFallbackHandler<GetProductQuery, Product>
{
    private readonly ICacheProvider _cache;

    public CachedProductFallback(ICacheProvider cache) => _cache = cache;

    public async ValueTask<Product> HandleFallbackAsync(
        GetProductQuery message, Exception exception, CancellationToken ct)
    {
        var cached = await _cache.GetAsync<Product>($"product:{message.Id}");
        return cached ?? throw new InvalidOperationException("No cached product");
    }
}

// Secondary fallback: return default
public class DefaultProductFallback : IFallbackHandler<GetProductQuery, Product>
{
    public ValueTask<Product> HandleFallbackAsync(
        GetProductQuery message, Exception exception, CancellationToken ct)
    {
        return new ValueTask<Product>(Product.NotFound(message.Id));
    }
}

Using Configuration

builder.Services.AddTurboMediator(m =>
{
    m.WithFallback<GetProductQuery, Product>(options =>
    {
        options.ThrowOnAllFallbacksFailure = true;
        options.OnFallbackInvoked = (info) =>
        {
            Console.WriteLine($"Fallback {info.FallbackHandlerType.Name} invoked for {info.MessageType} (attempt {info.AttemptNumber})");
        };
    });
});

FallbackOptions

OptionDefaultDescription
ExceptionTypesAllException types that trigger fallback
ShouldHandlenullCustom predicate for when to use fallback
ThrowOnAllFallbacksFailuretrueThrow if all fallbacks fail
OnFallbackInvokednullCallback when a fallback is used
DefaultValueFactorynullFactory for default value if all else fails

Exception-Specific Fallbacks

[Fallback(typeof(NetworkFallback), OnExceptionTypes = new[] { typeof(HttpRequestException) })]
[Fallback(typeof(TimeoutFallback), OnExceptionTypes = new[] { typeof(TimeoutException) })]
[Fallback(typeof(GenericFallback), Order = 99)]
public record GetDataQuery(string Key) : IQuery<Data>;

Fallbacks are tried in Order sequence. The first fallback that returns successfully wins. If a fallback also throws, the next fallback in the chain is tried.

On this page