Skip to content

Mediary is a minimal, open-source library for .NET that implements the Request/Handler (Mediator) pattern with pipeline support — built from scratch with no external dependencies.

License

Notifications You must be signed in to change notification settings

facus26/Mediary

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mediary — Lightweight Request Dispatcher for .NET

Mediary is a minimal, open-source library for .NET that implements the Request/Handler (Mediator) pattern with pipeline support — inspired by MediatR, but built from scratch with no external dependencies.

Clean request handling, extensible pipeline behaviors, and a DI-friendly architecture — all with zero external dependencies.

Build codecov License: MIT .NET NuGet

🚨 Why Mediary?

Mediary is a lightweight request dispatcher for .NET designed to offer a clean, extensible, and dependency-free alternative to more complex mediators.

It focuses on performance, clarity, and developer control, while maintaining compatibility with the .NET dependency injection ecosystem.

✅ Key benefits

  • Lightweight and fast — no unnecessary overhead or runtime reflection
  • 🧩 Extensible pipeline behaviors — clean middleware-style request handling
  • 🧼 Minimalist design — no external dependencies, no magic
  • 🧪 Test-friendly — everything is composable and DI-compatible
  • 📦 NuGet-ready — simple to install and integrate

🧭 Table of Contents


📦 Installation

You can install Mediary via NuGet:

dotnet add package Mediary

Or via the NuGet UI in Visual Studio by searching for Mediary.

📦 Available at: https://www.nuget.org/packages/Mediary


🚀 Features


🔧 Usage

1. Define a Request

public class GetAllPlansQuery : IQuery<List<PlanDto>> { }

2. Create a Handler

public class GetAllPlansHandler : IRequestHandler<List<PlanDto>, GetAllPlansQuery>
{
    public Task<List<PlanDto>> HandleAsync(GetAllPlansQuery request)
    {
        // Your logic here
    }
}

3. Inject the Dispatcher

public class PlanService
{
    private readonly IRequestDispatcher _dispatcher;

    public PlanService(IRequestDispatcher dispatcher) =>
        _dispatcher = dispatcher;

    public Task<List<PlanDto>> GetPlansAsync() =>
        _dispatcher.DispatchAsync<GetAllPlansQuery, List<PlanDto>>(new GetAllPlansQuery());
}

🛠 Dependency Injection

Mediary supports flexible registration depending on your needs:

✅ Option 1 — Auto-registration (Recommended)

services.AddMediary()
    .AddRequestHandlersFromAssembly(typeof(Program).Assembly)
    .AddPipelineBehaviorsFromAssembly(typeof(Program).Assembly);

This will:

🔧 Option 2 — Semi-manual (Builder API)

If you prefer full control but want to use the builder pattern:

services.AddMediary()
    .AddRequestHandler<List<PlanDto>, GetAllPlansQuery, GetAllPlansHandler>()
    .AddPipelineBehaviors<List<PlanDto>, GetAllPlansQuery, LoggingBehavior<List<PlanDto>, GetAllPlansQuery>>();

You can also override the dispatcher:

services.AddMediary<CustomDispatcher>();

🧩 Option 3 — Fully manual

If you want zero coupling to Mediary’s builder or extension methods, you can register everything manually:

services.AddScoped<IRequestDispatcher, RequestDispatcher>();
services.AddScoped<IRequestHandler<List<PlanDto>, GetAllPlansQuery>, GetAllPlansHandler>();
services.AddScoped<IRequestPipelineBehavior<List<PlanDto>, GetAllPlansQuery>, LoggingBehavior<List<PlanDto>, GetAllPlansQuery>>();

This gives you maximum flexibility and full control over dependency injection.


🔍 Built-in Behaviors

Interface Description
LoggingBehavior<TResponse, TRequest> Logs the start and end of a request using ILogger.
PerformanceBehavior<TResponse, TRequest> Measures and logs execution time of each request.

✅ Register pipeline behaviors globally

You can register the logging and performance behaviors globally in two ways:

1. Using the MediaryBuilder

services.AddMediary()
    .AddOpenPipelineBehaviors(typeof(LoggingBehavior<,>))
    .AddOpenPipelineBehaviors(typeof(PerformanceBehavior<,>));

2. Manual registration

services.AddScoped(typeof(IRequestPipelineBehavior<,>), typeof(LoggingBehavior<,>));
services.AddScoped(typeof(IRequestPipelineBehavior<,>), typeof(PerformanceBehavior<,>));

This will ensure that both behaviors are applied to all requests automatically.


✅ Core Interfaces

Interface Description
IRequest<TResponse> Base request contract used for dispatching
IRequestHandler<TResponse, TRequest> Handles a specific request and returns a response
IRequestPipelineBehavior<TResponse, TRequest> Defines pipeline logic before/after the handler
IRequestDispatcher Responsible for dispatching requests through the pipeline

⚙️ Handling Commands Without Return Values

Mediary uses a single interface: IRequest<TResponse>.
When you don’t need to return data from a command, you can use semantic result types to indicate intent:

✅ Built-in Result Types

Type Purpose
Unit Used when no response is needed (replaces void)
Success Indicates a generic successful result
Created Indicates a resource was created
Updated Indicates a resource was updated
Deleted Indicates a resource was deleted

These types are lightweight readonly structs defined in Mediary.Core.Results.

Example: Command with no output

public class ClearCacheCommand : IRequest<Unit> { }

public class ClearCacheHandler : IRequestHandler<Unit, ClearCacheCommand>
{
    public Task<Unit> HandleAsync(ClearCacheCommand request) =>
        Task.FromResult(Unit.Value);
}

Example: Command with semantic return

public class CreateUserCommand : IRequest<Created> { }

public class CreateUserHandler : IRequestHandler<Created, CreateUserCommand>
{
    public Task<Created> HandleAsync(CreateUserCommand request) =>
        Task.FromResult(Created.Value);
}

🔖 Request Metadata

You can optionally annotate your request types with descriptive metadata using the [RequestInfo] attribute:

[RequestInfo("Creates a new user", "Command", "Users")]
public class CreateUserCommand : ICommand<Guid> { }

This helps document the purpose of the request and can be consumed by logging, debugging, or inspection tools.

Both built-in behaviors (LoggingBehavior and PerformanceBehavior) will use this description if available.

This metadata is also accessible from within handlers or behaviors via extension methods.

👉 See RequestInfoAttribute in the source.


🪪 Aliases

For semantic clarity, Mediary exposes aliases for intent-based request types:

Alias Inherits Purpose
IQuery<TResponse> IRequest<TResponse> Read-only request
ICommand<TResponse> IRequest<TResponse> Write command

Aliases are optional and meant to improve readability:

// Represents: GET /users/{id}
public class GetUserByIdQuery : IQuery<UserDto> { }

// Represents: POST /users
public class CreateUserCommand : ICommand<UserDto> { }

📄 License

MIT License — Free for personal and commercial use.


🙌 Contributing

Open to feedback, ideas, PRs and improvements — feel free to fork and collaborate.


👨‍💻 Author

Built with ❤️ by Facundo JuarezGitHub

About

Mediary is a minimal, open-source library for .NET that implements the Request/Handler (Mediator) pattern with pipeline support — built from scratch with no external dependencies.

Resources

License

Stars

Watchers

Forks

Languages