EV.MediatorX
8.0.0
dotnet add package EV.MediatorX --version 8.0.0
NuGet\Install-Package EV.MediatorX -Version 8.0.0
<PackageReference Include="EV.MediatorX" Version="8.0.0" />
<PackageVersion Include="EV.MediatorX" Version="8.0.0" />
<PackageReference Include="EV.MediatorX" />
paket add EV.MediatorX --version 8.0.0
#r "nuget: EV.MediatorX, 8.0.0"
#:package EV.MediatorX@8.0.0
#addin nuget:?package=EV.MediatorX&version=8.0.0
#tool nuget:?package=EV.MediatorX&version=8.0.0
⚡ EV.MediatorX
Una implementación robusta y eficiente del patrón CQRS Mediator para .NET 8+ diseñada para aplicaciones empresariales modernas.
✨ Características
- 🎯 Comandos y Consultas (CQRS) - Separación clara entre operaciones de lectura y escritura.
- 🔄 Pipeline Behaviors - Middleware personalizable para logging, validación y más.
- ✅ Validación Integrada - Sistema de validación fluido y extensible.
- 📢 Domain Events - Manejo robusto de eventos de dominio con notificaciones.
- 🛡️ Manejo de Excepciones - Captura automática y gestión centralizada de errores.
- 🎁 Patrón Result<T> - Manejo funcional de errores sin excepciones.
- ⚡ Async/Await - API completamente asíncrona para máximo rendimiento.
- 📦 Dependency Injection - Integración perfecta con el contenedor DI de .NET.
- 🔧 Altamente Configurable - Adapta el comportamiento a tus necesidades.
🔐 Seguridad y Privacidad (DataSanitizer)
- ✅ Cumple con GDPR y normativas de privacidad.
- 🚫 Evita exponer credenciales en logs.
- 🐛 Permite debug sin comprometer seguridad.
- 🔍 Detecta automáticamente campos sensibles por nombre.
- 📝 Campos personalizados para datos sensibles: "identification", "password", "token", "key", "secret", "authorization", "connectionstring", "apikey", "clientsecret", "bearer", "credential", "pin", "ssn", "dni", "nif", "passport", "username", "email", "value", "owner", "pwd", "createdby", "updatedby", "deletedby".
📦 Instalación
dotnet add package EV.MediatorX
O vía NuGet Package Manager:
Install-Package EV.MediatorX
🚀 Inicio Rápido
1. Configurar en Program.cs
using EV.MediatorX.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Configurar EV.MediatorX
builder.Services.AddMediatorX(config =>
{
// Registrar handlers y validadores del assembly actual
config.RegisterServicesFromAssembly(typeof(Program).Assembly);
config.RegisterValidatorsFromAssembly(typeof(Program).Assembly);
// Agregar behaviors predeterminados (recomendado)
config.AddDefaultBehaviors(
includeValidation: true,
includePerformance: true,
includeExceptionHandling: true,
includeLogging: true
);
});
// Agregar tus servicios
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
2. Crear un Comando
using EV.MediatorX.Abstractions.Messaging;
// Definir el comando
public sealed record CreateProductCommand(
string Name,
string Description,
decimal Price,
int Stock
) : ICommand<Guid>;
// Implementar el handler
public sealed class CreateProductCommandHandler : ICommandHandler<CreateProductCommand, Guid>
{
private readonly IProductRepository _repository;
private readonly ILogger<CreateProductCommandHandler> _logger;
public CreateProductCommandHandler(
IProductRepository repository,
ILogger<CreateProductCommandHandler> logger)
{
_repository = repository;
_logger = logger;
}
public async Task<Result<Guid>> HandleAsync(
CreateProductCommand request,
CancellationToken ct)
{
_logger.LogInformation("Creating product: {Name}", request.Name);
var product = Product.Create(
request.Name,
request.Description,
request.Price,
request.Stock
);
await _repository.AddAsync(product, ct);
_logger.LogInformation("Product created with Id: {ProductId}", product.Id);
return Result.Success(product.Id);
}
}
3. Crear una Consulta
using EV.MediatorX.Abstractions.Messaging;
// Definir la consulta
public sealed record GetProductByIdQuery(Guid Id) : IQuery<ProductDto>;
// Implementar el handler
public sealed class GetProductByIdQueryHandler : IQueryHandler<GetProductByIdQuery, ProductDto>
{
private readonly IProductRepository _repository;
public GetProductByIdQueryHandler(IProductRepository repository)
{
_repository = repository;
}
public async Task<Result<ProductDto>> HandleAsync(
GetProductByIdQuery request,
CancellationToken ct)
{
var product = await _repository.GetByIdAsync(request.Id, ct);
if (product is null)
{
return Result.Failure<ProductDto>(
Error.NotFound(
"Product.NotFound",
$"Product with ID {request.Id} not found"
)
);
}
var dto = new ProductDto
{
Id = product.Id,
Name = product.Name,
Description = product.Description,
Price = product.Price,
Stock = product.Stock,
CreatedAt = product.CreatedAt
};
return Result.Success(dto);
}
}
4. Usar en un Controller
using EV.MediatorX.Core;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IMediator _mediator;
public ProductsController(IMediator mediator) => _mediator = mediator;
[HttpPost]
[ProducesResponseType(typeof(Guid), StatusCodes.Status201Created)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Create([FromBody] CreateProductCommand command)
{
var result = await _mediator.SendAsync(command);
return result.IsFailure
? BadRequest(new {
error = result.Error.Code,
message = result.Error.Message
})
: CreatedAtAction(nameof(GetById), new { id = result.Value }, result.Value);
}
[HttpGet("{id}")]
[ProducesResponseType(typeof(ProductDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetById(Guid id)
{
var result = await _mediator.SendAsync(new GetProductByIdQuery(id));
return result.IsFailure
? NotFound(new {
error = result.Error.Code,
message = result.Error.Message
})
: Ok(result.Value);
}
[HttpGet]
[ProducesResponseType(typeof(List<ProductDto>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetAll()
{
var result = await _mediator.SendAsync(new GetAllProductsQuery());
return Ok(result.Value);
}
[HttpPut("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> Update(
Guid id,
[FromBody] UpdateProductRequest request)
{
var command = new UpdateProductCommand(
id,
request.Name,
request.Description,
request.Price,
request.Stock
);
var result = await _mediator.SendAsync(command);
return result.IsFailure
? NotFound(new {
error = result.Error.Code,
message = result.Error.Message
})
: NoContent();
}
[HttpDelete("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> Delete(Guid id)
{
var result = await _mediator.SendAsync(new DeleteProductCommand(id));
return result.IsFailure
? NotFound(new {
error = result.Error.Code,
message = result.Error.Message
})
: NoContent();
}
}
🎯 Características Avanzadas
Validación de Comandos
Implementa validadores para asegurar la integridad de los datos antes de ejecutar comandos:
using EV.MediatorX.Behaviors;
public sealed class CreateProductCommandValidator : IValidator<CreateProductCommand>
{
public Task<ValidationResult> ValidateAsync(
ValidationContext<CreateProductCommand> context,
CancellationToken cancellationToken = default)
{
var cmd = context.InstanceToValidate;
var errors = new List<ValidationFailure>();
// Validar nombre
if (string.IsNullOrWhiteSpace(cmd.Name))
{
errors.Add(new ValidationFailure(
nameof(cmd.Name),
"Name is required"
));
}
else if (cmd.Name.Length < 3)
{
errors.Add(new ValidationFailure(
nameof(cmd.Name),
"Name must be at least 3 characters"
));
}
// Validar precio
if (cmd.Price <= 0)
{
errors.Add(new ValidationFailure(
nameof(cmd.Price),
"Price must be greater than zero"
));
}
// Validar stock
if (cmd.Stock < 0)
{
errors.Add(new ValidationFailure(
nameof(cmd.Stock),
"Stock cannot be negative"
));
}
return Task.FromResult(
errors.Any()
? ValidationResult.WithErrors(errors)
: ValidationResult.Success()
);
}
}
Events (Notificaciones)
Implementa eventos para reaccionar a cambios importantes:
using EV.MediatorX.Abstractions.Messaging;
using EV.MediatorX.Api.Interfaces;
using EV.MediatorX.Api.Models;
using EV.MediatorX.Core;
// 1. Definir el evento
public sealed record ProductCreatedEvent(Guid ProductId) : IEvent;
// 2. Crear el handler del evento
internal sealed class ProductCreatedEventHandler : INotificationHandler<ProductCreatedEvent>
{
private readonly ILogger<ProductCreatedEventHandler> _logger;
public ProductCreatedEventHandler(ILogger<ProductCreatedEventHandler> logger)
{
_logger = logger;
}
public async Task HandleAsync(
ProductCreatedEvent @event,
CancellationToken cancellationToken)
{
_logger.LogInformation(
"Product created event received for ProductId: {ProductId}",
@event.ProductId
);
// Lógica de negocio: enviar email, actualizar caché, etc.
// await _emailService.SendProductCreatedNotificationAsync(...);
await Task.CompletedTask;
}
}
// 3. Levantar el evento desde tu entidad
public sealed class Product : EventAggregator<Guid>
{
public static Product Create(string name, string description, decimal price, int stock)
{
var product = new Product
{
Id = Guid.NewGuid(),
Name = name,
Description = description,
Price = price,
Stock = stock,
CreatedAt = DateTime.UtcNow
};
// Agregar el evento
product.RaiseEvent(new ProductCreatedEvent(product.Id));
return product;
}
}
// 4. Publicar eventos en el repositorio
public sealed class ProductService(IPublisher publisher) : IProductRepository
{
private readonly IPublisher _publisher = publisher;
private readonly SemaphoreSlim _lock = new(1, 1);
...
public async Task AddAsync(Product product, CancellationToken cancellationToken = default)
{
await _lock.WaitAsync(cancellationToken);
try
{
_products[product.Id] = product;
// Obtener y publicar eventos
var events = product.GetEvents();
product.ClearEvents();
foreach (var @event in events)
{
await _publisher.PublishAsync(@event, cancellationToken);
}
}
finally
{
_lock.Release();
}
}
}
Pipeline Behaviors Personalizados
Crea behaviors personalizados para añadir funcionalidad transversal:
using EV.MediatorX.Abstractions;
using EV.MediatorX.Abstractions.Messaging;
public class TransactionBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IBaseCommand
{
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger<TransactionBehavior<TRequest, TResponse>> _logger;
public TransactionBehavior(
IUnitOfWork unitOfWork,
ILogger<TransactionBehavior<TRequest, TResponse>> logger)
{
_unitOfWork = unitOfWork;
_logger = logger;
}
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
var requestName = request.GetType().Name;
_logger.LogInformation("Beginning transaction for {RequestName}", requestName);
await _unitOfWork.BeginTransactionAsync(cancellationToken);
try
{
var response = await next();
await _unitOfWork.CommitAsync(cancellationToken);
_logger.LogInformation("Transaction committed for {RequestName}", requestName);
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "Transaction failed for {RequestName}", requestName);
await _unitOfWork.RollbackAsync(cancellationToken);
throw;
}
}
}
// Registrar el behavior
builder.Services.AddMediatorX(config =>
{
config.RegisterServicesFromAssembly(typeof(Program).Assembly);
config.AddOpenBehavior(typeof(TransactionBehavior<,>));
config.AddDefaultBehaviors();
});
Behavior de Performance Monitoring (Incluido en paquete)
public class PerformanceBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IBaseCommand
{
private readonly ILogger<PerformanceBehavior<TRequest, TResponse>> _logger;
private readonly Stopwatch _timer;
public PerformanceBehavior(ILogger<PerformanceBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
_timer = new Stopwatch();
}
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
_timer.Start();
var response = await next();
_timer.Stop();
var elapsedMilliseconds = _timer.ElapsedMilliseconds;
if (elapsedMilliseconds > 500) // Alertar si toma más de 500ms
{
var requestName = typeof(TRequest).Name;
_logger.LogWarning(
"Long Running Request: {Name} -> {ElapsedMilliseconds:N0} milliseconds",
requestName,
elapsedMilliseconds
);
}
return response;
}
}
🎁 Patrón Result<T>
El patrón Result<T> permite manejar errores de forma funcional sin excepciones:
Crear Resultados
// Resultado exitoso con valor
var successResult = Result.Success(productId);
// Resultado exitoso sin valor (para comandos void)
var emptySuccess = Result.Success();
// Resultado fallido
var failureResult = Result.Failure<Product>(
Error.NotFound("Product.NotFound", "Product not found")
);
// Tipos de errores disponibles
var notFoundError = Error.NotFound("Resource.NotFound", "Resource not found");
var validationError = Error.Validation("Validation.Failed", "Validation failed");
var conflictError = Error.Conflict("Resource.Conflict", "Resource already exists");
var unauthorizedError = Error.Unauthorized("Auth.Unauthorized", "Not authorized");
Trabajar con Resultados
// Verificar éxito/fallo
if (result.IsSuccess)
{
var value = result.Value;
// Procesar el valor
}
else if (result.IsFailure)
{
var error = result.Error;
Console.WriteLine($"Error: {error.Code} - {error.Message}");
}
// Métodos funcionales
result.OnSuccess(value =>
Console.WriteLine($"Success: {value}")
);
result.OnFailure(error =>
Console.WriteLine($"Error: {error.Message}")
);
// Transformar resultados
var mappedResult = result.Map(product => new ProductDto
{
Id = product.Id,
Name = product.Name
});
// Obtener valor o default
var valueOrDefault = result.GetValueOrDefault(defaultProduct);
Encadenar Operaciones
var result = await GetProductAsync(id)
.Map(product => UpdateProduct(product, newData))
.OnSuccess(product => LogUpdate(product))
.OnFailure(error => LogError(error));
📋 Orden de Ejecución de Behaviors
Los behaviors se ejecutan en el siguiente orden:
- ExceptionHandlingBehavior - Captura y maneja excepciones
- LoggingBehavior - Registra la ejecución del request (opcional)
- ValidationBehavior - Valida el request antes de ejecutar
- Behaviors Personalizados - Tus behaviors (ej: Transaction, Performance)
- Handler - Ejecuta la lógica de negocio real
Request → Exception → Logging → Validation → Custom Behaviors → Handler → Response
🔧 Configuración Completa
Configuración Básica
builder.Services.AddMediatorX(config =>
{
// Registrar assemblies
config.RegisterServicesFromAssembly(typeof(Program).Assembly);
config.RegisterValidatorsFromAssembly(typeof(Program).Assembly);
// Behaviors predeterminados
config.AddDefaultBehaviors(
includeValidation: true,
includePerformance: true,
includeExceptionHandling: true,
includeLogging: true
);
});
Configuración Avanzada
builder.Services.AddMediatorX(config =>
{
// Registrar múltiples assemblies
config
.RegisterServicesFromAssembly(typeof(Program).Assembly)
.RegisterServicesFromAssembly(typeof(ApplicationLayer).Assembly)
.RegisterValidatorsFromAssembly(typeof(Program).Assembly);
// Agregar behaviors personalizados (se ejecutan después de los default)
config.AddOpenBehavior(typeof(TransactionBehavior<,>));
config.AddOpenBehavior(typeof(PerformanceBehavior<,>));
config.AddOpenBehavior(typeof(CachingBehavior<,>));
// Behaviors predeterminados
config.AddDefaultBehaviors(
includeValidation: true,
includeExceptionHandling: true,
includePerformance: false, // Desactivar si no deseas tener metrica de performance
includeLogging: false // Desactivar si usas behavior personalizado
);
});
📊 Comandos vs Consultas
Comandos (ICommand)
- Modifican el estado del sistema
- Retornan
Result<T>oResult - Pueden tener validación
- Pueden generar eventos de dominio
// Comando con respuesta
public sealed record CreateProductCommand(...) : ICommand<Guid>;
// Comando sin respuesta
public sealed record DeleteProductCommand(Guid Id) : ICommand;
Consultas (IQuery)
- Solo leen datos
- No modifican el estado
- Retornan
Result<T> - Normalmente no tienen validación
public sealed record GetProductByIdQuery(Guid Id) : IQuery<ProductDto>;
public sealed record GetAllProductsQuery : IQuery<List<ProductDto>>;
📝 Logging
EV.MediatorX usa ILogger<T> para logging completo:
Configurar Logging en appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"EV.MediatorX": "Debug"
}
}
}
Niveles de Log
- Debug: Detalles de ejecución, validación, behaviors
- Information: Inicio/fin de comandos, eventos publicados
- Warning: Validaciones fallidas, operaciones lentas
- Error: Excepciones, fallos de handlers
Ejemplo de Logs
[INFO] Creating product: Laptop Pro
[DEBUG] Validating CreateProductCommand
[DEBUG] Executing CreateProductCommandHandler
[INFO] Product created with Id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
[INFO] Publishing ProductCreatedEvent
[DEBUG] ProductCreatedEventHandler executed successfully
🏗️ Arquitectura y Patrones
Clean Architecture
EV.MediatorX se integra perfectamente con Clean Architecture:
Presentation Layer (API/Controllers)
↓ (usa IMediator)
Application Layer (Handlers, Validators, Events)
↓ (usa interfaces)
Domain Layer (Entities, Domain Events)
↓
Infrastructure Layer (Repositories, DbContext)
Estructura de Carpetas Recomendada en tu Proyecto
YourProject/
├── Controllers/
│ └── ProductsController.cs
├── Application/
│ ├── Products/
│ │ ├── Commands/
│ │ │ ├── CreateProduct/
│ │ │ │ ├── CreateProductCommand.cs
│ │ │ │ ├── CreateProductCommandHandler.cs
│ │ │ │ └── CreateProductCommandValidator.cs
│ │ │ └── UpdateProduct/
│ │ │ │ ...
│ │ │ └── DeleteProduct/
│ │ │ │ ...
│ │ ├── Queries/
│ │ │ ├── GetProductById/
│ │ │ │ ├── GetProductByIdQuery.cs
│ │ │ │ └── GetProductByIdQueryHandler.cs
│ │ │ └── GetAllProducts/
│ │ └── Events/
│ │ ├── ProductCreatedEvent.cs
│ │ └── ProductCreatedEventHandler.cs
│ └── Behaviors/
│ ├── TransactionBehavior.cs
│ └── PerformanceBehavior.cs
├── Domain/
│ └── Entities/
│ └── Product.cs
└── Infrastructure/
└── Repositories/
└── ProductRepository.cs
🛠️ Casos de Uso Comunes
CRUD Completo
Consulta el código de ejemplo incluido que implementa un CRUD completo de productos con:
- ✅ Create (con validación)
- ✅ Read (por ID y listado completo)
- ✅ Update
- ✅ Delete
- ✅ Events
- ✅ Validación automática
- ✅ Manejo de errores
Operaciones Complejas
// Comando con múltiples pasos
public sealed record ProcessOrderCommand(Guid OrderId) : ICommand<OrderResult>;
public sealed class ProcessOrderCommandHandler : ICommandHandler<ProcessOrderCommand, OrderResult>
{
private readonly IOrderRepository _orderRepository;
private readonly IPaymentService _paymentService;
private readonly IInventoryService _inventoryService;
private readonly IPublisher _publisher;
public async Task<Result<OrderResult>> HandleAsync(
ProcessOrderCommand request,
CancellationToken ct)
{
// 1. Obtener orden
var order = await _orderRepository.GetByIdAsync(request.OrderId, ct);
if (order is null)
return Result.Failure<OrderResult>(
Error.NotFound("Order.NotFound", "Order not found")
);
// 2. Verificar inventario
var inventoryCheck = await _inventoryService.CheckAvailabilityAsync(order, ct);
if (!inventoryCheck.IsSuccess)
return Result.Failure<OrderResult>(inventoryCheck.Error);
// 3. Procesar pago
var payment = await _paymentService.ProcessPaymentAsync(order, ct);
if (!payment.IsSuccess)
return Result.Failure<OrderResult>(payment.Error);
// 4. Actualizar orden
order.MarkAsProcessed(payment.Value.TransactionId);
await _orderRepository.UpdateAsync(order, ct);
// 5. Publicar evento
await _publisher.PublishAsync(
new OrderProcessedEvent(order.Id, payment.Value.TransactionId),
ct
);
return Result.Success(new OrderResult(order.Id, order.Status));
}
}
🐛 Solución de Problemas
Handlers no se registran automáticamente
Problema: Los handlers no se encuentran.
Solución: Asegúrate de registrar el assembly correcto:
config.RegisterServicesFromAssembly(typeof(YourHandler).Assembly);
Validación no se ejecuta
Problema: Los validadores no se invocan.
Solución: Verifica que:
- Los validadores estén registrados
- El behavior de validación esté activado
config.RegisterValidatorsFromAssembly(typeof(Program).Assembly);
config.AddDefaultBehaviors(includeValidation: true);
Eventos no se publican
Problema: Los eventos no se disparan.
Solución: Asegúrate de:
- Heredar de
EventAggregator<TKey> - Usar
RaiseEvent()para ir agregando o creando los eventos - Publicar eventos después de guardar:
var events = entity.GetEvents();
entity.ClearEvents();
foreach (var @event in events)
{
await _publisher.PublishAsync(@event, cancellationToken);
}
Errores de inyección de dependencias
Problema: Unable to resolve service for type 'IMediator'
Solución: Llama a AddMediatorX() en Program.cs:
builder.Services.AddMediatorX(config => { ... });
🚀 Mejores Prácticas
1. Separación de Responsabilidades
- Un handler = una responsabilidad
- Comandos para escritura, consultas para lectura
- Mantén los handlers pequeños y enfocados
2. Validación
- Siempre valida en el borde (API/Controller)
- Usa validadores para lógica de validación compleja
- Valida comandos, no consultas (generalmente)
3. Manejo de Errores
- Usa el patrón Result<T> en lugar de excepciones para flujo de negocio
- Reserva excepciones para errores inesperados
- Proporciona códigos de error descriptivos
4. Domain Events
- Usa eventos para efectos secundarios
- Mantén los eventos inmutables (records)
- Publica eventos después de persistir cambios
5. Performance
- Usa
CancellationTokenen todos los métodos async - Implementa caching en consultas frecuentes
- Monitorea performance con behaviors personalizados
6. Testing
- Los handlers son fáciles de testear (solo dependen de interfaces)
- Mockea dependencias en tests unitarios
- Testea behaviors de forma aislada
[Fact]
public async Task Handle_ValidCommand_ReturnsSuccess()
{
// Arrange
var mockRepo = new Mock<IProductRepository>();
var mockLogger = new Mock<ILogger<CreateProductCommandHandler>>();
var handler = new CreateProductCommandHandler(mockRepo.Object, mockLogger.Object);
var command = new CreateProductCommand("Test", "Description", 10.0m, 5);
// Act
var result = await handler.HandleAsync(command, CancellationToken.None);
// Assert
Assert.True(result.IsSuccess);
Assert.NotEqual(Guid.Empty, result.Value);
mockRepo.Verify(x => x.AddAsync(It.IsAny<Product>(), It.IsAny<CancellationToken>()), Times.Once);
}
📄 Licencia
Copyright © 2025 EV. Todos los derechos reservados.
| 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 was computed. 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. |
-
net8.0
- Microsoft.Extensions.DependencyInjection (>= 8.0.1)
- Microsoft.Extensions.Logging (>= 8.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 8.0.0 | 210 | 10/30/2025 |
✨ Características
- 🎯 **Comandos y Consultas (CQRS)** - Separación clara entre operaciones de lectura y escritura.
- 🔄 **Pipeline Behaviors** - Middleware personalizable para logging, validación y más.
- ✅ **Validación Integrada** - Sistema de validación fluido y extensible.
- 📢 **Domain Events** - Manejo robusto de eventos de dominio con notificaciones.
- 🛡️ **Manejo de Excepciones** - Captura automática y gestión centralizada de errores.
- 🎁 **Patrón Result** - Manejo funcional de errores sin excepciones.
- ⚡ **Async/Await** - API completamente asíncrona para máximo rendimiento.
- 📦 **Dependency Injection** - Integración perfecta con el contenedor DI de .NET.
- 🔧 **Altamente Configurable** - Adapta el comportamiento a tus necesidades.
🔐 Seguridad y Privacidad (DataSanitizer)
- ✅ Cumple con GDPR y normativas de privacidad.
- 🚫 Evita exponer credenciales en logs.
- 🐛 Permite debug sin comprometer seguridad.
- 🔍 Detecta automáticamente campos sensibles por nombre.
- 📝 Campos personalizados para datos sensibles: "identification", "password", "token", "key", "secret", "authorization", "connectionstring", "apikey", "clientsecret", "bearer", "credential", "pin", "ssn", "dni", "nif", "passport", "username", "email", "value", "owner", "pwd", "createdby", "updatedby", "deletedby".