Tooark.Mediator 4.0.1

dotnet add package Tooark.Mediator --version 4.0.1
                    
NuGet\Install-Package Tooark.Mediator -Version 4.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Tooark.Mediator" Version="4.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tooark.Mediator" Version="4.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Tooark.Mediator" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Tooark.Mediator --version 4.0.1
                    
#r "nuget: Tooark.Mediator, 4.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Tooark.Mediator@4.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Tooark.Mediator&version=4.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Tooark.Mediator&version=4.0.1
                    
Install as a Cake Tool

Tooark.Mediator

Biblioteca com implementação de Mediator para projetos .NET, focada em CQRS/CQS com baixo acoplamento entre camadas.

Conteúdo

Visão Geral

O pacote Tooark.Mediator fornece:

  • implementação concreta de IMediator;
  • registro automático de handlers por assembly;
  • pipeline de behaviors para preocupações transversais;
  • estratégia configurável de publicação de notificações;
  • integração com DI do Microsoft.Extensions.DependencyInjection.

🔧 Instalação

dotnet add package Tooark.Mediator

⚙️ Configuração

using Tooark.Mediator.Injections;

builder.Services.AddTooarkMediator(typeof(Program).Assembly);

Também é possível configurar as opções do mediador:

using Tooark.Mediator.Enums;
using Tooark.Mediator.Injections;

builder.Services.AddTooarkMediator(options =>
{
  options.NotifyPublishStrategy = ENotifyStrategy.Sequential;
}, typeof(Program).Assembly);

Recomendação: informe sempre os assemblies explicitamente (ex: typeof(Program).Assembly). Sem assemblies, o assembly chamador é escaneado como fallback — funciona para o caso comum, mas a forma explícita é imune a refatorações que movam a chamada para outro projeto. Classes genéricas abertas são ignoradas pelo scan (o dispatch não suporta open generics).


📦 Componentes

Classe principal

  • Mediator: implementação de IMediator.

Injeção de dependência

  • TooarkDependencyInjection.AddTooarkMediator(IServiceCollection, params Assembly[])
  • TooarkDependencyInjection.AddTooarkMediator(IServiceCollection, Action<MediatorOptions>, params Assembly[])
  • TooarkDependencyInjection.AddTooarkMediatorBehavior<TBehavior>(IServiceCollection)
  • TooarkDependencyInjection.AddTooarkMediatorBehavior(IServiceCollection, Type)

Opções

  • MediatorOptions
    • NotifyPublishStrategy (padrão: ENotifyStrategy.ParallelWhenAll)
    • Registrado via padrão Options: chamadas múltiplas de AddTooarkMediator compõem as configurações em ordem de registro
    • O Mediator recebe IOptions<MediatorOptions>. Para configurar fora do AddTooarkMediator, use services.Configure<MediatorOptions>(...) — registrar MediatorOptions diretamente no container não tem efeito

Estratégias de publicação

  • ENotifyStrategy.ParallelWhenAll (padrão): inicia todos os handlers e aguarda a conclusão de todos com Task.WhenAll. Melhor latência quando os handlers são independentes. Todos os handlers são iniciados mesmo que algum falhe ao iniciar, e a primeira falha é propagada ao chamador.
  • ENotifyStrategy.Sequential: inicia cada handler somente após o anterior concluir, na ordem de registro. Se um handler falhar, os seguintes não são executados (fail-fast). Use quando a ordem dos efeitos colaterais importa ou os handlers compartilham recursos não thread-safe.

Pipeline de behaviors

IPipelineBehavior<TRequest, TResponse> envolve a execução do handler, permitindo tratar preocupações transversais — validação, log, transação, cache, autorização — sem repeti-las em cada handler. Cada behavior decide se invoca a etapa seguinte: não invocar interrompe o pipeline (curto-circuito) e a resposta do próprio behavior é devolvida ao chamador.

Os behaviors se aplicam apenas a requisições; notificações não passam pelo pipeline. O handler é resolvido antes da montagem da cadeia, então Handler.NotFound continua falhando antes de qualquer behavior executar. Sem behaviors registrados, o despacho permanece sendo a invocação direta do handler.

O CancellationToken é parâmetro obrigatório da etapa seguinte — repasse o token recebido para propagar o cancelamento, ou informe um token encadeado para aplicar um limite próprio.

Um behavior genérico aberto pode restringir a quais requisições se aplica pela restrição de tipo. Um behavior declarado com where TRequest : ICommand<TResponse> envolve apenas comandos, e o container o ignora ao despachar consultas — sem necessidade de verificação de tipo em tempo de execução. O mesmo vale para restrições sobre interfaces próprias da aplicação.

