.NET Standard 2.0
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Merq --version 1.3.0
NuGet\Install-Package Merq -Version 1.3.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="Merq" Version="1.3.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Merq --version 1.3.0
#r "nuget: Merq, 1.3.0"
#r directive can be used in F# Interactive, C# scripting and .NET Interactive. Copy this into the interactive tool or source code of the script to reference the package.
// Install Merq as a Cake Addin
#addin nuget:?package=Merq&version=1.3.0

// Install Merq as a Cake Tool
#tool nuget:?package=Merq&version=1.3.0

Mercury: messenger of the Roman gods

Mercury > Merq-ry > Merq

Merq brings the Message Bus pattern together with a command-oriented interface to in-process application architecture.

These patterns are well established in microservices and service oriented architectures, but their benefits can be applied to apps too, especially extensible ones where multiple teams can contribute extensions which are composed at run-time.

The resulting improved decoupling between components makes it easier to evolve them independently, while improving discoverability of available commands and events. You can see this approach applied in the real world in VSCode commands and various events such as window events. Clearly, in the case of VSCode, everything is in-process, but the benefits of a clean and predictable API are pretty obvious.

Merq provides the same capabilities for .NET apps.

Events

Events can be any type, there is no restriction or interfaces you must implement. Nowadays, C# record types are a perfect fit for event data types. An example event could be a one-liner such as:

public record ItemShipped(string Id, DateTimeOffset Date);

The events-based API surface on the message bus is simple enough:

public interface IMessageBus
{
    void Notify<TEvent>(TEvent e);
    IObservable<TEvent> Observe<TEvent>();
}

By relying on IObservable<TEvent>, Merq integrates seamlessly with more powerful event-driven handling via System.Reactive or the more lightweight RxFree. Subscribing to events with either of those packages is trivial:

IDisposable subscription;

// constructor may use DI to get the dependency
public CustomerViewModel(IMessageBus bus)
{
    subscription = bus.Observe<ItemShipped>().Subscribe(OnItemShipped);
}

void OnItemShipped(ItemShipped e) => // Refresh item status

public void Dispose() => subscription.Dispose();

Commands

Commands can also be any type, and C# records make for concise definitions:

record CancelOrder(string OrderId) : IAsyncCommand;

Unlike events, command messages need to signal the invocation style they require for execution:

// perhaps a method invoked when a user 
// clicks/taps a Cancel button next to an order
async Task OnCancel(string orderId)
{
    await bus.ExecuteAsync(new CancelOrder(orderId), CancellationToken.None);
    // refresh UI for new state.
}

An example of a synchronous command could be:

record SignOut() : ICommand;

void OnSignOut() => bus.Execute(new SignOut());

// or alternatively, for void commands that have no additional data:
void OnSignOut() => bus.Execute<SignOut>();

There are also ICommand<TResult> and IAsyncCommand<TResult> to signal that the execution yields a result.

While these marker interfaces on the command messages might seem unnecessary, they are actually quite important. They solve a key problem that execution abstractions face: whether a command execution is synchronous or asynchronous (as well as void or value-returning) should not be abstracted since otherwise you can end up in a common anti-pattern (i.e. async guidelines for ASP.NET), known as sync over async and async over sync.

The marker interfaces on the command messages drive the compiler to only allow the right invocation style on the message bus, as defined by the command author:

public interface IMessageBus
{
    // sync void
    void Execute(ICommand command);
    // sync value-returning
    TResult? Execute<TResult>(ICommand<TResult> command);
    // async void
    Task ExecuteAsync(IAsyncCommand command, CancellationToken cancellation);
    // async value-returning
    Task<TResult> ExecuteAsync<TResult>(IAsyncCommand<TResult> command, CancellationToken cancellation);
}

For example, to create a value-returning async command that retrieves some value, you would have:

record FindDocuments(string Filter) : IAsyncCommand<IEnumerable<string>>;

