Skip to content

A Free and Open-Source Mediator Pattern Implementation for .NET

License

Notifications You must be signed in to change notification settings

angelobelchior/TheMediator

Repository files navigation

TheMediator

drawing

.NET

NuGet

TheMediator é uma implementação de alto desempenho do padrão Mediator para .NET, com suporte a Filters e Notifications. É leve, extensível e rápido, ideal para desacoplar componentes em sistemas.

Esse projeto faz parte da saga "Reinventando a Roda: Criando seu próprio MediatR", onde eu crio uma implementação própria.

Foram 3 posts no total:

Instalação

Você pode instalar o pacote via NuGet usando o seguinte comando:

dotnet add package TheMediator

Feito isso, você pode registrar o Mediator no seu projeto:

builder.Services.AddTheMediator(configuration =>
{
    configuration.AddServicesFromAssemblies(typeof(Program).Assembly);
    
    configuration.Filters.Add<LoggingFilter>();
    
    configuration.Notifiers.Configurations.DeliveryMode = NotificationDeliveryMode.FireAndForget;
}); 

Funcionalidades

Handlers

Os Handlers são responsáveis por processar as requisições.

Eles implementam a interface IRequestHandler<TRequest, TResponse>, onde TRequest é o tipo da requisição e TResponse é o tipo da resposta.

TResponse não é obrigatório, e o TRequest pode ser qualquer tipo, incluindo string, int, Guid, etc.

É possível ter mais de um Handler para o mesmo tipo de requisição (TRequest) contanto que o TResponse seja diferente.

Os Handlers são registrados automaticamente pelo método AddServicesFromAssemblies no AddTheMediator.

Exemplo de uso:

public record ProductRequest(string Name, decimal Price);

public record ProductResponse(Guid Id, string Name, decimal Price);

public class CreateProductRequestHandler(IProductsRepository repository)
    : IRequestHandler<ProductRequest, ProductResponse>
{
    public async Task<ProductResponse> HandleAsync(ProductRequest request, CancellationToken cancellationToken)
    {
        var created = await repository.Create(request, cancellationToken);
        return created;
    }
}

public class SearchProductRequestHandler(IProductsRepository repository)
    : IRequestHandler<string, IReadOnlyCollection<ProductResponse>?>
{
    public Task<IReadOnlyCollection<ProductResponse>?> HandleAsync(
        string? query,
        CancellationToken cancellationToken)
    {
        return repository.Search(query, cancellationToken);
    }
}

// Exemplo de uso

app.MapPost("/products",
        async (
            [FromServices] ISender sender,
            [FromBody] ProductRequest request,
            CancellationToken cancellationToken) =>
        {
            var response = await sender.SendAsync<ProductRequest, ProductResponse?>(request, cancellationToken);
            return response is null
                ? Results.BadRequest("Cannot create product")
                : Results.Created($"/products/{response.Id}", response);
        })
    .WithName("Products.Post.ProductRequest");

Filters

Os Filters permitem adicionar lógica antes ou depois da execução de um handler, como validações, logging ou manipulação de exceções.

Exemplo de uso:

public class LoggerRequestFilter(ILogger<LoggerRequestFilter> logger)
    : IRequestFilter
{
    public Task<TResponse> FilterAsync<TRequest, TResponse>(TRequest request, Func<Task<TResponse>> next,
        CancellationToken cancellationToken)
        where TRequest : notnull
    {
        logger.LogInformation("{Type} execution started: {Time}", request.GetType().Name, DateTime.Now);
        var response = next();
        logger.LogInformation("{Type} execution finished: {Time}", request.GetType().Name, DateTime.Now);
        return response;
    }
}

Os Filters devem ser registrados manualmente no AddTheMediator e a ordem de execução é a ordem em que foram registrados.

Notifications

As Notifications permitem enviar mensagens para múltiplos handlers de forma assíncrona (quando o delivery mode é FireAndForget) ou síncrona (quando o delivery mode é WaitForAll).

O DeliveryMode padrão é FireAndForget, e ele pode ser configurado no AddTheMediator.

As Notifications são registrados automaticamente pelo método AddServicesFromAssemblies no AddTheMediator.

Exemplo de uso:

public class LogInfoProductCreatedNotification(ILogger<LogInfoProductCreatedNotification> logger)
    : INotificationHandler<ProductResponse>
{
    public async Task HandleAsync(ProductResponse notification, CancellationToken cancellationToken)
    {
        logger.LogInformation("{Class} Product {ProductName} created", nameof(CreateProductNotification),
            notification.Name);
    }
}

public class SendEmailProductCreatedNotification(ILogger<SendEmailProductCreatedNotification> logger, IEmailService emailService)
    : INotificationHandler<ProductResponse>
{
    public async Task HandleAsync(ProductResponse notification, CancellationToken cancellationToken)
    {
        await emailService.SendAsync($"Product {notification.Name} created...", notification.Dump(), cancellationToken);
    }
}

// Exemplo de uso

public class CreateProductRequestHandler(IPublisher publisher, IProductsRepository repository)
    : IRequestHandler<ProductRequest, ProductResponse>
{
    public async Task<ProductResponse> HandleAsync(ProductRequest request, CancellationToken cancellationToken)
    {
        var created = await repository.Create(request, cancellationToken);
        await publisher.PublishAsync(created, cancellationToken);
        return created;
    }
}

Logging

O TheMediator utiliza o ILogger para registrar informações sobre as requisições e respostas via método Trace.

Por default, essa opção vem desabilitada.

Para mudar essa configuração é necessário mudar o level do logger no appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "TheMediator.Core": "Trace"
    }
  }
}

About

A Free and Open-Source Mediator Pattern Implementation for .NET

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages