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 Optional extensions:
dotnet add package Orchestrix.Mediator.SourceGenerators dotnet add package Orchestrix.Mediator.Cqrs 2. Register in Program.cs
builder.Services.AddOrchestrix(cfg => { cfg.UseSourceGenerator() // optional: enables zero-reflection dispatch .RegisterHandlersFromAssemblies(typeof(MyHandler).Assembly); }); 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()); } } 4. Dispatch via ISender
var id = await sender.Send(new CreateUserCommand("Mohammad")); 📢 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}"); } } 📣 Both handlers will run: one sequentially, the other in parallel!
await publisher.Publish(new UserRegistered("user@example.com")); 📡 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); } } } Dispatch:
await foreach (var user in sender.CreateStream(new GetUsers(5), ct)) { Console.WriteLine(user.Name); } 🪝 Built-in Hooks for Tracing & Logging
Orchestrix.Mediator supports lifecycle instrumentation via hooks:
ISendHookIPublishHookIStreamHook
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}"); } Register:
services.AddOrchestrix(cfg => cfg.AddHook<LoggingHook>()); 🧱 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); } } Register with:
cfg.AddOpenBehavior(typeof(LoggingBehavior<,>)); 🧭 CQRS Done Right
Install:
dotnet add package Orchestrix.Mediator.Cqrs 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()); } 🔁 TrySend / TryPublish
bool handled = await sender.TrySend(new OptionalCommand()); bool published = await publisher.TryPublish(new OptionalEvent()); Avoid exceptions when no handler is found.
✨ Optional: Source Generator Boost
Install:
dotnet add package Orchestrix.Mediator.SourceGenerators Add:
cfg.UseSourceGenerator(); 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()); 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)