A lightweight, high-performance implementation of the Mediator pattern for .NET applications. This library provides a clean way to decouple business logic through request/response handling, command processing, and a flexible pipeline architecture with behavior support.
- π― Mediator Pattern: Clean separation of concerns with type-safe request handling
- π Pipeline Architecture: Extensible pipeline with behavior support for cross-cutting concerns
- π Request/Response & Commands: Support for both query (request-response) and command patterns
- π§ Flexible Behaviors: Built-in logging, validation, and performance behaviors
- β‘ High Performance: Minimal overhead with async/await support throughout
- ποΈ Builder Pattern: Fluent API for easy configuration and registration
- π§ͺ Comprehensive Testing: 82 unit tests with full coverage
- π¦ .NET 9.0: Built on the latest .NET framework
simple-mediator/
βββ src/
β βββ SimpleMediator/ # Core mediator library
β βββ Core/ # Core abstractions and implementations
β β βββ Mediator.cs # Main mediator class
β β βββ Pipeline.cs # Request processing pipeline
β β βββ MediatorBuilder.cs # Fluent builder for mediator setup
β β βββ IRequestHandler.cs # Handler interfaces
β β βββ IPipelineBehavior.cs # Behavior interfaces
β βββ Behaviors/ # Built-in pipeline behaviors
β β βββ LoggingBehavior.cs # Request/response logging
β β βββ ValidationBehavior.cs # Input validation
β β βββ PerformanceBehavior.cs # Performance monitoring
β βββ SimpleMediator.csproj # Library project file
βββ tests/
β βββ SimpleMediator.Tests/ # Comprehensive unit tests
β βββ MediatorTests.cs # Core mediator functionality tests
β βββ PipelineTests.cs # Pipeline behavior tests
β βββ MediatorBuilderTests.cs # Builder pattern tests
β βββ CommonBehaviorsTests.cs # Built-in behaviors tests
β βββ SimpleMediator.Tests.csproj # Test project file
βββ samples/
β βββ WebApp/ # Example ASP.NET Core application
β βββ Program.cs # Web app with mediator integration
β βββ TestRequest.cs # Example request/handler implementation
β βββ WebApp.csproj # Sample app project file
βββ .github/workflows/ # CI/CD pipeline definitions
β βββ ci.yml # Build and test pipeline
β βββ code-quality.yml # Code formatting and analysis
β βββ security.yml # Security and dependency scanning
β βββ coverage.yml # Test coverage reporting
β βββ release.yml # Automated releases
βββ SimpleMediator.sln # Solution file
βββ README.md # This file
using SimpleMediator.Core;
public class GetUserQuery
{
public int UserId { get; set; }
}
public class UserResponse
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
}
public class CreateUserCommand
{
public string Name { get; set; } = "";
public string Email { get; set; } = "";
}// Query handler (request-response pattern)
public class GetUserHandler : IRequestHandler<GetUserQuery, UserResponse>
{
public async Task<UserResponse> HandleAsync(GetUserQuery request)
{
// Simulate database call
await Task.Delay(10);
return new UserResponse
{
Id = request.UserId,
Name = "John Doe",
Email = "john@example.com"
};
}
}
// Command handler (no response)
public class CreateUserHandler : IRequestHandler<CreateUserCommand>
{
public async Task HandleAsync(CreateUserCommand command)
{
// Simulate user creation
await Task.Delay(50);
Console.WriteLine($"User {command.Name} created with email {command.Email}");
}
}using SimpleMediator.Core;
using SimpleMediator.Behaviors;
// Build mediator with handlers and behaviors
var mediator = new MediatorBuilder()
.AddHandler(new GetUserHandler())
.AddHandler(new CreateUserHandler())
.AddBehavior(new LoggingBehavior<GetUserQuery, UserResponse>())
.AddBehavior(new PerformanceBehavior<CreateUserCommand>())
.Build();// Execute query
var userQuery = new GetUserQuery { UserId = 123 };
var user = await mediator.OperationAsync<GetUserQuery, UserResponse>(userQuery);
Console.WriteLine($"Retrieved user: {user.Name}");
// Execute command
var createCommand = new CreateUserCommand
{
Name = "Jane Smith",
Email = "jane@example.com"
};
await mediator.OperationAsync(createCommand);Add SimpleMediator to your ASP.NET Core application:
using SimpleMediator.Core;
using SimpleMediator.Behaviors;
var builder = WebApplication.CreateBuilder(args);
// Register mediator with DI container
builder.Services.AddSingleton(serviceProvider =>
{
return new MediatorBuilder()
.AddHandler(new GetUserHandler())
.AddHandler(new CreateUserHandler())
.AddBehavior(new LoggingBehavior<GetUserQuery, UserResponse>())
.AddBehavior(new ValidationBehavior<CreateUserCommand>())
.Build();
});
var app = builder.Build();
// Use in controllers or minimal APIs
app.MapGet("/users/{id:int}", async (int id, Mediator mediator) =>
{
var query = new GetUserQuery { UserId = id };
var user = await mediator.OperationAsync<GetUserQuery, UserResponse>(query);
return Results.Ok(user);
});
app.MapPost("/users", async (CreateUserCommand command, Mediator mediator) =>
{
await mediator.OperationAsync(command);
return Results.Created();
});
app.Run();SimpleMediator supports pipeline behaviors for cross-cutting concerns:
// Logs request/response automatically
.AddBehavior(new LoggingBehavior<MyRequest, MyResponse>())
.AddBehavior(new LoggingBehavior<MyCommand>()) // For commands// Monitors execution time and logs slow operations
.AddBehavior(new PerformanceBehavior<MyRequest, MyResponse>())// Validates requests before processing
.AddBehavior(new ValidationBehavior<MyRequest, MyResponse>())Create your own behaviors by implementing IPipelineBehavior<TRequest, TResponse>:
public class RetryBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
public async Task<TResponse> HandleAsync(
TRequest request,
Func<TRequest, Task<TResponse>> next)
{
const int maxRetries = 3;
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
return await next(request);
}
catch (Exception) when (attempt < maxRetries)
{
await Task.Delay(1000 * attempt); // Exponential backoff
}
}
return await next(request); // Final attempt
}
}
// Register the custom behavior
.AddBehavior(new RetryBehavior<MyRequest, MyResponse>())The main mediator class that coordinates request handling through a pipeline.
Methods:
Task<TResponse?> OperationAsync<TRequest, TResponse>(TRequest request)- Handle request-response patternTask OperationAsync<TRequest>(TRequest request)- Handle command patternTResponse? Operation<TRequest, TResponse>(TRequest request)- Synchronous request-responsevoid Operation<TRequest>(TRequest request)- Synchronous command handling
Fluent builder for configuring the mediator with handlers and behaviors.
Methods:
AddHandler<TRequest, TResponse>(IRequestHandler<TRequest, TResponse> handler)- Register query handlerAddHandler<TRequest>(IRequestHandler<TRequest> handler)- Register command handlerAddBehavior<TRequest, TResponse>(IPipelineBehavior<TRequest, TResponse> behavior)- Add pipeline behaviorAddBehavior<TRequest>(IPipelineBehavior<TRequest> behavior)- Add command behaviorBuild()- Create configured mediator instance
Internal pipeline that processes requests through registered behaviors and handlers.
Interface for handlers that process queries and return responses.
public interface IRequestHandler<TRequest, TResponse>
{
Task<TResponse> HandleAsync(TRequest request);
}Interface for handlers that process commands without returning responses.
public interface IRequestHandler<TRequest>
{
Task HandleAsync(TRequest request);
}Interface for implementing cross-cutting concerns in the pipeline.
public interface IPipelineBehavior<TRequest, TResponse>
{
Task<TResponse> HandleAsync(TRequest request, Func<TRequest, Task<TResponse>> next);
}Interface for implementing cross-cutting concerns for commands.
public interface IPipelineBehavior<TRequest>
{
Task HandleAsync(TRequest request, Func<TRequest, Task> next);
}- .NET 9.0 SDK or later
- Visual Studio 2022, VS Code, or any .NET compatible IDE
git clone https://github.com/marcusackert/simple-mediator.git
cd simple-mediator
dotnet build# Navigate to the sample app
cd samples/WebApp
dotnet run
# Or run from solution root
dotnet run --project samples/WebApp
# Visit http://localhost:5000 to see the sample in action# Run all tests
dotnet test
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"
# Run specific test project
dotnet test tests/SimpleMediator.Testsdotnet build --configuration Release
dotnet pack --configuration ReleaseThe project maintains high test coverage with 82 comprehensive unit tests covering:
- β Core mediator functionality
- β Pipeline behavior execution
- β Handler registration and resolution
- β Error handling and edge cases
- β Builder pattern validation
- β Built-in behaviors (logging, validation, performance)
- β Async/await patterns
- β Thread safety
Run tests with coverage reporting:
dotnet test --collect:"XPlat Code Coverage" --results-directory ./coverageusing SimpleMediator.Core;
using SimpleMediator.Behaviors;
// Define models
public record GetWeatherQuery(string City);
public record WeatherResponse(string City, int Temperature, string Condition);
public record LogWeatherCommand(string City, int Temperature);
// Implement handlers
public class GetWeatherHandler : IRequestHandler<GetWeatherQuery, WeatherResponse>
{
public async Task<WeatherResponse> HandleAsync(GetWeatherQuery request)
{
// Simulate API call
await Task.Delay(100);
return new WeatherResponse(request.City, Random.Shared.Next(-10, 35), "Sunny");
}
}
public class LogWeatherHandler : IRequestHandler<LogWeatherCommand>
{
public async Task HandleAsync(LogWeatherCommand command)
{
await Task.Delay(50);
Console.WriteLine($"Logged weather for {command.City}: {command.Temperature}Β°C");
}
}
// Program entry point
class Program
{
static async Task Main(string[] args)
{
// Configure mediator
var mediator = new MediatorBuilder()
.AddHandler(new GetWeatherHandler())
.AddHandler(new LogWeatherHandler())
.AddBehavior(new LoggingBehavior<GetWeatherQuery, WeatherResponse>())
.AddBehavior(new PerformanceBehavior<GetWeatherQuery, WeatherResponse>())
.Build();
// Use mediator
var weather = await mediator.OperationAsync<GetWeatherQuery, WeatherResponse>(
new GetWeatherQuery("London"));
Console.WriteLine($"Weather in {weather.City}: {weather.Temperature}Β°C, {weather.Condition}");
await mediator.OperationAsync(
new LogWeatherCommand(weather.City, weather.Temperature));
}
}The samples/WebApp project demonstrates:
- ASP.NET Core Integration: Complete setup with DI container
- Request/Response Pattern: GET endpoint using queries
- Command Pattern: POST endpoint for commands
- Pipeline Behaviors: Logging and performance monitoring
- Error Handling: Graceful error responses
Visit the sample at: http://localhost:5000
SimpleMediator implements a clean, extensible architecture following SOLID principles:
- π Mediator Pattern: Eliminates direct dependencies between components
- π Single Responsibility: Each handler focuses on one specific operation
- π Open/Closed: Easy to extend with new handlers and behaviors
- π Dependency Inversion: Depends on abstractions, not implementations
- π§© Pipeline Architecture: Flexible behavior composition for cross-cutting concerns
Request β Pipeline β Behaviors β Handler β Response
β β β β β
Validation β Logging β Perf β Business β Transform
- Request Entry: Request enters through
Mediator.OperationAsync() - Pipeline Processing:
Pipelinecoordinates the request flow - Behavior Chain: Each behavior processes the request in sequence
- Handler Execution: Appropriate handler processes the business logic
- Response Flow: Response flows back through behaviors for post-processing
- π Performance: Minimal overhead with efficient request routing
- π§ͺ Testability: Easy to unit test handlers and behaviors in isolation
- π§ Maintainability: Clear separation of concerns and dependencies
- π Scalability: Simple to add new features without modifying existing code
- π‘οΈ Reliability: Consistent error handling and behavior execution
We welcome contributions! Here's how to get started:
-
Fork and Clone
git clone https://github.com/yourusername/simple-mediator.git cd simple-mediator -
Install Dependencies
dotnet restore
-
Run Tests
dotnet test
- π§ Code Quality: Ensure all tests pass and maintain code coverage
- π Documentation: Update README and code comments for new features
- β Testing: Add comprehensive unit tests for new functionality
- π¨ Formatting: Follow existing code style (use
dotnet format) - π Pull Requests: Create detailed PRs with clear descriptions
- π Additional built-in behaviors (caching, circuit breaker, etc.)
- π Performance optimizations
- π Documentation improvements
- π§ͺ Additional test scenarios
- π‘ Example projects and use cases
This project is licensed under the MIT License - see the LICENSE file for details.
- π Documentation: Check this README and code comments
- π Issues: Create an issue for bugs or feature requests
- π¬ Discussions: Use GitHub Discussions for questions and ideas
- π§ Contact: Reach out via GitHub for any other inquiries
Future enhancements planned:
- Dependency Injection Integration: First-class DI container support
- Additional Behaviors: Caching, circuit breaker, rate limiting
- Performance Optimizations: Source generators for handler registration
- Metrics Integration: Built-in metrics and telemetry support
- Documentation Site: Comprehensive documentation website
β Star this repository if you find SimpleMediator helpful for your projects!