Skip to content

TanNguyenNet/Mediator

Repository files navigation

Mediator

.NET 8.0 License: MIT Tests

High-performance Mediator pattern implementation for .NET with CQRS support. Inspired by MediatR with focus on performance and simplicity.

✨ Features

  • πŸš€ High Performance - Static caching, minimal allocations, aggressive inlining
  • πŸ“¦ CQRS Ready - Request/Response, Commands, Queries, and Notifications
  • πŸ”Œ Pipeline Behaviors - Cross-cutting concerns (logging, validation, caching)
  • ⚑ Streaming Support - IAsyncEnumerable for large datasets
  • πŸ›‘οΈ Validation - Built-in validation behavior with custom validators
  • 🎯 Pre/Post Processors - Execute logic before/after handlers
  • πŸ’‰ DI Integration - Native Microsoft.Extensions.DependencyInjection support

πŸ“¦ Installation

dotnet add package Mediator
dotnet add package Mediator.Extensions.DependencyInjection

πŸš€ Quick Start

1. Define a Request and Handler

// Request
public record GetUserQuery(int UserId) : IRequest<UserDto>;

// Handler
public class GetUserHandler : IRequestHandler<GetUserQuery, UserDto>
{
    public async Task<UserDto> Handle(GetUserQuery request, CancellationToken ct)
    {
        return new UserDto(request.UserId, "John Doe");
    }
}

2. Register Services

services.AddMediator(config =>
{
    config.AddAssembly(typeof(Program).Assembly);
});

3. Send Requests

public class UserController
{
    private readonly IMediator _mediator;

    public UserController(IMediator mediator) => _mediator = mediator;

    public async Task<UserDto> GetUser(int id)
    {
        return await _mediator.Send(new GetUserQuery(id));
    }
}

πŸ“– Usage Examples

Commands (No Response)

public record CreateUserCommand(string Name, string Email) : IRequest;

public class CreateUserHandler : IRequestHandler<CreateUserCommand, Unit>
{
    public async Task<Unit> Handle(CreateUserCommand request, CancellationToken ct)
    {
        // Create user logic
        return Unit.Value;
    }
}

Notifications (Pub/Sub)

public record UserCreated(int UserId, string Name) : INotification;

public class SendWelcomeEmail : INotificationHandler<UserCreated>
{
    public async Task Handle(UserCreated notification, CancellationToken ct)
    {
        // Send email
    }
}

public class LogUserCreated : INotificationHandler<UserCreated>
{
    public async Task Handle(UserCreated notification, CancellationToken ct)
    {
        // Log event
    }
}

// Usage
await _mediator.Publish(new UserCreated(1, "John"));

Streaming (IAsyncEnumerable)

public record GetLogsStream(DateTime From) : IStreamRequest<LogEntry>;

public class GetLogsHandler : IStreamRequestHandler<GetLogsStream, LogEntry>
{
    public async IAsyncEnumerable<LogEntry> Handle(
        GetLogsStream request,
        [EnumeratorCancellation] CancellationToken ct)
    {
        await foreach (var log in _db.GetLogsAsync(request.From, ct))
        {
            yield return log;
        }
    }
}

Pipeline Behaviors

public class LoggingBehavior<TRequest, TResponse> 
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : notnull
{
    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken ct)
    {
        _logger.LogInformation("Handling {Request}", typeof(TRequest).Name);
        
        var response = await next();
        
        _logger.LogInformation("Handled {Request}", typeof(TRequest).Name);
        return response;
    }
}

// Registration
services.AddMediator(config =>
{
    config.AddAssembly(typeof(Program).Assembly);
    config.AddBehavior(typeof(LoggingBehavior<,>));
});

Validation

public class CreateUserValidator : IValidator<CreateUserCommand>
{
    public Task<ValidationResult> ValidateAsync(
        CreateUserCommand request, 
        CancellationToken ct)
    {
        if (string.IsNullOrEmpty(request.Name))
            return Task.FromResult(
                ValidationResult.Failure("Name", "Name is required"));
        
        return Task.FromResult(ValidationResult.Success);
    }
}

// Registration
services.AddMediator(config =>
{
    config.AddAssembly(typeof(Program).Assembly);
    config.AddBehavior(typeof(ValidationBehavior<,>));
});
services.AddTransient<IValidator<CreateUserCommand>, CreateUserValidator>();

βš™οΈ Configuration

services.AddMediator(config =>
{
    // Scan assemblies for handlers
    config.AddAssembly(typeof(Program).Assembly);
    
    // Configure lifetimes
    config.MediatorLifetime = ServiceLifetime.Scoped;
    config.HandlerLifetime = ServiceLifetime.Transient;
    config.BehaviorLifetime = ServiceLifetime.Transient;
    
    // Add behaviors (executed in order)
    config.AddBehavior(typeof(LoggingBehavior<,>));
    config.AddBehavior(typeof(ValidationBehavior<,>));
    config.AddBehavior(typeof(RequestPreProcessorBehavior<,>));
    config.AddBehavior(typeof(RequestPostProcessorBehavior<,>));
});

πŸ§ͺ Testing

The library includes 79 comprehensive unit tests covering:

  • βœ… Request/Response handling
  • βœ… Notifications (multiple handlers)
  • βœ… Pipeline behaviors
  • βœ… Pre/Post processors
  • βœ… Validation
  • βœ… Streaming with IAsyncEnumerable
  • βœ… Cancellation support
  • βœ… Exception handling
cd tests/Mediator.Tests
dotnet test

πŸ“Š Project Structure

Mediator/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ Mediator/
β”‚   β”‚   β”œβ”€β”€ Abstractions/          # Interfaces
β”‚   β”‚   β”œβ”€β”€ Behaviors/             # Pipeline behaviors
β”‚   β”‚   β”œβ”€β”€ Exceptions/            # Custom exceptions
β”‚   β”‚   β”œβ”€β”€ Validation/            # Validation support
β”‚   β”‚   β”œβ”€β”€ Wrappers/              # Handler wrappers
β”‚   β”‚   β”œβ”€β”€ Mediator.cs            # Main implementation
β”‚   β”‚   └── Unit.cs                # Unit type for void returns
β”‚   └── Mediator.Extensions.DependencyInjection/
β”‚       β”œβ”€β”€ MediatorServiceConfiguration.cs
β”‚       └── ServiceCollectionExtensions.cs
└── tests/
    └── Mediator.Tests/            # Unit tests

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

πŸ“„ License

This project is licensed under the MIT License.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages