Overview
Savvyio.Extensions.Dispatchers adds mediator support on top of Savvyio.Commands, Savvyio.Domain, Savvyio.EventDriven, and Savvyio.Queries. Its IMediator contract combines fire-and-forget operations for commands, domain events, and integration events with request-reply queries, so consumers can depend on one application-facing dispatcher instead of four separate abstractions.
The package also adds SavvyioOptions extensions for registering a mediator implementation and for discovering dispatchers and handlers by assembly scan. The default Mediator implementation composes the existing command, domain event, integration event, and query dispatchers through a shared IServiceLocator.
Key APIs
IMediator combines ICommandDispatcher, IDomainEventDispatcher, IIntegrationEventDispatcher, and IQueryDispatcher into a single contract. It is the package's main abstraction when you want one dependency that can commit commands, raise domain events, publish integration events, and execute queries.
Mediator is the default IMediator implementation. Its constructor accepts an IServiceLocator, then builds an internal CommandDispatcher, DomainEventDispatcher, IntegrationEventDispatcher, and QueryDispatcher and forwards Commit, Raise, Publish, Query, and their asynchronous counterparts to those lower-level dispatchers.
AddMediator<TImplementation> registers one mediator implementation for five service contracts in SavvyioOptions: IMediator, ICommandDispatcher, IDomainEventDispatcher, IIntegrationEventDispatcher, and IQueryDispatcher. Use it when the same concrete mediator should satisfy both the unified mediator contract and the narrower dispatcher interfaces.
UseAutomaticDispatcherDiscovery adds dispatcher registrations to SavvyioOptions by scanning either Assembly.GetCallingAssembly() or AssemblyContext.CurrentDomainAssemblies. The bruteAssemblyScanning switch controls whether discovery stays local to the calling assembly or scans the broader application domain.
UseAutomaticHandlerDiscovery adds handler registrations to SavvyioOptions using the same discovery model. It is the setup entry point when you want handler implementations in the calling assembly, or in all current-domain assemblies, to be picked up without listing each handler type explicitly.
Basic usage
using System;
using System.Collections.Generic;
using Codebelt.Extensions.Xunit;
using Savvyio.Commands;
using Savvyio.Dispatchers;
using Savvyio.Extensions;
using Savvyio.Handlers;
using Savvyio.Queries;
using Xunit;
namespace MyProject.Tests;
public class MediatorWorkflowTest : Test
{
public MediatorWorkflowTest(ITestOutputHelper output) : base(output) { }
[Fact]
public void Mediator_MissingHandlers_ThrowsOrphanedHandlerException()
{
var sut = new Mediator(new EmptyServiceLocator());
var commandError = Assert.Throws<OrphanedHandlerException>(() => sut.Commit(new ArchiveOrder("A-42")));
var queryError = Assert.Throws<OrphanedHandlerException>(() => sut.Query(new ReadOrderStatus()));
TestOutput.WriteLines(commandError.Message, queryError.Message);
Assert.Contains(nameof(ArchiveOrder), commandError.Message, StringComparison.Ordinal);
Assert.Contains(nameof(ReadOrderStatus), queryError.Message, StringComparison.Ordinal);
}
}
public sealed record ArchiveOrder(string OrderId) : Command;
public sealed record ReadOrderStatus : Query<string>;
public sealed class EmptyServiceLocator : IServiceLocator
{
public IEnumerable<object> GetServices(Type serviceType) => Array.Empty<object>();
}
Use this pattern when one mediator should surface missing command and query registrations immediately instead of letting the request disappear in composition.
It matters because Mediator keeps the command and query entry points behind one API while still preserving the fail-fast handler resolution behavior of the underlying dispatchers.
Installation
dotnet add package Savvyio.Extensions.Dispatchers
Usage guidance
Adopt this package when your application already uses multiple Savvyio request types and you want one mediator contract plus optional assembly-based registration helpers to wire them together. If you only need a single dispatch style, the narrower packages such as Savvyio.Commands, Savvyio.Domain, Savvyio.EventDriven, or Savvyio.Queries keep the dependency surface smaller.
Family packages
- 🏭Savvyio.App
- 📦Savvyio.Commands
- 📦Savvyio.Commands.Messaging
- 📦Savvyio.Core
- 📦Savvyio.Domain
- 📦Savvyio.Domain.EventSourcing
- 📦Savvyio.EventDriven
- 📦Savvyio.EventDriven.Messaging
- 📦Savvyio.Extensions.Dapper
- 📦Savvyio.Extensions.DapperExtensions
- 📦Savvyio.Extensions.DependencyInjection
- 📦Savvyio.Extensions.DependencyInjection.Dapper
- 📦Savvyio.Extensions.DependencyInjection.DapperExtensions
- 📦Savvyio.Extensions.DependencyInjection.Domain
- 📦Savvyio.Extensions.DependencyInjection.EFCore
- 📦Savvyio.Extensions.DependencyInjection.EFCore.Domain
- 📦Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing
- 📦Savvyio.Extensions.DependencyInjection.NATS
- 📝Savvyio.Extensions.DependencyInjection.Newtonsoft.Json
- 📦Savvyio.Extensions.DependencyInjection.QueueStorage
- 📦Savvyio.Extensions.DependencyInjection.RabbitMQ
- 📦Savvyio.Extensions.DependencyInjection.SimpleQueueService
- 📝Savvyio.Extensions.DependencyInjection.Text.Json
- 📦Savvyio.Extensions.EFCore
- 📦Savvyio.Extensions.EFCore.Domain
- 📦Savvyio.Extensions.EFCore.Domain.EventSourcing
- 📦Savvyio.Extensions.NATS
- 📝Savvyio.Extensions.Newtonsoft.Json
- 📦Savvyio.Extensions.QueueStorage
- 📦Savvyio.Extensions.RabbitMQ
- 📦Savvyio.Extensions.SimpleQueueService
- 📝Savvyio.Extensions.Text.Json
- 📦Savvyio.Messaging
- 📦Savvyio.Queries