ATCMediator es una implementación del patrón Mediator para arquitecturas limpias. Funciona como una “torre de control” para tus casos de uso o servicios, centralizando comandos y queries. La librería busca fomentar una comunidad para fortalecer la experiencia de desarrollo en C# .NET.
Ver en NuGetPuedes instalar ATCMediator desde NuGet ejecutando:
dotnet add package ATCMediator --version 4.1.2
Después de instalar el paquete, registra los handlers en el contenedor de dependencias:
public static class DependencyInjection
{
public static IServiceCollection AddMicroProductApplication(this IServiceCollection services)
{
// Registra todos los handlers de ATCMediator que estén en este ensamblado
services.AddATCMediator(Assembly.GetExecutingAssembly());
return services;
}
}
public class CreateProductCommand : IAtcRequest
{
public string? Name { get; set; }
public decimal Price { get; set; }
}
public class CreateProductCommandHandler : IAtcRequestHandler
{
private readonly IProductRepository _productRepository;
public CreateProductCommandHandler(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task HandlerAsync(CreateProductCommand command, CancellationToken cancellationToken = default)
{
var product = Domain.Product.Product.Create(command.Name!, command.Price);
await _productRepository.AddAsync(product);
return product.Id;
}
}
public class GetProductAllQuery : IAtcRequest> {}
public class GetProductAllQueryHandler : IAtcRequestHandler>
{
private readonly IProductRepository _productRepository;
public GetProductAllQueryHandler(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task> HandlerAsync(GetProductAllQuery query, CancellationToken cancellationToken = default)
{
var products = await _productRepository.GetAllAsync();
return products;
}
}
[ApiController]
[Route("api/[controller]")]
public class ProductController : ControllerBase
{
private readonly IMediator _mediator;
public ProductController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task CrearProducto([FromBody] CreateProductCommand createProduct)
{
var result = await _mediator.ExecuteAsync(createProduct);
return Ok(result);
}
[HttpGet]
public async Task ObtenerProductos()
{
var productos = await _mediator.ExecuteAsync(new GetProductAllQuery());
return Ok(productos);
}
}
Puedes interceptar y extender el flujo de ejecución mediante la interfaz IAtcPipelineBehavior<TRequest, TResponse>. Esto permite aplicar validaciones, logs, autorización y más, al estilo de un middleware.
✨ Ejemplo: ValidationBehavior con FluentValidation
public class ValidationBehavior<TRequest, TResponse> : IAtcPipelineBehavior<TRequest, TResponse>
where TRequest : IAtcRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public async Task<TResponse> HandleAsync(
TRequest request,
CancellationToken cancellationToken,
Func<Task<TResponse>> next)
{
var context = new ValidationContext<TRequest>(request);
var failures = (await Task.WhenAll(
_validators.Select(v => v.ValidateAsync(context, cancellationToken))))
.SelectMany(r => r.Errors)
.Where(f => f != null)
.ToList();
if (failures.Any())
throw new ValidationException(failures);
return await next();
}
}
🧪 Registro del Behavior y Validadores
builder.Services.AddScoped(typeof(IAtcPipelineBehavior<,>), typeof(ValidationBehavior<,>));
builder.Services.AddValidatorsFromAssemblyContaining<CreateProductValidator>();
Puedes encadenar múltiples behaviors para crear un pipeline robusto y flexible:
🛡️ Middleware para capturar excepciones de validación
public class ValidationExceptionMiddleware
{
private readonly RequestDelegate _next;
public ValidationExceptionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (ValidationException ex)
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
context.Response.ContentType = "application/json";
var errors = ex.Errors
.Select(f => new { f.PropertyName, f.ErrorMessage })
.ToList();
await context.Response.WriteAsJsonAsync(new
{
message = "La validación falló.",
errors
});
}
}
}
Y lo registras en Program.cs antes del controlador:
app.UseMiddleware<ValidationExceptionMiddleware>();