MiduX 1.0.0
dotnet add package MiduX --version 1.0.0
NuGet\Install-Package MiduX -Version 1.0.0
<PackageReference Include="MiduX" Version="1.0.0" />
<PackageVersion Include="MiduX" Version="1.0.0" />
<PackageReference Include="MiduX" />
paket add MiduX --version 1.0.0
#r "nuget: MiduX, 1.0.0"
#:package MiduX@1.0.0
#addin nuget:?package=MiduX&version=1.0.0
#tool nuget:?package=MiduX&version=1.0.0
MiduX
Lightweight mediator pipeline for modern .NET applications
MiduX is a minimal and extensible implementation of the Mediator pattern for .NET, promoting a clean separation of concerns using CQRS principles. It simplifies communication between components by handling Commands, Queries, and Notifications through a centralized and pluggable pipeline.
๐ Table of Contents
- Features
- Installation
- Requirements
- Configuration
- Recommended Folder Structure
- Usage Examples
- Exception Handling Middleware
- Unit Testing and Mocking
- Integration with Validation
- Benefits of MiduX
- Contributions
- License
๐ Features
- โ Native support for Commands, Queries, and Notifications
- ๐งฑ Built on the Mediator pattern
- ๐งผ Supports CQRS and Clean Architecture practices
- ๐ Extensible โ easily integrates with validation, logging, caching, etc.
- ๐งช Emphasis on testability and separation of concerns
๐ฆ Installation
Add the MiduX
package to your project via NuGet:
dotnet add package MiduX
Alternatively, add the following to your .csproj
:
<PackageReference Include="MiduX" Version="1.0.0" />
๐ Requirements
- .NET 8 or later
- FluentValidation
- Microsoft.Extensions.DependencyInjection
- Microsoft.Extensions.Logging
โ๏ธ Configuration
In your Program.cs
or Startup.cs
, register your handlers and add the mediator to the dependency injection pipeline:
builder.Services.AddScoped<IRequestHandler<CreateTodoCommand, Guid>, CreateTodoCommandHandler>();
builder.Services.AddScoped<IRequestHandler<GetTodoByIdQuery, TodoDto>, GetTodoByIdQueryHandler>();
builder.Services.AddTransient<INotificationHandler<AlertNotification>, AlertNotificationHandler>();
builder.Services.AddMediator();
Additionally, configure the culture if needed:
using System.Globalization;
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en-US");
๐งฉ Recommended Folder Structure
โโโ src/
โโโ Application/
โ โโโ Commands/
โ โโโ Handlers/
โ โโโ Queries/
โ โโโ Notifications/
โโโ Domain/
โ โโโ Entities/
โโโ Infrastructure/
โ โโโ Handlers/
โโโ WebApi/
โโโ Controllers/
โโโ Middleware/
โ๏ธ Usage Examples
Todo Controller
Commands and Queries
[ApiController]
[Route("api/[controller]")]
public class TodoController : ControllerBase
{
private readonly IMediator _mediator;
public TodoController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetById(Guid id)
{
var query = new GetTodoByIdQuery(id);
var result = await _mediator.Send<GetTodoByIdQuery, TodoDto>(query);
return result != null ? Ok(result) : NotFound();
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateTodoCommand command)
{
var todoId = await _mediator.Send<CreateTodoCommand, Guid>(command);
return CreatedAtAction(nameof(GetById), new { id = todoId }, todoId);
}
}
Creating a Custom Handler
public record CreateTodoCommand(string Title) : IRequest<Guid>;
public class CreateTodoCommandHandler : IRequestHandler<CreateTodoCommand, Guid>
{
public Task<Guid> Handle(CreateTodoCommand request, CancellationToken cancellationToken)
{
var id = Guid.NewGuid();
// Your logic to save the TODO
return Task.FromResult(id);
}
}
๐ Usage Example: Notifications
Sending a Notification (Application Layer)
await _mediator.Publish(new AlertNotification("New todo created"));
Handling the Notification (Infrastructure Layer)
public class AlertNotificationHandler : INotificationHandler<AlertNotification>
{
public Task Handle(AlertNotification notification, CancellationToken cancellationToken)
{
// Notification logic (e.g., logging, sending emails, etc.)
Console.WriteLine($"Alert: {notification.Message}");
return Task.CompletedTask;
}
}
โ ๏ธ Exception Handling Middleware
Use the middleware below to catch exceptions thrown by MiduX, especially ValidationException
:
public class ExceptionHandlerMiddleware(
RequestDelegate next,
ILogger<ExceptionHandlerMiddleware> logger)
{
private readonly ILogger<ExceptionHandlerMiddleware> _logger = logger;
private readonly RequestDelegate _next = next;
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
if (ex is MediatorException mediatorEx && mediatorEx.InnerException is ValidationException validationEx)
{
_logger.LogWarning(ex, "Validation error");
var errors = validationEx.Errors
.GroupBy(e => e.PropertyName)
.ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray());
var problemDetails = new ValidationProblemDetails
{
Title = "Validation error",
Status = StatusCodes.Status400BadRequest,
Instance = context.Request.Path,
Detail = "Some fields did not pass validation.",
Errors = errors
};
context.Response.StatusCode = StatusCodes.Status400BadRequest;
context.Response.ContentType = "application/json";
await context.Response.WriteAsJsonAsync(problemDetails);
}
else
{
_logger.LogError(ex, "Internal server error");
await WriteProblemDetailsAsync(context, StatusCodes.Status500InternalServerError, "Internal server error", "An unexpected error occurred.");
}
}
}
private static async Task WriteProblemDetailsAsync(HttpContext context, int statusCode, string title, string detail)
{
if (!context.Response.HasStarted)
{
context.Response.StatusCode = statusCode;
context.Response.ContentType = "application/json";
var problemDetails = new ProblemDetails
{
Title = title,
Detail = detail,
Status = statusCode,
Instance = context.Request.Path
};
await context.Response.WriteAsJsonAsync(problemDetails);
}
}
}
public class ValidationProblemDetails : ProblemDetails
{
public IDictionary<string, string[]>? Errors { get; set; }
}
Middleware Registration
In Program.cs
:
app.UseMiddleware<ExceptionHandlerMiddleware>();
๐งช Unit Testing and Mocking
Mocking IMediator.Send<TRequest, TResponse>
simplifies unit testing for controllers and services. For example, using Moq:
[Fact]
public async Task GetById_ShouldReturnTodo_WhenFound()
{
var mediatorMock = new Mock<IMediator>();
var fakeTodo = new TodoDto { Id = Guid.NewGuid(), Title = "Test Todo" };
mediatorMock.Setup(m => m.Send<GetTodoByIdQuery, TodoDto>(It.IsAny<GetTodoByIdQuery>(), default))
.ReturnsAsync(fakeTodo);
var controller = new TodoController(mediatorMock.Object);
var result = await controller.GetById(fakeTodo.Id);
Assert.IsType<OkObjectResult>(result);
}
โ Integration with Validation
MiduX integrates seamlessly with libraries like FluentValidation. For instance, create a validator:
public class CreateTodoCommandValidator : AbstractValidator<CreateTodoCommand>
{
public CreateTodoCommandValidator()
{
RuleFor(x => x.Title).NotEmpty();
}
}
When using the validation pipeline behavior, any validation errors will be caught and processed by the middleware.
๐ก Benefits of MiduX
- Clear separation between read and write operations (CQRS)
- Low coupling between application components
- Built-in validation via exceptions
- Asynchronous and parallel notifications
- Easily extensible and integrable with other libraries
๐ค Contributing
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch
- Submit a PR with tests and documentation
- Join our Discussions
๐ License
MIT License - Free for commercial and personal use. See LICENSE for details.
Made with โค๏ธ by Silvano ยท Report Issue
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
- FluentValidation (>= 11.11.0)
- Microsoft.Extensions.DependencyInjection (>= 9.0.3)
- Microsoft.Extensions.Logging (>= 9.0.3)
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 |
---|---|---|
1.0.0 | 139 | 4/5/2025 |
Initial release of the framework featuring support for pipeline behaviors, handler resolution, and integration with popular libraries like FluentValidation and Microsoft.Extensions.DependencyInjection.