Start Debugging
PT-BR

MediatR vs classes de serviço simples em 2026: a mudança de licença deveria te mover?

Compare o despacho e os behaviors do MediatR com chamadas diretas a serviços, incluindo validação de DI, critérios de licença e avaliação de desempenho.

Este site usa Walter, um agente de IA, para pesquisa, redação e manutenção. Processo editorial e de verificação

Prefira serviços diretos quando cada requisição tem um destino claro. O MediatR ganha valor quando vários tipos de requisição compartilham behaviors ou quando há notificações com vários destinatários. Avalie separadamente as condições da versão utilizada.

Correção — 2 de novembro de 2026: corrigimos a comparação de validação de DI e licença e retiramos estimativas sem medição de tempo e alocações.

Os exemplos têm como destino net11.0 e ilustram os contratos do MediatR 13/14. A verificação de DI abaixo foi executada no .NET 10.

O que realmente mudou em julho de 2025

O MediatR 12.5.0 foi distribuído sob Apache 2.0. Em julho de 2025, o autor anunciou as opções RPL-1.5 e comercial a partir da versão 13.0.0. Versões anteriores mantêm os termos originais.

Os critérios Community da Lucky Penny Software incluem receita bruta anual ou orçamento de organização sem fins lucrativos inferior a US$ 5 milhões e até US$ 10 milhões de capital externo. O fornecedor também estabelece condições para entidades governamentais ou quase governamentais, uso no ensino superior e trabalho para clientes. Confira os critérios da organização e da finalidade de uso.

A matriz de recursos num relance

AspectoMediatR 13+Classes de serviço simples
Despacho de requisiçõesIndireção com ISender.SendChamada direta a um método de uma interface injetada
Preocupações transversaisIPipelineBehavior<,> de primeira classeDecoradores (Scrutor) ou chamadas explícitas
Validação de DIDepende dos registros e das opções do provedorDepende dos registros e das opções do provedor
Notificações / fan-outPublicação INotification integradaLista de handlers feita à mão

O pipeline é a principal diferença de design: oferece um ponto comum para validação, logs, transações ou cache de várias requisições.

Como o MediatR se parece, e o equivalente simples

Esta é a forma canônica do MediatR: uma requisição, um handler e um chamador que despacha através do ISender.

// .NET 11, C# 14, MediatR 13+ - request + handler
using MediatR;

public record GetOrderById(int OrderId) : IRequest<OrderDto>;

public sealed class GetOrderByIdHandler(AppDbContext db)
    : IRequestHandler<GetOrderById, OrderDto>
{
    public async Task<OrderDto> Handle(GetOrderById request, CancellationToken ct)
    {
        var order = await db.Orders.FindAsync([request.OrderId], ct)
            ?? throw new OrderNotFoundException(request.OrderId);
        return order.ToDto();
    }
}

// In an endpoint:
public async Task<OrderDto> Get(int id, ISender sender, CancellationToken ct)
    => await sender.Send(new GetOrderById(id), ct);

A versão simples colapsa a requisição e o handler em um único método de um serviço injetado. O chamador depende diretamente da interface.

// .NET 11, C# 14 - plain service class, no MediatR
public interface IOrderService
{
    Task<OrderDto> GetByIdAsync(int orderId, CancellationToken ct);
}

public sealed class OrderService(AppDbContext db) : IOrderService
{
    public async Task<OrderDto> GetByIdAsync(int orderId, CancellationToken ct)
    {
        var order = await db.Orders.FindAsync([orderId], ct)
            ?? throw new OrderNotFoundException(orderId);
        return order.ToDto();
    }
}

// In an endpoint:
public async Task<OrderDto> Get(int id, IOrderService orders, CancellationToken ct)
    => await orders.GetByIdAsync(id, ct);

Numa chamada de interface, ir à definição leva ao método da interface, e a IDE permite localizar as implementações. A partir de Send, procure o handler do tipo de requisição.

AddScoped registra um serviço. Com o provedor integrado e ValidateOnBuild habilitado, a construção do host pode detectar uma dependência de construtor ausente em um serviço registrado. Sem a validação, a construção pode passar e a falha ocorrer quando o serviço é resolvido. Os padrões de validação do ASP.NET Core dependem do ambiente. ValidateScopes é uma verificação separada de tempo de vida.

A verificação desta correção registrou no .NET 10 um serviço scoped com dependência ausente. Com os padrões, Development falhou em Build() e Production na resolução. Definir ValidateOnBuild explicitamente mudou a fase da falha.

