DEV Community

Cover image for De MediatR para Mediator: uma migração mais leve e performática

De MediatR para Mediator: uma migração mais leve e performática

*Dando continuidade à série sobre bibliotecas .NET que deixaram de ser open source e gratuitas, neste artigo apresento uma alternativa ao MediatR e explico por que ela pode representar uma migração mais leve em termos de estrutura e performance.

Desde que foi anunciado que o MediatR passará a adotar um modelo de licenciamento pago — mesmo que isso ainda leve algum tempo e a versão atual permaneça gratuita — nós, desenvolvedores, começamos a buscar alternativas próximas do que já conhecemos ou, em alguns casos, optamos por implementações próprias, como abordado no artigo anterior sobre o AutoMapper.

Tendo isso em mente, encontrei uma alternativa — especialmente adequada para projetos que precisam ser migrados e para equipes já familiarizadas com o MediatR — que é o Mediator (com “o”). Assim como o anterior, ele implementa o padrão mediator e preserva as mesmas abstrações e recursos, já que foi desenvolvido com base no próprio MediatR.

Isso permite que atualizações em sistemas existentes ocorram de forma muito mais tranquila e rápida, além de facilitar sua adoção em novos contextos, já que a estrutura de implementação praticamente não se altera em relação ao que já estamos habituados a utilizar.

Um ponto especialmente relevante — e que me chamou bastante a atenção — é o foco do Mediator em performance. Isso se deve ao fato de sua implementação utilizar Source Generators, um recurso introduzido no C# 9, em que, diferentemente do MediatR, que se baseia em reflexão para registrar e resolver seus handlers, é gerado código durante o tempo de compilação.

Isso não significa que a abordagem do MediatR seja ruim — muito pelo contrário. A reflexão utilizada por ele é altamente otimizada. No entanto, código gerado e compilado diretamente tende a oferecer desempenho consideravelmente superior em relação a soluções baseadas em reflexão.

Os benchmarks disponíveis na documentação do Mediator evidenciam um ganho significativo de performance em relação ao MediatR.

Mediator Benchmark

Migrando do MediatR para o Mediator

A seguir, mostro um exemplo real de migração de um projeto baseado em MediatR para Mediator, destacando o que foi alterado.

1) Removendo o MediatR

Desinstale todas as referências ao pacote MediatR com o seguinte comando:

dotnet remove package MediatR
Enter fullscreen mode Exit fullscreen mode

No projeto de exemplo, esse processo foi realizado em três Class Libraries.

Uninstall MediatR

2) Instalando o Mediator

Adicione os pacotes do Mediator em substituição:

dotnet add package Mediator.SourceGenerator --version 3.0.*-*
dotnet add package Mediator.Abstractions --version 3.0.*-*
Enter fullscreen mode Exit fullscreen mode

3) Ajustando os serviços

Altere a classe de configuração de dependências:

// Anterior
// using MediatR;

// Novo
using Mediator;

// Anterior
// services.AddMediator(c => c.RegisterServicesFromAssembly(typeof(Course).GetTypeInfo().Assembly));

// Novo
services.AddMediator((MediatorOptions options) => options.Assemblies = [typeof(Course)]);
Enter fullscreen mode Exit fullscreen mode

4) Atualizando comandos, eventos e handlers

Command:

// Anterior
// using MediatR;

// Novo
using Mediator;

public abstract class CourseCommand : IRequest
{
    // Propriedades e construtor inalterados
}
Enter fullscreen mode Exit fullscreen mode

Event:

// Anterior
// using MediatR;

// Novo
using Mediator;

public sealed class RegisteredCourseEvent : INotification
{
    // Propriedades, construtor e factory method inalterados
}

Enter fullscreen mode Exit fullscreen mode

CommandHandler:

//Anterior
// using MediatR;

//Novo
using Mediator;

public sealed class CourseCommandHandler :
    IRequestHandler<RegisterCourseCommand>,
    IRequestHandler<UpdateCourseCommand>,
    IRequestHandler<RemoveCourseCommand>
{
    // Atributos e construtor inalterados

    //Anterior
    // public async Task<Unit> Handle(RegisterCourseCommand request, CancellationToken cancellationToken)

    // Novo
    public async ValueTask<Unit> Handle(RegisterCourseCommand request, CancellationToken cancellationToken)
    {
        // Lógica de negócio inalterada

        // Anterior
        // return await Unit.Task;

        // Novo
        return await Unit.ValueTask;
    }

   // Anterior
   // public async Task<Unit> Handle(UpdateCourseCommand request, CancellationToken cancellationToken)

    // Novo
    public async ValueTask<Unit> Handle(UpdateCourseCommand request, CancellationToken cancellationToken)
    {
        // Lógica de negócio inalterada

        // Anterior
        // return await Unit.Task;

        // Novo
        return await Unit.ValueTask;
    }

    // Anterior
    // public async Task<Unit> Handle(RemoveCourseCommand request, CancellationToken cancellationToken)

    // Novo
    public async ValueTask<Unit> Handle(RemoveCourseCommand request, CancellationToken cancellationToken)
    {
        // Lógica de negócio inalterada

        // Anterior
        // return await Unit.Task;

        // Novo
        return await Unit.ValueTask;
    }
}
Enter fullscreen mode Exit fullscreen mode

EventHandler:

// Anterior
// using MediatR;

// Novo
using Mediator;

public sealed class CourseEventHandler : INotificationHandler<RegisteredCourseEvent>
{
    // Anterior
    // public async Task Handle(RegisteredCourseEvent notification, CancellationToken cancellationToken)

    // Novo
    public async ValueTask Handle(RegisteredCourseEvent notification, CancellationToken cancellationToken)
    {
        // Exemplo para envio de e-mail quando o cliente se registrar em um curso
        //notification.Emails.ForEach(e => SendMail(e));

        // Anterior
        // await Task.CompletedTask;

        // Novo
        await ValueTask.CompletedTask;
    }
}
Enter fullscreen mode Exit fullscreen mode

CourseAppService


// Anterior
// using MediatR;

// Novo
using Mediator;

public sealed class CourseAppService : ICourseAppService
{
    // Atributos, construtor e métodos inalterados
}
Enter fullscreen mode Exit fullscreen mode

⚠️ Observação: Note que nos Handlers, o retorno dos métodos Handle mudou de Task para ValueTask.

Neste post, não vamos entrar em detalhes sobre o funcionamento do ValueTask, mas, de forma resumida, trata-se de uma struct que pode evitar alocação de memória heap, reduzindo a pressão sobre o Garbage Collector e sendo mais eficiente em cenários onde a operação assíncrona é concluída rapidamente.

Você pode ver as alterações e o projeto de exemplo completo no GitHub.

Conclusão

O Mediator se mostra uma alternativa viável, leve e altamente compatível com a abordagem que o MediatR já entregava.

Sua estrutura familiar, aliada ao uso de Source Generators, torna a migração simples e vantajosa em termos de performance e manutenção. Para quem está reavaliando o futuro da stack de desenvolvimento .NET, vale a pena considerar o Mediator.

E você, já migrou ou ainda está avaliando como fazer a transição? Compartilhe sua experiência nos comentários.

Até o próximo artigo!

Precisa de apoio para decidir o futuro dos seus projetos — manter, evoluir ou começar do zero? Fale com a gente!
Nosso time de especialistas está pronto para ajudar você a enfrentar esses e outros desafios tecnológicos.

Referências

Top comments (0)