Compliance · CQRS · Mapperly
Getting off MediatR and AutoMapper, in production
MediatR and AutoMapper go dual-license. 100+ handlers to migrate without pausing delivery.
- 100+
- handlers migrated
- 9
- mapping profiles
- 0
- delivery pauses
The problem
MediatR and AutoMapper announce a move to dual-licensing. Over 100 CQRS handlers and 9 mapping profiles depend on them, on a live platform, with no maintenance window available to rewrite everything at once.
The approach
The migration ran handler by handler behind the existing abstraction: a custom OSS dispatcher replaced MediatR without changing handler signatures, and Mapperly replaced AutoMapper — its mappers are generated at compile time instead of resolved by reflection at runtime.
// MediatR moved to a dual licence in 2025. Rather than accept the risk or
// pay for 100+ handlers we already owned, the dispatch surface was replaced
// with the smallest thing that satisfied our actual usage: send, and nothing
// else. No pipelines, no notifications, no reflection at runtime.
public interface IRequestHandler<in TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
Task<TResponse> HandleAsync(TRequest request, CancellationToken ct);
}
public sealed class Dispatcher(IServiceProvider provider) : IDispatcher
{
public Task<TResponse> SendAsync<TResponse>(
IRequest<TResponse> request, CancellationToken ct = default)
{
// Handlers are resolved by closed generic type, so a missing
// registration fails at startup rather than at first request.
var handlerType = typeof(IRequestHandler<,>)
.MakeGenericType(request.GetType(), typeof(TResponse));
dynamic handler = provider.GetRequiredService(handlerType);
return handler.HandleAsync((dynamic)request, ct);
}
}The result
100+ handlers and 9 mapping profiles migrated without pausing delivery or introducing a functional regression, plus a measurable mapping speed-up: compile-time generation removes AutoMapper's reflection cost.