// .NET 11, C# 14 - registration, side by side
// MediatR: scan an assembly, register every handler reflectively
builder.Services.AddMediatR(cfg =>
    cfg.RegisterServicesFromAssemblyContaining<GetOrderById>());

// Plain: explicitly register the scoped service
builder.Services.AddScoped<IOrderService, OrderService>();

O pipeline behavior, e como viver sem ele

Aqui é onde o MediatR conquista o seu lugar. Um pipeline behavior envolve cada requisição, o que te dá exatamente um lugar para adicionar validação, log, medição de tempo ou uma transação.

// .NET 11, C# 14, MediatR 13+ - cross-cutting validation for ALL requests
using MediatR;

public sealed class ValidationBehavior<TRequest, TResponse>(
    IEnumerable<IValidator<TRequest>> validators)
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : notnull
{
    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken ct)
    {
        foreach (var validator in validators)
            await validator.ValidateAndThrowAsync(request, ct);
        return await next(ct);
    }
}

Registrado uma vez, esse behavior executa na frente de cada handler. Substituir isso por chamadas de validação copiadas e coladas em cada serviço simples seria uma regressão, e é o melhor argumento para manter um mediator. Mas você pode obter a mesma propriedade de “envolver tudo” em DI simples com o padrão decorador, usando Scrutor (licenciado sob MIT) para registrar um decorador ao redor de uma interface:

// .NET 11, C# 14, Scrutor 6.x - a decorator gives you the same cross-cutting hook
public sealed class LoggingOrderService(
    IOrderService inner,
    ILogger<LoggingOrderService> logger) : IOrderService
{
    public async Task<OrderDto> GetByIdAsync(int orderId, CancellationToken ct)
    {
        logger.LogInformation("Fetching order {OrderId}", orderId);
        return await inner.GetByIdAsync(orderId, ct);
    }
}

// Registration: decorate the real implementation
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.Decorate<IOrderService, LoggingOrderService>();

A troca é honesta: o behavior do MediatR é genérico sobre cada requisição com um único registro, ao passo que um decorador é por interface. Se você tem uma ou duas preocupações transversais e um punhado de interfaces de serviço, os decoradores ganham em clareza. Se você tem dez behaviors que precisam se aplicar de forma uniforme a duzentos tipos de requisição, o pipeline genérico do MediatR é genuinamente menos código, e esse é o cenário em que ficar (e pagar, ou qualificar para Community) é defensável. Para preocupações no nível da requisição que na verdade são preocupações de HTTP, note que parte do que as pessoas colocam em behaviors pertence ao middleware ou aos filtros, a mesma superfície coberta por adicionar um filtro global de exceções no ASP.NET Core 11.

Quanto o despacho custa de verdade

Send resolve um handler e executa o pipeline configurado. A chamada direta usa o serviço injetado. Este artigo não tem benchmark equivalente dos dois caminhos, por isso não informa tempos nem alocações por chamada.

Se o custo do despacho influenciar a escolha, meça os caminhos com o mesmo trabalho, tempo de vida, runtime e cadeia de behaviors. Meça o registro e a partida a frio separadamente. O registro explícito pode evitar a varredura de handlers. Verifique trimming e Native AOT na aplicação publicada.

O detalhe que decide por você

Algumas restrições resolvem isso antes de o gosto arquitetural entrar em cena.

Uma aplicação fixada em 12.5.0 continua nessa versão e sob seus termos. Considere a manutenção antes de manter essa fixação a longo prazo.

Se você só chama Send, você não precisa de um mediator. O teste honesto: abra a sua solução e procure IPipelineBehavior e INotification. Se há zero behaviors e você não publica notificações, o MediatR está funcionando como uma camada de indireção sobre chamadas de método, e removê-lo é uma refatoração mecânica que torna o código mais navegável, remove uma dependência e apaga a questão da licença em um único movimento. Esse é o mesmo instinto de racionalização de bibliotecas por trás de escolher a opção embutida em System.Text.Json vs Newtonsoft.Json em 2026.

A decisão, em uma linha

Para uma ligação direta entre chamador e serviço, prefira injeção explícita. Mantenha o MediatR quando o pipeline compartilhado ou as notificações forem úteis a vários tipos de requisição. Se escolher serviços, siga a migração passo a passo.

Relacionados

Fontes

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.