class FindDocumentsHandler : IAsyncCommandHandler<FindDocument, IEnumerable<string>>
{
    public bool CanExecute(FindDocument command) => !string.IsNullOrEmpty(command.Filter);
    
    public Task<IEnumerable<string>> ExecuteAsync(FindDocument command, CancellationToken cancellation)
        => // evaluate command.Filter across all documents and return matches
}

In order to execute such command, the only execute method the compiler will allow is:

IEnumerable<string> files = await bus.ExecuteAsync(new FindDocuments("*.json"));

If the consumer tries to use Execute, the compiler will complain that the command does not implement ICommand<TResult>, which is the synchronous version of the marker interface. Likewise, mistakes cannot be made when implementing the handler, since the handler interfaces define constraints on what the commands must implement:

// sync
public interface ICommandHandler<in TCommand> : ... where TCommand : ICommand;
public interface ICommandHandler<in TCommand, out TResult> : ... where TCommand : ICommand<TResult>;

// async
public interface IAsyncCommandHandler<in TCommand> : ... where TCommand : IAsyncCommand;
public interface IAsyncCommandHandler<in TCommand, TResult> : ... where TCommand : IAsyncCommand<TResult>

This design choice also makes it impossible to end up executing a command implementation improperly.

In addition to execution, the IMessageBus also provides a mechanism to determine if a command has a registered handler at all via the CanHandle<T> method as well as a validation mechanism via CanExecute<T>, as shown above in the FindDocumentsHandler example.

Commands can notify new events, and event observers/subscribers can in turn execute commands.

Product Versions
.NET net5.0 net5.0-windows net6.0 net6.0-android net6.0-ios net6.0-maccatalyst net6.0-macos net6.0-tvos net6.0-windows net7.0 net7.0-android net7.0-ios net7.0-maccatalyst net7.0-macos net7.0-tvos net7.0-windows
.NET Core netcoreapp2.0 netcoreapp2.1 netcoreapp2.2 netcoreapp3.0 netcoreapp3.1
.NET Standard netstandard2.0 netstandard2.1
.NET Framework net461 net462 net463 net47 net471 net472 net48
MonoAndroid monoandroid
MonoMac monomac
MonoTouch monotouch
Tizen tizen40 tizen60
Xamarin.iOS xamarinios
Xamarin.Mac xamarinmac
Xamarin.TVOS xamarintvos
Xamarin.WatchOS xamarinwatchos
Compatible target framework(s)
Additional computed target framework(s)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages (6)

Showing the top 5 NuGet packages that depend on Merq:

Package Downloads
Clide.Installer

Clide Installer: VSIX, MSI and EXE (chained) installer integration.

Merq.Core The ID prefix of this package has been reserved for one of the owners of this package by NuGet.org.

Merq: Default Message Bus (Commands + Events) Implementation, for internal application architecture via command and event messages. Only the main application assembly needs to reference this package. Components and extensions can simply reference the interfaces in Merq.

Merq.VisualStudio The ID prefix of this package has been reserved for one of the owners of this package by NuGet.org.

Merq MEF components suitable for hosting with Microsoft.VisualStudio.Composition.

guit

Library to build plugins for dotnet-guit.

Merq.AutoMapper The ID prefix of this package has been reserved for one of the owners of this package by NuGet.org.

A specialized Message Bus that allows cross observing and executing of events and commands from structurally compatible types even if they are from disparate assemblies, as long as their full name is the same.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.0.0-beta.3 94 11/19/2022
2.0.0-beta.2 91 11/18/2022
2.0.0-beta 33 11/16/2022
2.0.0-alpha 35 11/16/2022
1.3.0 351 7/28/2022
1.2.0-beta 76 7/20/2022
1.2.0-alpha 65 7/16/2022
1.1.4 2,287 5/6/2020
1.1.1 20,406 6/15/2018
1.1.0 11,950 6/15/2018
1.0.0 5,466 4/30/2018