MiduX 1.0.0

dotnet add package MiduX --version 1.0.0
                    
NuGet\Install-Package MiduX -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MiduX" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MiduX" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="MiduX" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MiduX --version 1.0.0
                    
#r "nuget: MiduX, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MiduX@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MiduX&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=MiduX&version=1.0.0
                    
Install as a Cake Tool

MiduX

NuGet Downloads License: MIT

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

  • โœ… 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");

โ””โ”€โ”€ 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:

  1. Fork the repository
  2. Create a feature branch
  3. Submit a PR with tests and documentation
  4. Join our Discussions

๐Ÿ“œ License

MIT License - Free for commercial and personal use. See LICENSE for details.


Made with โค๏ธ by Silvano ยท Report Issue

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.