Skip to content

Simple loosely implementation of the Mediator pattern for simple scenarios

License

Notifications You must be signed in to change notification settings

marcusackert/simple-mediator

Repository files navigation

SimpleMediator

CI/CD Pipeline Code Quality Security Scan Test Coverage

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.

✨ Features

  • 🎯 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

πŸ“ Project Structure

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

πŸš€ Quick Start

1. Define Your Request and Response Models

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; } = "";
}

2. Implement Request Handlers

// 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}");
    }
}

3. Configure the Mediator

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();

4. Use the Mediator

// 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);

πŸ”§ ASP.NET Core Integration

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();

🧩 Pipeline Behaviors

SimpleMediator supports pipeline behaviors for cross-cutting concerns:

Built-in Behaviors

Logging Behavior

// Logs request/response automatically
.AddBehavior(new LoggingBehavior<MyRequest, MyResponse>())
.AddBehavior(new LoggingBehavior<MyCommand>()) // For commands

Performance Behavior

// Monitors execution time and logs slow operations
.AddBehavior(new PerformanceBehavior<MyRequest, MyResponse>())

Validation Behavior

// Validates requests before processing
.AddBehavior(new ValidationBehavior<MyRequest, MyResponse>())

Custom Behaviors

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>())

πŸ“š API Reference

Core Classes

Mediator

The main mediator class that coordinates request handling through a pipeline.

Methods:

  • Task<TResponse?> OperationAsync<TRequest, TResponse>(TRequest request) - Handle request-response pattern
  • Task OperationAsync<TRequest>(TRequest request) - Handle command pattern
  • TResponse? Operation<TRequest, TResponse>(TRequest request) - Synchronous request-response
  • void Operation<TRequest>(TRequest request) - Synchronous command handling

MediatorBuilder

Fluent builder for configuring the mediator with handlers and behaviors.

Methods:

  • AddHandler<TRequest, TResponse>(IRequestHandler<TRequest, TResponse> handler) - Register query handler
  • AddHandler<TRequest>(IRequestHandler<TRequest> handler) - Register command handler
  • AddBehavior<TRequest, TResponse>(IPipelineBehavior<TRequest, TResponse> behavior) - Add pipeline behavior
  • AddBehavior<TRequest>(IPipelineBehavior<TRequest> behavior) - Add command behavior
  • Build() - Create configured mediator instance

Pipeline

Internal pipeline that processes requests through registered behaviors and handlers.

Interfaces

IRequestHandler<TRequest, TResponse>

Interface for handlers that process queries and return responses.

public interface IRequestHandler<TRequest, TResponse>
{
    Task<TResponse> HandleAsync(TRequest request);
}

IRequestHandler<TRequest>

Interface for handlers that process commands without returning responses.

public interface IRequestHandler<TRequest>
{
    Task HandleAsync(TRequest request);
}

IPipelineBehavior<TRequest, TResponse>

Interface for implementing cross-cutting concerns in the pipeline.

public interface IPipelineBehavior<TRequest, TResponse>
{
    Task<TResponse> HandleAsync(TRequest request, Func<TRequest, Task<TResponse>> next);
}

IPipelineBehavior<TRequest>

Interface for implementing cross-cutting concerns for commands.

public interface IPipelineBehavior<TRequest>
{
    Task HandleAsync(TRequest request, Func<TRequest, Task> next);
}

πŸƒβ€β™‚οΈ Running the Project

Prerequisites

  • .NET 9.0 SDK or later
  • Visual Studio 2022, VS Code, or any .NET compatible IDE

Clone and Build

git clone https://github.com/marcusackert/simple-mediator.git
cd simple-mediator
dotnet build

Running the Sample Application

# 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

Running Tests

# Run all tests
dotnet test

# Run with coverage
dotnet test --collect:"XPlat Code Coverage"

# Run specific test project
dotnet test tests/SimpleMediator.Tests

Building for Release

dotnet build --configuration Release
dotnet pack --configuration Release

πŸ“Š Test Coverage

The 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 ./coverage

🎯 Examples

Complete Console Application Example

using 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));
    }
}

Sample Web Application

The samples/WebApp project demonstrates:

  1. ASP.NET Core Integration: Complete setup with DI container
  2. Request/Response Pattern: GET endpoint using queries
  3. Command Pattern: POST endpoint for commands
  4. Pipeline Behaviors: Logging and performance monitoring
  5. Error Handling: Graceful error responses

Visit the sample at: http://localhost:5000

πŸ—οΈ Architecture

SimpleMediator implements a clean, extensible architecture following SOLID principles:

Core Architecture 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 Flow

Request β†’ Pipeline β†’ Behaviors β†’ Handler β†’ Response
    ↓         ↓          ↓         ↓         ↓
 Validation β†’ Logging β†’ Perf β†’ Business β†’ Transform
  1. Request Entry: Request enters through Mediator.OperationAsync()
  2. Pipeline Processing: Pipeline coordinates the request flow
  3. Behavior Chain: Each behavior processes the request in sequence
  4. Handler Execution: Appropriate handler processes the business logic
  5. Response Flow: Response flows back through behaviors for post-processing

Key Benefits

  • πŸš€ 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

🀝 Contributing

We welcome contributions! Here's how to get started:

Development Setup

  1. Fork and Clone

    git clone https://github.com/yourusername/simple-mediator.git
    cd simple-mediator
  2. Install Dependencies

    dotnet restore
  3. Run Tests

    dotnet test

Contribution Guidelines

  1. πŸ”§ Code Quality: Ensure all tests pass and maintain code coverage
  2. πŸ“ Documentation: Update README and code comments for new features
  3. βœ… Testing: Add comprehensive unit tests for new functionality
  4. 🎨 Formatting: Follow existing code style (use dotnet format)
  5. πŸ“‹ Pull Requests: Create detailed PRs with clear descriptions

Areas for Contribution

  • πŸ†• Additional built-in behaviors (caching, circuit breaker, etc.)
  • πŸ“Š Performance optimizations
  • πŸ“š Documentation improvements
  • πŸ§ͺ Additional test scenarios
  • πŸ’‘ Example projects and use cases

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ†˜ Support

  • πŸ“– 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

🎯 Roadmap

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!

About

Simple loosely implementation of the Mediator pattern for simple scenarios

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages