Fluentish.Mediator.Generator
1.0.2
dotnet add package Fluentish.Mediator.Generator --version 1.0.2
NuGet\Install-Package Fluentish.Mediator.Generator -Version 1.0.2
<PackageReference Include="Fluentish.Mediator.Generator" Version="1.0.2" />
<PackageVersion Include="Fluentish.Mediator.Generator" Version="1.0.2" />
<PackageReference Include="Fluentish.Mediator.Generator" />
paket add Fluentish.Mediator.Generator --version 1.0.2
#r "nuget: Fluentish.Mediator.Generator, 1.0.2"
#:package Fluentish.Mediator.Generator@1.0.2
#addin nuget:?package=Fluentish.Mediator.Generator&version=1.0.2
#tool nuget:?package=Fluentish.Mediator.Generator&version=1.0.2
Fluentish.Mediator
Fluentish.Mediator is a high-performance, source-generated mediator implementation for .NET.
Unlike traditional mediator libraries that rely on runtime reflection and service location pattern matching, Fluentish.Mediator moves the dispatch logic to compile-time. It generates strongly typed implementations of your module interfaces, providing immediate build-time feedback on missing handlers and ensuring zero-overhead dispatch at runtime.
Design Philosophy
This library prioritizes correctness, explicitness, and performance over "magic" or loose coupling via generic containers.
- Compile-Time Safety: Relationships between Requests, Handlers, and Modules are validated during the build. If a request does not have a registered handler, the project will not compile.
- Explicit Dependencies: Instead of injecting a generic
IMediatorthat hides what a class actually does, you inject specific Modules (e.g.,IUserModule). This makes architectural boundaries and dependencies explicit in your constructors. - Zero Runtime Overhead: The library generates direct method calls to handlers. There is no
MakeGenericType,Invoke, or dictionary lookups occurring during the "Send" phase. - AOT Compatibility: Because it avoids runtime type scanning and reflection-based instantiation, it is naturally compatible with Native AOT.
Installation
Install the core library and the source generator via NuGet. You need both packages to enable the runtime types and the compile-time generation features.
Using the .NET CLI:
dotnet add package Fluentish.Mediator
dotnet add package Fluentish.Mediator.Generator
Or via PackageReference in your project file:
<ItemGroup>
<PackageReference Include="Fluentish.Mediator" Version="1.0.0" />
<PackageReference Include="Fluentish.Mediator.Generator" Version="1.0.0" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
Note: The
Fluentish.Mediator.Generatoris a development-only dependency that generates the C# code during compilation.
Architecture Overview
The library organizes logic into Modules. A Module is an interface definition that groups related capabilities. You define the interface, and the Source Generator implements the dispatch logic.
Core Concepts
- Module: An interface inheriting
IMediatorModule. - Request: A POCO implementing
IRequestorIRequest<TResult>. - Handler: A class implementing
IRequestHandler<TModule, TRequest, TResult>. - Registration: A partial method attribute
[RegisterModule]that triggers DI generation.
Usage
1. Define a Module
Create an interface that represents a logical domain boundary. Inherit from IMediatorModule.
using Fluentish.Mediator;
namespace MyDomain.Features.Billing;
public interface IBillingModule : IMediatorModule
{
// The source generator will populate this interface with
// Execute() methods for every request bound to this module.
}
2. Define Requests and Handlers
Bind a handler to a specific module using the generic arguments in IRequestHandler.
public record GenerateInvoiceRequest(Guid OrderId) : IRequest<InvoiceDto>;
public class GenerateInvoiceHandler : IRequestHandler<IBillingModule, GenerateInvoiceRequest, InvoiceDto>
{
public async Task<InvoiceDto> Handle(GenerateInvoiceRequest request)
{
// Business logic...
return new InvoiceDto();
}
}
3. Register Dependencies
Instead of manual services.AddTransient calls, use the [RegisterModule] attribute on a partial void method. The source generator produces the method body, registering the Module implementation and all associated handlers/middleware.
using Microsoft.Extensions.DependencyInjection;
using Fluentish.Mediator;
using MyDomain.Features.Billing;
public static partial class ServiceCollectionExtensions
{
[RegisterModule(typeof(IBillingModule))]
public static partial void AddBillingModule(IServiceCollection services);
}
4. Consumption
Inject the specific module interface into your consumers.
public class BillingController : ControllerBase
{
private readonly IBillingModule _billing;
public BillingController(IBillingModule billing)
{
_billing = billing;
}
[HttpPost("invoice")]
public async Task<IActionResult> CreateInvoice(Guid orderId)
{
// The Execute method is strongly typed and auto-generated.
// It resolves the correct handler from the DI container.
var result = await _billing.Execute(new GenerateInvoiceRequest(orderId));
return Ok(result);
}
}
Middleware (Pipeline Behaviors)
Middleware allows you to intercept requests for logging, validation, or transaction management. Middleware is strictly typed and supports both global (per-module) and per-request scoping.
To implement middleware, implement IRequestMiddleware:
public class LoggingMiddleware<TRequest, TResult> : IRequestMiddleware<IBillingModule, TRequest, TResult>
where TRequest : IRequest<TResult>
{
private readonly ILogger<LoggingMiddleware<TRequest, TResult>> _logger;
public LoggingMiddleware(ILogger<LoggingMiddleware<TRequest, TResult>> logger)
{
_logger = logger;
}
public async Task<TResult> Handle(TRequest request, RequestHandler<TRequest, TResult> next)
{
_logger.LogInformation("Processing {RequestType}", typeof(TRequest).Name);
var result = await next(request);
_logger.LogInformation("Completed {RequestType}", typeof(TRequest).Name);
return result;
}
}
The source generator automatically detects registered middleware in the container and chains them in the generated Execute implementation.
Module Composition
Modules can be composed to create aggregate boundaries using IComposedMediatorModule. This allows a parent module to expose the capabilities of child modules while maintaining a single injection point.
// Define sub-modules
public interface IInvoicingModule : IMediatorModule {}
public interface IPaymentModule : IMediatorModule {}
// Define a composite module
public interface IBillingSuperModule :
IMediatorModule,
IComposedMediatorModule<IInvoicingModule>,
IComposedMediatorModule<IPaymentModule>
{
}
The generator produces an implementation for IBillingSuperModule that routes requests to the appropriate underlying implementations of IInvoicingModule or IPaymentModule.
How it Works
- Parsing: The analyzer scans for interfaces inheriting
IMediatorModuleand classes implementingIRequestHandler. - Mapping: It maps requests to handlers based on the generic arguments.
- Generation:
- Interface Expansion: It generates a partial interface definition containing specific
Execute(TRequest)overloads for every discovered handler. - Implementation: It generates a concrete class that implements the interface. This class accepts
IServiceProviderto resolve handlers lazily. - DI Configuration: It generates the body of the partial method decorated with
[RegisterModule], adding all necessary registrations to theIServiceCollection.
- Interface Expansion: It generates a partial interface definition containing specific
This architecture ensures that the "mediator" is not a dictionary of delegates at runtime, but a concrete, compiled class specifically built for your application's shape.
Learn more about Target Frameworks and .NET Standard.
This package has no dependencies.
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.2 | 92 | 12/27/2025 |
Initial stable release.