Validação no registro

Uma requisição é processada por um único handler. Se o scan ou o registro manual resultar em mais de um handler para a mesma requisição, AddTooarkMediator lança InternalServerErrorException com o código Handler.Duplicated, identificando a requisição e os handlers em conflito. Notificações continuam aceitando quantos handlers forem registrados.

Desempenho do dispatch

O despacho usa wrappers genéricos em cache estático: reflection ocorre apenas na primeira chamada de cada tipo de mensagem. As exceções lançadas pelos handlers chegam ao chamador diretamente (sem TargetInvocationException).

Handlers suportados

  • IRequestHandler<TRequest, TResponse>
  • ICommandHandler<TCommand, TResponse>
  • ICommandHandler<TCommand>
  • IQueryHandler<TQuery, TResponse>
  • INotifyHandler<TNotify>

Behaviors suportados

  • IPipelineBehavior<TRequest, TResponse>

Os handlers ficam no namespace Tooark.Mediator.Handlers e os behaviors em Tooark.Mediator.Behaviors.


📝 Exemplos de Uso

Exemplo CQRS

using Tooark.Mediator.Abstractions;
using Tooark.Mediator.Handlers;

public sealed record CreateUserCommand(string Name) : ICommand<Guid>;

public sealed class CreateUserCommandHandler : ICommandHandler<CreateUserCommand, Guid>
{
  public Task<Guid> HandleAsync(CreateUserCommand request, CancellationToken cancellationToken = default)
  {
    return Task.FromResult(Guid.NewGuid());
  }
}

public sealed record GetUserByIdQuery(Guid Id) : IQuery<string>;

public sealed class GetUserByIdQueryHandler : IQueryHandler<GetUserByIdQuery, string>
{
  public Task<string> HandleAsync(GetUserByIdQuery request, CancellationToken cancellationToken = default)
  {
    return Task.FromResult($"Usuário {request.Id}");
  }
}

Exemplo de uso com IMediator

using Tooark.Mediator.Abstractions;

public sealed class UsersService(IMediator mediator)
{
  public Task<Guid> CreateAsync(string name, CancellationToken cancellationToken)
  {
    return mediator.SendAsync(new CreateUserCommand(name), cancellationToken);
  }

  public Task<string> GetAsync(Guid id, CancellationToken cancellationToken)
  {
    return mediator.SendAsync(new GetUserByIdQuery(id), cancellationToken);
  }
}

Exemplo de publicação de notificação

using Tooark.Mediator.Abstractions;
using Tooark.Mediator.Handlers;

public sealed record UserCreatedNotify(Guid UserId) : INotify;

public sealed class UserCreatedNotifyHandler : INotifyHandler<UserCreatedNotify>
{
  public Task HandleAsync(UserCreatedNotify notification, CancellationToken cancellationToken = default)
  {
    Console.WriteLine($"Usuário criado: {notification.UserId}");
    return Task.CompletedTask;
  }
}

await mediator.PublishAsync(new UserCreatedNotify(Guid.NewGuid()), cancellationToken);

Exemplo de behavior de pipeline

Behavior genérico aberto, aplicado a todas as requisições:

using Tooark.Mediator.Abstractions;
using Tooark.Mediator.Behaviors;

