Uma implementação leve e eficiente do padrão Mediator para .NET, construída sobre TheNoobs.Results para tratamento robusto de erros.
- Leve e performático: Implementação minimalista focada em desempenho
- Type-safe: Totalmente tipado com suporte a genéricos
- Pipeline de handlers: Suporte a middlewares/pipelines para cross-cutting concerns
- Event handling: Suporte nativo para publicação de eventos
- Injeção de dependência: Integração perfeita com
Microsoft.Extensions.DependencyInjection - Result pattern: Integrado com
TheNoobs.Resultspara tratamento de erros funcional - .NET 9.0: Construído para a versão mais recente do .NET
Instale via NuGet Package Manager:
dotnet add package TheNoobs.MediatorOu via Package Manager Console:
Install-Package TheNoobs.MediatorNo seu Program.cs ou Startup.cs:
using TheNoobs.Mediator.DependencyInjection;
// Registre o mediator e escaneie assemblies para handlers
builder.Services.AddMediator(typeof(Program).Assembly);public interface ICreateUserCommand
{
string Name { get; }
string Email { get; }
}
public record CreateUserCommand(string Name, string Email) : ICreateUserCommand;using TheNoobs.Mediator.Abstractions;
using TheNoobs.Results;
public class CreateUserHandler : IHandler<ICreateUserCommand, User>
{
private readonly IUserRepository _repository;
public CreateUserHandler(IUserRepository repository)
{
_repository = repository;
}
public async ValueTask<Result<User>> HandleAsync(
ICreateUserCommand command,
CancellationToken cancellationToken)
{
var user = new User(command.Name, command.Email);
await _repository.SaveAsync(user, cancellationToken);
return user;
}
}public class UserController : ControllerBase
{
private readonly IMediator _mediator;
public UserController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<IActionResult> CreateUser(CreateUserCommand command)
{
var result = await _mediator.SendAsync<ICreateUserCommand, User>(
command,
HttpContext.RequestAborted);
return result.Match(
success => Ok(success),
fail => BadRequest(fail)
);
}
}Commands devem ser definidos como interfaces (requisito do mediator). Os handlers implementam IHandler<TCommand, TResult>:
public interface IUpdateProductCommand
{
Guid Id { get; }
string Name { get; }
decimal Price { get; }
}
public class UpdateProductHandler : IHandler<IUpdateProductCommand, Product>
{
public async ValueTask<Result<Product>> HandleAsync(
IUpdateProductCommand command,
CancellationToken cancellationToken)
{
// Lógica de atualização
return updatedProduct;
}
}Para eventos assíncronos, use IEventHandler<TEvent>:
public record UserCreatedEvent(Guid UserId, string Email);
public class SendWelcomeEmailHandler : IEventHandler<UserCreatedEvent>
{
private readonly IEmailService _emailService;
public SendWelcomeEmailHandler(IEmailService emailService)
{
_emailService = emailService;
}
public async ValueTask<Result<Void>> HandleAsync(
UserCreatedEvent @event,
CancellationToken cancellationToken)
{
await _emailService.SendWelcomeEmailAsync(@event.Email);
return Void.Value;
}
}Publique eventos:
await _mediator.PublishAsync(
new UserCreatedEvent(user.Id, user.Email),
cancellationToken);Pipelines permitem adicionar comportamentos transversais (logging, validação, transações, etc.):
public class LoggingPipeline<TCommand, TResult> : IHandlerPipeline<TCommand, TResult>
where TCommand : notnull
where TResult : notnull
{
private readonly ILogger<LoggingPipeline<TCommand, TResult>> _logger;
public LoggingPipeline(ILogger<LoggingPipeline<TCommand, TResult>> logger)
{
_logger = logger;
}
public async ValueTask<Result<TResult>> HandleAsync(
TCommand command,
Func<TCommand, CancellationToken, ValueTask<Result<TResult>>> next,
CancellationToken cancellationToken)
{
_logger.LogInformation("Executing command {CommandType}", typeof(TCommand).Name);
var result = await next(command, cancellationToken);
_logger.LogInformation("Command {CommandType} completed", typeof(TCommand).Name);
return result;
}
}Os pipelines são automaticamente descobertos e registrados pelo AddMediator().
public class ValidationPipeline<TCommand, TResult> : IHandlerPipeline<TCommand, TResult>
where TCommand : notnull
where TResult : notnull
{
private readonly IValidator<TCommand> _validator;
public ValidationPipeline(IValidator<TCommand> validator)
{
_validator = validator;
}
public async ValueTask<Result<TResult>> HandleAsync(
TCommand command,
Func<TCommand, CancellationToken, ValueTask<Result<TResult>>> next,
CancellationToken cancellationToken)
{
var validationResult = await _validator.ValidateAsync(command);
if (!validationResult.IsValid)
{
return new ValidationFail(validationResult.Errors);
}
return await next(command, cancellationToken);
}
}public class TransactionPipeline<TCommand, TResult> : IHandlerPipeline<TCommand, TResult>
where TCommand : notnull
where TResult : notnull
{
private readonly IUnitOfWork _unitOfWork;
public TransactionPipeline(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async ValueTask<Result<TResult>> HandleAsync(
TCommand command,
Func<TCommand, CancellationToken, ValueTask<Result<TResult>>> next,
CancellationToken cancellationToken)
{
await _unitOfWork.BeginTransactionAsync(cancellationToken);
try
{
var result = await next(command, cancellationToken);
if (result.IsSuccess)
{
await _unitOfWork.CommitAsync(cancellationToken);
}
else
{
await _unitOfWork.RollbackAsync(cancellationToken);
}
return result;
}
catch
{
await _unitOfWork.RollbackAsync(cancellationToken);
throw;
}
}
}services.AddMediator(
typeof(Program).Assembly,
typeof(DomainHandler).Assembly,
typeof(ApplicationHandler).Assembly
);// Command
public interface IPlaceOrderCommand
{
Guid CustomerId { get; }
List<OrderItem> Items { get; }
}
public record PlaceOrderCommand(Guid CustomerId, List<OrderItem> Items) : IPlaceOrderCommand;
// Handler
public class PlaceOrderHandler : IHandler<IPlaceOrderCommand, Order>
{
private readonly IOrderRepository _orderRepository;
private readonly IMediator _mediator;
public PlaceOrderHandler(IOrderRepository orderRepository, IMediator mediator)
{
_orderRepository = orderRepository;
_mediator = mediator;
}
public async ValueTask<Result<Order>> HandleAsync(
IPlaceOrderCommand command,
CancellationToken cancellationToken)
{
var order = Order.Create(command.CustomerId, command.Items);
await _orderRepository.SaveAsync(order, cancellationToken);
// Publica evento
await _mediator.PublishAsync(
new OrderPlacedEvent(order.Id, order.CustomerId),
cancellationToken);
return order;
}
}
// Event Handler
public class OrderPlacedNotificationHandler : IEventHandler<OrderPlacedEvent>
{
private readonly INotificationService _notificationService;
public OrderPlacedNotificationHandler(INotificationService notificationService)
{
_notificationService = notificationService;
}
public async ValueTask<Result<Void>> HandleAsync(
OrderPlacedEvent @event,
CancellationToken cancellationToken)
{
await _notificationService.NotifyOrderPlacedAsync(@event.OrderId);
return Void.Value;
}
}Contribuições são bem-vindas! Por favor:
- Faça um fork do projeto
- Crie uma branch para sua feature (
git checkout -b feature/AmazingFeature) - Commit suas mudanças (
git commit -m 'Add some AmazingFeature') - Push para a branch (
git push origin feature/AmazingFeature) - Abra um Pull Request
Este projeto está sob a licença especificada no arquivo LICENSE.
♥ Made with love by The Noobs!