Hecole.Mediator
1.3.0
dotnet add package Hecole.Mediator --version 1.3.0
NuGet\Install-Package Hecole.Mediator -Version 1.3.0
<PackageReference Include="Hecole.Mediator" Version="1.3.0" />
<PackageVersion Include="Hecole.Mediator" Version="1.3.0" />
<PackageReference Include="Hecole.Mediator" />
paket add Hecole.Mediator --version 1.3.0
#r "nuget: Hecole.Mediator, 1.3.0"
#:package Hecole.Mediator@1.3.0
#addin nuget:?package=Hecole.Mediator&version=1.3.0
#tool nuget:?package=Hecole.Mediator&version=1.3.0
🇧🇷 Português | 🇺🇸 English
🧩 Hecole.Mediator
A lightweight and performant implementation of the Mediator pattern for .NET 8 and .NET 10, inspired by MediatR.
Designed for modular projects based on Clean Architecture, with support for CQRS (Commands, Queries, Requests, and Notifications), async behavior pipelines (validation, logging, performance monitoring, and exception handling), and native integration with Microsoft.Extensions.DependencyInjection.
Ideal for systems with independent modules that need use case orchestration with low coupling.
🚀 Key Features
✅ CQRS Support —
IRequest<TResponse>,IRequestHandler<TRequest, TResponse>,INotification, andINotificationHandler<TNotification>.🧠 Async Pipeline Behaviors — Middleware chain for cross-cutting concerns: async validation with FluentValidation (
ValidateAsync+MustAsync— fixed in v1.2.0), structured logging, performance monitoring, and unhandled exception capture.⚙️ Auto-Registration via DI —
AddHecoleMediatorextension for automatic assembly scanning and handler/behavior registration.⚡ Optimized Performance — Reflection invoker caching with
ConcurrentDictionaryto avoid reflection on the hot path; parallel notification dispatch viaTask.WhenAll.🧱 Robustness — Isolated exception handling per notification handler, multiple validator support, and fire-and-forget notification semantics.
🎯 Multi-Targeting — Supports
net8.0andnet10.0in the same NuGet package. The runtime resolves the correct target automatically.🧩 Optional Dependencies — Core depends only on
Microsoft.Extensions.DependencyInjection. FluentValidation and Logging are optional.🧭 Clean Architecture Aligned — Pure interfaces for Domain/SharedKernel and pluggable implementations in the Infrastructure layer.
🧰 Requirements
- .NET 8.0 or .NET 10.0 or later.
- Optional packages:
FluentValidation(forValidationBehavior)Microsoft.Extensions.Logging(for structured logging)
💾 Installation
Via NuGet:
dotnet add package Hecole.Mediator
Or as a project reference:
git clone https://github.com/mvsergio/Hecole.Mediator.git
<ProjectReference Include="..\Hecole.Mediator\Hecole.Mediator.csproj" />
🧠 Usage
DI Registration (Program.cs)
using Hecole.Mediator.Implementation.Extensions;
using System.Reflection;
var builder = WebApplication.CreateBuilder(args);
// Register Mediator and scan assemblies (e.g., Application layer)
builder.Services.AddHecoleMediator(
Assembly.GetAssembly(typeof(CreateInstitutionCommandHandler))
);
// Global behaviors (registration order matters: first registered = outermost)
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(PerformanceBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
// Register validators
builder.Services.AddTransient<IValidator<CreateInstitutionCommand>, CreateInstitutionCommandValidator>();
AddHecoleMediatorregistersICoreMediatoras Scoped (recommended — works with scopedDbContext).
AddCoreMediatorregistersICoreMediatoras Singleton (use when all dependencies are also singletons).
Command / Query Handler
using Hecole.Mediator.Interfaces;
public record CreateInstitutionCommand(string Name) : IRequest<CreateInstitutionResult>;
public record CreateInstitutionResult(Guid Id);
public class CreateInstitutionCommandHandler
: IRequestHandler<CreateInstitutionCommand, CreateInstitutionResult>
{
private readonly IRepository _repository;
public CreateInstitutionCommandHandler(IRepository repository)
{
_repository = repository;
}
public async Task<CreateInstitutionResult> Handle(
CreateInstitutionCommand request,
CancellationToken cancellationToken)
{
var id = await _repository.SaveAsync(request, cancellationToken);
return new CreateInstitutionResult(id);
}
}
Notification Handler
using Hecole.Mediator.Interfaces;
public record InstitutionCreatedEvent(Guid Id, string Name) : INotification;
public class SendWelcomeEmailHandler : INotificationHandler<InstitutionCreatedEvent>
{
public async Task Handle(InstitutionCreatedEvent notification, CancellationToken cancellationToken)
{
// Send welcome email
await Task.CompletedTask;
}
}
Notifications are dispatched to all registered handlers in parallel via Task.WhenAll. If one handler throws, the others still execute — the exception propagates to the caller after all handlers complete.
Controller
using Hecole.Mediator.Interfaces;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/institutions")]
public class InstitutionController : ControllerBase
{
private readonly ICoreMediator _mediator;
public InstitutionController(ICoreMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<IActionResult> Create(
[FromBody] CreateInstitutionCommand command,
CancellationToken cancellationToken)
{
var result = await _mediator.Send(command, cancellationToken);
return Ok(result);
}
}
Validation with MustAsync() (fixed in v1.2.0)
using FluentValidation;
public class CreateInstitutionCommandValidator : AbstractValidator<CreateInstitutionCommand>
{
public CreateInstitutionCommandValidator(IRepository repository)
{
RuleFor(x => x.Name)
.NotEmpty().WithMessage("Name is required")
.MaximumLength(200).WithMessage("Name too long");
RuleFor(x => x.Name)
.MustAsync(async (name, ct) =>
{
return !await repository.ExistsAsync(name, ct);
})
.WithMessage("An institution with this name already exists");
}
}
In v1.1.0 and earlier,
MustAsync()rules were silently ignored becauseValidationBehaviorcalled the synchronousValidate(). Since v1.2.0, it correctly usesValidateAsync()withTask.WhenAll, and theCancellationTokenis propagated.
Custom Behavior
using Hecole.Mediator.Interfaces;
using Hecole.Mediator.Interfaces.Behaviors;
public class AuditBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
// Before handler
Console.WriteLine($"Processing {typeof(TRequest).Name}");
var response = await next();
// After handler
Console.WriteLine($"Completed {typeof(TRequest).Name}");
return response;
}
}
Register it in the DI container:
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(AuditBehavior<,>));
🔧 Built-in Behaviors
| Behavior | Description |
|---|---|
ValidationBehavior<,> |
Runs all IValidator<TRequest> via ValidateAsync. Throws ValidationException on failure. |
LoggingBehavior<,> |
Logs request start/end with elapsed time. |
PerformanceBehavior<,> |
Logs a warning when a request takes longer than 500ms. |
UnhandledExceptionBehavior<,> |
Catches and logs unhandled exceptions, then re-throws. |
🤝 Contributing
Contributions are welcome!
git clone https://github.com/mvsergio/Hecole.Mediator.git
git checkout -b feature/my-feature
git commit -m "Add my feature"
git push origin feature/my-feature
Open a Pull Request with a description of your changes.
Please add unit tests and follow the existing code style (#nullable enable, async/await).
⚖️ License
Distributed under the MIT License. See LICENSE for details.
📋 Changelog
See CHANGELOG.md for a full list of changes.
🇧🇷 Português
🇧🇷 Versão completa em português
Uma implementação leve e performática do padrão Mediator para .NET 8 e .NET 10, inspirada no MediatR.
Projetada para projetos modulares baseados em Clean Architecture, com suporte a CQRS (Commands, Queries, Requests e Notifications), pipelines de behaviors assíncronos (validação, logging, monitoramento de performance e tratamento de exceções) e integração nativa com Microsoft.Extensions.DependencyInjection.
Ideal para sistemas com módulos independentes que precisam de orquestração de use cases com baixo acoplamento.
🚀 Features Principais
✅ Suporte a CQRS —
IRequest<TResponse>,IRequestHandler<TRequest, TResponse>,INotificationeINotificationHandler<TNotification>.🧠 Pipeline de Behaviors Assíncronos — Cadeia de middlewares para cross-cutting concerns: validação assíncrona com FluentValidation (
ValidateAsync+MustAsync— corrigido na v1.2.0), logging estruturado, monitoramento de performance e captura de exceções.⚙️ Registro Automático via DI — Extensão
AddHecoleMediatorpara scan automático de assemblies e registro de handlers/behaviors.⚡ Performance Otimizada — Caching de invokers com
ConcurrentDictionarypara evitar reflection no hot path; execução paralela de notifications viaTask.WhenAll.🧱 Robustez — Tratamento isolado de exceções por notification handler, suporte a múltiplos validators e semântica fire-and-forget para notifications.
🎯 Multi-Targeting — Suporte a
net8.0enet10.0no mesmo pacote NuGet. O runtime resolve o target correto automaticamente.🧩 Dependências Opcionais — Core depende apenas de
Microsoft.Extensions.DependencyInjection. FluentValidation e Logging são opcionais.🧭 Alinhado com Clean Architecture — Interfaces puras para Domain/SharedKernel e implementações plugáveis na camada Infrastructure.
🧰 Requisitos
- .NET 8.0 ou .NET 10.0 ou superior.
- Pacotes opcionais:
FluentValidation(paraValidationBehavior)Microsoft.Extensions.Logging(para logging estruturado)
💾 Instalação
Via NuGet:
dotnet add package Hecole.Mediator
Ou como referência de projeto:
git clone https://github.com/mvsergio/Hecole.Mediator.git
<ProjectReference Include="..\Hecole.Mediator\Hecole.Mediator.csproj" />
🧠 Uso
Registro no DI (Program.cs)
using Hecole.Mediator.Implementation.Extensions;
using System.Reflection;
var builder = WebApplication.CreateBuilder(args);
// Registra o Mediator e escaneia assemblies (ex.: camada Application)
builder.Services.AddHecoleMediator(
Assembly.GetAssembly(typeof(CreateInstitutionCommandHandler))
);
// Behaviors globais (ordem de registro importa: primeiro registrado = mais externo)
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(PerformanceBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
// Registrar validators
builder.Services.AddTransient<IValidator<CreateInstitutionCommand>, CreateInstitutionCommandValidator>();
AddHecoleMediatorregistraICoreMediatorcomo Scoped (recomendado — funciona comDbContextscoped).
AddCoreMediatorregistraICoreMediatorcomo Singleton (use quando todas as dependências também são singletons).
Handler de Command / Query
using Hecole.Mediator.Interfaces;
public record CreateInstitutionCommand(string Name) : IRequest<CreateInstitutionResult>;
public record CreateInstitutionResult(Guid Id);
public class CreateInstitutionCommandHandler
: IRequestHandler<CreateInstitutionCommand, CreateInstitutionResult>
{
private readonly IRepository _repository;
public CreateInstitutionCommandHandler(IRepository repository)
{
_repository = repository;
}
public async Task<CreateInstitutionResult> Handle(
CreateInstitutionCommand request,
CancellationToken cancellationToken)
{
var id = await _repository.SaveAsync(request, cancellationToken);
return new CreateInstitutionResult(id);
}
}
Handler de Notification
using Hecole.Mediator.Interfaces;
public record InstitutionCreatedEvent(Guid Id, string Name) : INotification;
public class SendWelcomeEmailHandler : INotificationHandler<InstitutionCreatedEvent>
{
public async Task Handle(InstitutionCreatedEvent notification, CancellationToken cancellationToken)
{
// Enviar e-mail de boas-vindas
await Task.CompletedTask;
}
}
Notifications são despachadas para todos os handlers registrados em paralelo via Task.WhenAll. Se um handler lançar exceção, os demais ainda executam — a exceção é propagada ao caller após todos os handlers completarem.
Controller
using Hecole.Mediator.Interfaces;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/institutions")]
public class InstitutionController : ControllerBase
{
private readonly ICoreMediator _mediator;
public InstitutionController(ICoreMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<IActionResult> Create(
[FromBody] CreateInstitutionCommand command,
CancellationToken cancellationToken)
{
var result = await _mediator.Send(command, cancellationToken);
return Ok(result);
}
}
Validação com MustAsync() (corrigido na v1.2.0)
using FluentValidation;
public class CreateInstitutionCommandValidator : AbstractValidator<CreateInstitutionCommand>
{
public CreateInstitutionCommandValidator(IRepository repository)
{
RuleFor(x => x.Name)
.NotEmpty().WithMessage("Name is required")
.MaximumLength(200).WithMessage("Name too long");
RuleFor(x => x.Name)
.MustAsync(async (name, ct) =>
{
return !await repository.ExistsAsync(name, ct);
})
.WithMessage("An institution with this name already exists");
}
}
Na v1.1.0 e anteriores, regras
MustAsync()eram ignoradas silenciosamente porque oValidationBehaviorchamavaValidate()(síncrono). Desde a v1.2.0, ele usa corretamenteValidateAsync()comTask.WhenAll, e oCancellationTokené propagado.
Custom Behavior
using Hecole.Mediator.Interfaces;
using Hecole.Mediator.Interfaces.Behaviors;
public class AuditBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
// Antes do handler
Console.WriteLine($"Processando {typeof(TRequest).Name}");
var response = await next();
// Depois do handler
Console.WriteLine($"Concluído {typeof(TRequest).Name}");
return response;
}
}
Registre no container DI:
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(AuditBehavior<,>));
🔧 Behaviors Incluídos
| Behavior | Descrição |
|---|---|
ValidationBehavior<,> |
Executa todos os IValidator<TRequest> via ValidateAsync. Lança ValidationException em caso de falha. |
LoggingBehavior<,> |
Loga início/fim do request com tempo decorrido. |
PerformanceBehavior<,> |
Loga warning quando um request leva mais de 500ms. |
UnhandledExceptionBehavior<,> |
Captura e loga exceções não tratadas, depois re-lança. |
🤝 Contribuições
Contribuições são bem-vindas!
git clone https://github.com/mvsergio/Hecole.Mediator.git
git checkout -b feature/minha-feature
git commit -m "Adiciona minha feature"
git push origin feature/minha-feature
Abra um Pull Request com uma descrição das alterações.
Adicione testes unitários e siga o estilo de código existente (#nullable enable, async/await).
⚖️ Licença
Distribuído sob a MIT License. Veja LICENSE para detalhes.
📋 Changelog
Veja CHANGELOG.md para a lista completa de mudanças.
| Product | Versions 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. |
-
net10.0
- FluentValidation (>= 12.1.0)
- Microsoft.Extensions.DependencyInjection (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.10)
-
net8.0
- FluentValidation (>= 12.1.0)
- Microsoft.Extensions.DependencyInjection (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.10)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
1.3.0 (2026-04-28) — Fix open-generic IPipelineBehavior registration in AddHecoleMediator. Open-generic behaviors (ex: ObservabilityBehavior<TRequest, TResponse>) were registered with parameterized types causing ArgumentException at BuildServiceProvider. Now correctly registered as (typeof(IPipelineBehavior<,>), implementationType) when type.IsGenericTypeDefinition. Tests added (AddHecoleMediatorTests). No breaking change — workaround manual via services.Remove + AddTransient open-generic can be removed.