DSoftStudio Mediator

← Back to Documentation

ADR-0003: Fail-fast Handler Validation at Startup

Status

Released in v1.0.7

Context

DI-based mediators (including DSoftStudio.Mediator) can fail to resolve handlers at runtime if a handler or its dependencies throw during construction. By default, this failure is only detected when the handler is first resolved (e.g., on the first request), which can make misconfiguration hard to diagnose and affect unrelated requests. A fail-fast pattern is desired: all handlers should be validated at startup, and the app should fail immediately if any handler cannot be constructed.

Decision

DSoftStudio.Mediator provides fail-fast handler validation at startup via a source-generated ValidateMediatorHandlers() extension method on IServiceProvider. This approach was chosen over IHostedService to avoid adding a dependency on Microsoft.Extensions.Hosting.Abstractions to the core package.

Implementation

Architecture

The DependencyInjectionGenerator (Roslyn incremental source generator) emits two additional types alongside the existing MediatorServiceRegistry:

Generated Type Purpose
MediatorHandlerValidator Static class with Validate(IServiceProvider) — resolves every registered handler from DI
MediatorHandlerValidatorExtensions Extension method ValidateMediatorHandlers(this IServiceProvider)

Validation Strategy

For each handler type discovered at compile time:

Handler Category Validation Method What it validates
IRequestHandler<TReq, TRes> GetRequiredService<T>() Handler + all constructor dependencies
PipelineChainHandler<TReq, TRes> GetService<T>() (nullable) Pipeline chain + all behaviors, processors, exception handlers
INotificationHandler<TNotif> GetServices<T>() + enumerate All handler implementations for each notification type
IStreamRequestHandler<TReq, TRes> GetRequiredService<T>() Stream handler + dependencies
StreamPipelineChainHandler<TReq, TRes> GetService<T>() (nullable) Stream pipeline chain if registered

Error Aggregation

All failures are collected into a List<Exception> and thrown as a single AggregateException after all handlers have been validated. This reports all misconfigured handlers at once, not just the first failure.

Usage

// ASP.NET Core
var app = builder.Build();
app.Services.ValidateMediatorHandlers(); // Throws AggregateException on failure
app.Run();

// Console / test
var provider = services.BuildServiceProvider();
provider.ValidateMediatorHandlers();

Design Properties

Consequences

Test Results

Test Result
Happy path (all handlers resolve) ✅ Pass
Missing handlers → AggregateException ✅ Pass
Aggregate contains all failures ✅ Pass
Performance regression (allocation) ✅ No impact — Send=72B, Publish=0B
Performance regression (throughput) ✅ No impact
Full test suite (174 tests) ✅ 0 regressions

Document History

Date Version Changes
Draft Initial ADR with implementation plan
2026-03-15 v1.0.7 Released with source-generated startup validation