Blog/Marten / Wolverine

MediatR vs. Wolverine: Architecture, Ergonomics, and Feature Matrix

Ondřej Podhorný

Ondřej Podhorný

2026-09-25 · 8 min read

MediatR taught .NET teams to keep HTTP away from business logic. Wolverine asks whether that boundary still needs a marker interface.

For years, MediatR has served as the default standard for implementing CQRS and in-process decoupling in .NET applications. It introduced clean boundary separation between HTTP controllers and business logic, providing an accessible, predictable structure for monolithic codebases.

As distributed requirements, event-driven architectures, and performance constraints have matured, the limitations of simple in-memory mediator patterns have become clearer. Wolverine, part of the Critter Stack ecosystem, approaches this challenge from a fundamentally different perspective: replacing interface ceremony with code generation and integrating messaging capabilities directly into the framework core.

This article breaks down the architectural, ergonomic, and operational differences between MediatR and Wolverine to evaluate when each tool makes sense.

Ergonomics: interface ceremony vs. pure POCOs

The structural difference is evident in handler and message definitions. MediatR enforces strict generic interfaces, whereas Wolverine relies on conventions and dynamic code generation to eliminate boilerplate.

MediatR: marker interfaces and rigid signatures

In MediatR, every command, query, and handler must implement specific interfaces:

CreateOrderHandler.cscsharp
// 1. Command requires marker interface
public record CreateOrder(string CustomerId, decimal Amount)
    : IRequest<OrderResult>;

// 2. Handler ceremony and constructor injection boilerplate
public class CreateOrderHandler : IRequestHandler<CreateOrder, OrderResult>
{
    private readonly ILogger<CreateOrderHandler> _logger;

    public CreateOrderHandler(ILogger<CreateOrderHandler> logger)
    {
        _logger = logger;
    }

    public Task<OrderResult> Handle(CreateOrder request, CancellationToken ct)
    {
        _logger.LogInformation("Processing order for {CustomerId}...", request.CustomerId);
        return Task.FromResult(new OrderResult(Guid.NewGuid()));
    }
}

This pattern introduces several architectural constraints:

  • Interface coupling. Commands and handlers are coupled to IRequest<T> and IRequestHandler<TRequest, TResponse>.
  • Constructor clutter. Dependencies require explicit constructor injection and private field assignments.
  • Forced asynchrony. Handlers must return Task<T> and accept a CancellationToken, even when handling pure in-memory, synchronous computations. This forces artificial state machines and allocations via Task.FromResult.

Wolverine: convention over ceremony

Wolverine strips away external interface constraints entirely:

CreateOrderHandler.cscsharp
// 1. Clean, zero-interface C# POCO
public record CreateOrder(string CustomerId, decimal Amount);

// 2. Static class and direct method injection (no constructor DI)
public static class CreateOrderHandler
{
    public static OrderResult Handle(CreateOrder cmd, ILogger logger)
    {
        logger.LogInformation("Processing order for {CustomerId}...", cmd.CustomerId);
        return new OrderResult(Guid.NewGuid());
    }
}

Key ergonomic differences:

  • Zero-interface messaging. Messages remain pure POCOs without framework references.
  • Method-level dependency injection. Handlers can be declared as static classes, with dependencies passed directly into the Handle method parameters alongside the command.
  • Flexible signatures. If an operation is synchronous, return the raw type directly. If a CancellationToken is needed, declare it; if not, omit it.

Comprehensive feature matrix

While MediatR focuses solely on in-process dispatching, Wolverine acts as both an in-memory mediator and an integration bus.

FeatureMediatRWolverine
Scope and complexityMinimal in-process mediatorFull messaging and integration bus
Team onboardingDe-facto industry standardEmerging, convention-heavy
Coding styleIRequest and handlersZero-interface POCO
HTTP endpointsMinimal API or MVC onlyWolverine.Http, no MVC
Cascading messagesExplicit dispatch callsNative return types
Transactional outbox and inboxExternal librariesEF Core, PostgreSQL, SQL Server, Marten
Async brokersIn-process onlyRabbitMQ, Kafka, Azure Service Bus, SQS
Retry and dead letterBuilt by hand in behaviorsPer exception, plus circuit breakers
SagasNot supportedFirst-class stateful sagas
OpenTelemetryCustom pipelineBuilt in
Native AOT and cold startZero overhead out of the boxNeeds code generation
In-memory dispatchDirect delegate invokeFull message envelope

Architectural scope: beyond in-process dispatch

MediatR stops at the process boundary. When requirements involve asynchronous background processing, external message queues, or reliable transaction boundaries, developers must build or integrate secondary frameworks (such as MassTransit or custom outbox implementations).

Wolverine bridges this gap by unifying local invocation and external messaging into a single abstraction model:

  • Cascading messages via return types. Handlers can return tuples, for example (OrderResult Result, OrderPlaced Event). Wolverine automatically sends the event downstream to message queues or outbox storage while returning the result to the direct caller, avoiding manual dispatcher calls.
  • Native transactional outbox. Ensures consistency between database writes and message broker dispatching natively, without third-party plugins.
  • Resilience and error handling. Wolverine provides declarative retry policies, fallback logic, and dead-letter queues configured per message or exception type.

Operational trade-offs

Choosing between these frameworks requires weighing specific architectural compromises:

  • Conventions vs. explicit types. MediatR’s explicit interface hierarchies make code navigation straightforward via standard IDE tools. Wolverine’s convention-based discovery requires developers to understand its method naming and parameter binding rules.
  • Startup performance and AOT. MediatR relies on standard DI service registration. Wolverine uses Lamar and runtime Roslyn compilation by default to maximize throughput. For environments requiring minimal cold-start latencies or Native AOT, teams must incorporate ahead-of-time code pre-generation into their build pipelines.
  • Adoption and maintenance. MediatR has an established track record and extensive documentation across the enterprise ecosystem. Wolverine offers modern ergonomics and broader built-in capabilities, but onboarding requires familiarity with convention-driven patterns.

Which should you choose?

Choose MediatR if your application is a modular monolith requiring strict in-memory command dispatching, explicit interface-driven navigation, and zero additional infrastructure dependencies.

Choose Wolverine if your application needs an integrated messaging pipeline with transactional outbox support, message broker integration, minimal ceremony, and declarative resilience built directly into the runtime.