DEV Community

Cover image for Orchestrix.Mediator: A Modern Mediator Engine for .NET 8/9/10+
Mohammad Anzawi
Mohammad Anzawi

Posted on

Orchestrix.Mediator: A Modern Mediator Engine for .NET 8/9/10+

Tired of hitting limits with MediatR?

Meet Orchestrix.Mediator — a next-gen mediator engine designed for the modern .NET ecosystem.


✨ Why Orchestrix.Mediator?

Orchestrix.Mediator isn’t just a clone of MediatR. It’s a reimagined mediator with first-class support for the features developers now expect:

✅ Zero-reflection dispatch via source generators

✅ Parallel notifications with built-in fan-out

✅ Asynchronous streaming using IAsyncEnumerable<T>

✅ Hook-based diagnostics for tracing, logging, and metrics

✅ CQRS extensions with ICommand, IQuery, and semantic handlers

✅ Minimal API & Controller support

✅ Fully ValueTask-based and AOT-safe

All while preserving the simplicity and ergonomics of MediatR.


⚙️ Quick Start

1. Install

dotnet add package Orchestrix.Mediator 
Enter fullscreen mode Exit fullscreen mode

Optional extensions:

dotnet add package Orchestrix.Mediator.SourceGenerators dotnet add package Orchestrix.Mediator.Cqrs 
Enter fullscreen mode Exit fullscreen mode

2. Register in Program.cs

builder.Services.AddOrchestrix(cfg => { cfg.UseSourceGenerator() // optional: enables zero-reflection dispatch .RegisterHandlersFromAssemblies(typeof(MyHandler).Assembly); }); 
Enter fullscreen mode Exit fullscreen mode

3. Define a Command + Handler

public record CreateUserCommand(string Name) : IRequest<Guid>; public class CreateUserHandler : IRequestHandler<CreateUserCommand, Guid> { public ValueTask<Guid> Handle(CreateUserCommand request, CancellationToken ct) { return ValueTask.FromResult(Guid.NewGuid()); } } 
Enter fullscreen mode Exit fullscreen mode

4. Dispatch via ISender

var id = await sender.Send(new CreateUserCommand("Mohammad")); 
Enter fullscreen mode Exit fullscreen mode

📢 Notifications with Fan-Out

public record UserRegistered(string Email) : INotification; public class LogHandler : INotificationHandler<UserRegistered> { public ValueTask Handle(UserRegistered n, CancellationToken ct) => ValueTask.CompletedTask; } public class EmailHandler : IParallelNotificationHandler<UserRegistered> { public async ValueTask Handle(UserRegistered n, CancellationToken ct) { await Task.Delay(100); Console.WriteLine($"Welcome {n.Email}"); } } 
Enter fullscreen mode Exit fullscreen mode

📣 Both handlers will run: one sequentially, the other in parallel!

await publisher.Publish(new UserRegistered("user@example.com")); 
Enter fullscreen mode Exit fullscreen mode

📡 Streaming with IAsyncEnumerable<T>

public record GetUsers(int Count) : IStreamRequest<UserDto>; public class GetUsersHandler : IStreamRequestHandler<GetUsers, UserDto> { public async IAsyncEnumerable<UserDto> Handle(GetUsers req, [EnumeratorCancellation] CancellationToken ct) { for (int i = 0; i < req.Count; i++) { yield return new UserDto(Guid.NewGuid(), $"User-{i + 1}"); await Task.Delay(100, ct); } } } 
Enter fullscreen mode Exit fullscreen mode

Dispatch:

await foreach (var user in sender.CreateStream(new GetUsers(5), ct)) { Console.WriteLine(user.Name); } 
Enter fullscreen mode Exit fullscreen mode

🪝 Built-in Hooks for Tracing & Logging

Orchestrix.Mediator supports lifecycle instrumentation via hooks:

  • ISendHook
  • IPublishHook
  • IStreamHook

Example:

public class LoggingHook : ISendHook { public ValueTask OnSendStart(object request, CancellationToken ct) => Log($"[SEND] Start: {request.GetType().Name}"); public ValueTask OnSendComplete(object request, object? response, CancellationToken ct) => Log($"[SEND ✅] Completed: {response}"); public ValueTask OnSendError(object request, Exception ex, CancellationToken ct) => Log($"[SEND ❌] Failed: {ex.Message}"); } 
Enter fullscreen mode Exit fullscreen mode

Register:

services.AddOrchestrix(cfg => cfg.AddHook<LoggingHook>()); 
Enter fullscreen mode Exit fullscreen mode

🧱 Pipelines Still Here

Need validation, logging, or retry logic?

public class LoggingBehavior<TReq, TRes> : IPipelineBehavior<TReq, TRes> where TReq : IRequest<TRes> { public async ValueTask<TRes> Handle(TReq req, RequestHandlerDelegate<TRes> next, CancellationToken ct) { Console.WriteLine($"Handling {typeof(TReq).Name}"); return await next(ct); } } 
Enter fullscreen mode Exit fullscreen mode

Register with:

cfg.AddOpenBehavior(typeof(LoggingBehavior<,>)); 
Enter fullscreen mode Exit fullscreen mode

🧭 CQRS Done Right

Install:

dotnet add package Orchestrix.Mediator.Cqrs 
Enter fullscreen mode Exit fullscreen mode

Use:

public record SaveUser(string Name) : ICommand<Guid>; public class SaveUserHandler : ICommandHandler<SaveUser, Guid> { public ValueTask<Guid> Handle(SaveUser cmd, CancellationToken ct) => ValueTask.FromResult(Guid.NewGuid()); } 
Enter fullscreen mode Exit fullscreen mode

🔁 TrySend / TryPublish

bool handled = await sender.TrySend(new OptionalCommand()); bool published = await publisher.TryPublish(new OptionalEvent()); 
Enter fullscreen mode Exit fullscreen mode

Avoid exceptions when no handler is found.


✨ Optional: Source Generator Boost

Install:

dotnet add package Orchestrix.Mediator.SourceGenerators 
Enter fullscreen mode Exit fullscreen mode

Add:

cfg.UseSourceGenerator(); 
Enter fullscreen mode Exit fullscreen mode

No reflection. No runtime resolution. AOT-safe.

Orchestrix will emit a GeneratedDispatcher behind the scenes.


🧪 Fully Testable

var sender = new Mock<ISender>(); sender.Setup(s => s.Send(It.IsAny<IRequest<Guid>>(), It.IsAny<CancellationToken>())) .ReturnsAsync(Guid.NewGuid()); 
Enter fullscreen mode Exit fullscreen mode

Use ISender, IPublisher, or IMediator in your tests.


🔁 Migrating from MediatR?

✅ Use the same patterns

✅ Replace IMediator with ISender/IPublisher

✅ Update AddMediatR to AddOrchestrix

✅ Replace INotificationHandler<T> with IParallelNotificationHandler<T> if needed

Read full migration guide → MIGRATION.md


📚 Resources


🔥 Ready to Modernize Mediation?

Whether you’re building a CQRS-heavy application, orchestrating event-driven flows, or simply want better observability — Orchestrix.Mediator is built to scale with you.

👉 Give it a ⭐ on GitHub: github.com/anzawi/Orchestrix.Mediator

💬 Feedback or questions? Drop them in the repo’s issues!

Top comments (0)