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:
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}");
}
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!