public sealed class LoggingBehavior<TRequest, TResponse>(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
  : IPipelineBehavior<TRequest, TResponse>
  where TRequest : IRequest<TResponse>
{
  public async Task<TResponse> HandleAsync(
    TRequest request,
    RequestHandlerDelegate<TResponse> next,
    CancellationToken cancellationToken = default)
  {
    logger.LogInformation("Iniciando {Request}", typeof(TRequest).Name);

    var response = await next(cancellationToken);

    logger.LogInformation("Concluído {Request}", typeof(TRequest).Name);

    return response;
  }
}

Behavior fechado, aplicado a uma requisição específica, que interrompe o pipeline quando a requisição é inválida:

using Tooark.Exceptions;
using Tooark.Mediator.Behaviors;
using Tooark.Validations;

public sealed class CreateUserValidationBehavior : IPipelineBehavior<CreateUserCommand, Guid>
{
  public Task<Guid> HandleAsync(
    CreateUserCommand request,
    RequestHandlerDelegate<Guid> next,
    CancellationToken cancellationToken = default)
  {
    var validation = new Validation()
      .IsNotNullOrEmpty(request.Name, nameof(request.Name), "User.NameRequired");

    // Curto-circuito: o handler não é executado
    if (!validation.IsValid)
    {
      throw new BadRequestException(validation);
    }

    return next(cancellationToken);
  }
}

Behavior de unidade de trabalho, movendo o SaveChanges do Entity Framework para a esteira — os handlers apenas descrevem as alterações e a persistência acontece uma única vez, ao final:

using Tooark.Mediator.Abstractions;
using Tooark.Mediator.Behaviors;

public sealed class UnitOfWorkBehavior<TRequest, TResponse>(AppDbContext context)
  : IPipelineBehavior<TRequest, TResponse>
  where TRequest : ICommand<TResponse>
{
  public async Task<TResponse> HandleAsync(
    TRequest request,
    RequestHandlerDelegate<TResponse> next,
    CancellationToken cancellationToken = default)
  {
    var response = await next(cancellationToken);

    // Exceção do handler impede esta linha: nada é persistido
    await context.SaveChangesAsync(cancellationToken);

    return response;
  }
}

A restrição where TRequest : ICommand<TResponse> mantém as consultas fora da esteira — sem ela, toda leitura chamaria SaveChanges sem ter o que persistir. Um único SaveChangesAsync já é atômico, pois o Entity Framework envolve o lote em uma transação; BeginTransaction explícito só é necessário quando o handler persiste mais de uma vez ou combina o DbContext com outro recurso transacional.

O exemplo acima ilustra o padrão. Para Entity Framework Core, o pacote Tooark.Mediator.EntityFrameworkCore entrega esse behavior pronto, com tratamento de comandos aninhados e estratégia de transação explícita.

Com esse behavior registrado, o handler não toca em persistência:

public sealed class CreateUserHandler(AppDbContext context) : ICommandHandler<CreateUserCommand, Guid>
{
  public Task<Guid> HandleAsync(CreateUserCommand request, CancellationToken cancellationToken = default)
  {
    var user = new User(request.Name);

    context.Users.Add(user);

    return Task.FromResult(user.Id);
  }
}

Registro, na ordem em que devem executar — o primeiro registrado é o mais externo:

using Tooark.Mediator.Injections;

builder.Services.AddTooarkMediator(typeof(Program).Assembly);
builder.Services.AddTooarkMediatorBehavior(typeof(LoggingBehavior<,>));
builder.Services.AddTooarkMediatorBehavior<CreateUserValidationBehavior>();
builder.Services.AddTooarkMediatorBehavior(typeof(UnitOfWorkBehavior<,>));

A validação vem antes da unidade de trabalho: não faz sentido preparar a persistência para em seguida rejeitar a requisição.

Os behaviors não são descobertos pelo scan de assemblies: a ordem de execução é semântica e a ordem retornada pelo scan não é garantida. O registro é sempre explícito.

Dois pontos de atenção com a unidade de trabalho na esteira. Um comando despachado de dentro de outro comando persiste no meio da operação do externo, que persiste novamente ao final — evite o aninhamento ou trate a reentrância. E notificações não passam pelo pipeline: os handlers delas executam dentro do handler do comando, portanto antes do SaveChanges, e o que escreverem no contexto é persistido junto.


📋 Dependências

Pacote Versão Descrição
Tooark.Exceptions 4.x Exceções (ex.: BadRequestException)
Tooark.Mediator.Abstractions 4.x Contratos base do padrão Mediator
Microsoft.Extensions.DependencyInjection.Abstractions 8.x/10.x Abstrações de injeção de dependência
Microsoft.Extensions.Options 8.x/10.x Padrão Options para MediatorOptions

🪪 Contribuição

Contribuições são bem-vindas! Sinta-se à vontade para abrir issues e pull requests no repositório Tooark.Mediator.

📄 Licença

Este projeto está licenciado sob a licença BSD 3-Clause. Veja o arquivo LICENSE para mais detalhes.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Tooark.Mediator:

Package Downloads
Tooark

Package with all Tooark resources for .NET applications.

Tooark.Mediator.EntityFrameworkCore

Library that moves Entity Framework Core persistence into the Tooark.Mediator pipeline, keeping handlers free of SaveChanges.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.0.1 135 8/25/2026
4.0.0 128 8/25/2026
3.3.5 95 8/25/2026
3.3.4 103 8/12/2026
3.3.3 120 7/27/2026
3.3.2 287 6/11/2026
3.3.1 140 6/6/2026
3.3.0 141 4/17/2026