CleanBusMediator 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package CleanBusMediator --version 1.0.0
                    
NuGet\Install-Package CleanBusMediator -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CleanBusMediator" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CleanBusMediator" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="CleanBusMediator" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CleanBusMediator --version 1.0.0
                    
#r "nuget: CleanBusMediator, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CleanBusMediator@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CleanBusMediator&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=CleanBusMediator&version=1.0.0
                    
Install as a Cake Tool

๐Ÿš CleanBusMediator

CleanBus is a modern, modular, and extensible Command Bus / Mediator framework for .NET.
It promotes a clean separation of responsibilities through Commands, Events, Streaming, Middleware Pipelines, Exception Handling, and more โ€” all with full support for dependency injection and testability.

Think of it as your lightweight, powerful message dispatcher โ€” without the magic.


๐Ÿ“ฆ Installation

Coming soon to NuGet:

dotnet add package CleanBusMediator

๐Ÿ› ๏ธ Quick Setup

1. Register CleanBusMediator in your DI container

builder.Services.AddCleanBusMediator(typeof(CreateUserHandler).Assembly);

This scans your assembly for:

  • Command handlers
  • Event handlers
  • Middlewares
  • Exception handlers
  • Pre-/Post-processors
  • Stream handlers

โœ‰๏ธ Send Commands

Define a command

public class PingCommand : ICommand<string> { }

Handle the command

public class PingHandler : ICommandHandler<PingCommand, string>
{
    public Task<string> Handle(PingCommand command, CancellationToken cancellationToken)
        => Task.FromResult("Pong!");
}

Dispatch it

var result = await dispatcher.Send(new PingCommand());
// result: "Pong!"

๐Ÿ“ข Publish Events

Define an event

public class OrderPlaced : IEvent
{
    public Guid OrderId { get; set; }
}

Handle the event

public class SendConfirmationEmail : IEventHandler<OrderPlaced>
{
    public Task Handle(OrderPlaced notification, CancellationToken cancellationToken)
    {
        Console.WriteLine($"Email sent for order {notification.OrderId}");
        return Task.CompletedTask;
    }
}

Publish it

await dispatcher.Publish(new OrderPlaced { OrderId = Guid.NewGuid() });

๐ŸŒ€ Streaming Commands

Define a stream command

public class StreamNumbers : IStreamCommand<int>
{
    public int Count { get; set; }
}

Handle it

public class StreamNumbersHandler : IStreamCommandHandler<StreamNumbers, int>
{
    public async IAsyncEnumerable<int> Handle(StreamNumbers command, CancellationToken cancellationToken)
    {
        for (int i = 0; i < command.Count; i++)
        {
            yield return i;
            await Task.Delay(100);
        }
    }
}

Consume the stream

await foreach (var number in dispatcher.Stream(new StreamNumbers { Count = 5 }))
{
    Console.WriteLine(number);
}

๐Ÿงฉ Middleware Support

Define a middleware

public class LoggingMiddleware<TCommand, TResult> : ICommandMiddleware<TCommand, TResult>
    where TCommand : ICommand<TResult>
{
    public async Task<TResult> Handle(TCommand command, CancellationToken cancellationToken, CommandHandlerDelegate<TResult> next)
    {
        Console.WriteLine($"[Start] {typeof(TCommand).Name}");
        var result = await next();
        Console.WriteLine($"[End] {typeof(TCommand).Name}");
        return result;
    }
}

Registered automatically if in the scanned assembly.


๐Ÿ›ก๏ธ Exception Handling

Define an exception handler

public class PingExceptionHandler : ICommandExceptionHandler<PingCommand, string>
{
    public Task<string> Handle(PingCommand command, Exception exception, CancellationToken cancellationToken)
    {
        return Task.FromResult("Recovered from error");
    }
}

๐Ÿงช Pre- and Post-Processors

Pre-processor

public class ValidatePing : ICommandPreProcessor<PingCommand>
{
    public Task Process(PingCommand command, CancellationToken cancellationToken)
    {
        Console.WriteLine("Pre-processing PingCommand");
        return Task.CompletedTask;
    }
}

Post-processor

public class LogPingResult : ICommandPostProcessor<PingCommand, string>
{
    public Task Process(PingCommand command, string result, CancellationToken cancellationToken)
    {
        Console.WriteLine($"Post-processing result: {result}");
        return Task.CompletedTask;
    }
}

๐ŸŒ Event Middleware

public class EventLoggingMiddleware<TEvent> : IEventMiddleware<TEvent>
    where TEvent : IEvent
{
    public async Task Handle(TEvent @event, CancellationToken cancellationToken, EventHandlerDelegate next)
    {
        Console.WriteLine($"[Event Start] {@event.GetType().Name}");
        await next();
        Console.WriteLine($"[Event End] {@event.GetType().Name}");
    }
}

โšก Parallel Event Publishing

Optional: run event handlers in parallel

await dispatcher.PublishParallel(new OrderPlaced { OrderId = Guid.NewGuid() });

๐Ÿงช Testing Support

Fake Dispatcher

var dispatcher = new FakeDispatcher
{
    SendHandler = cmd => "FakeResult"
};

var result = await dispatcher.Send(new PingCommand());

Test Dispatcher (manual handler registration)

var testDispatcher = new TestDispatcher();
testDispatcher.RegisterHandler(new PingHandler());

var result = await testDispatcher.Send(new PingCommand());

๐Ÿ” Retry Policies

Custom retry logic per command:

public class SimpleRetryPolicyProvider : IRetryPolicyProvider
{
    public AsyncPolicy<TResult>? GetPolicy<TCommand, TResult>()
        where TCommand : ICommand<TResult>
    {
        return Policy<TResult>
            .Handle<Exception>()
            .WaitAndRetryAsync(3, retry => TimeSpan.FromMilliseconds(200));
    }
}

Used automatically via RetryMiddlewareFactory<,> if registered.


๐Ÿง  Scoped Pipeline Builder

Need different middlewares per request? Use ScopedCommandPipelineBuilder:

var scopedDispatcher = new Dispatcher(
    type => scopedProvider.GetRequiredService(type),
    new ScopedCommandPipelineBuilder(scopedProvider, defaultPipeline)
);

๐Ÿงฐ Built-in Middleware Available

  • โœ… LoggingMiddleware
  • โœ… ValidationMiddleware (via IValidator<>)
  • โœ… CachingMiddleware (for ICachableCommand)
  • โœ… RetryMiddleware (via IRetryPolicyProvider)

๐Ÿงพ License

Licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.