We’ve tweaked the handler interfaces slightly for the 1.1 release of Nimbus.

In the 1.0 series, handlers were void methods. I admit it: this was a design flaw. We thought it would make for a more simple introduction to using the bus - and it did - but the trade-off was that it was much more complicated to do clever stuff.

Consider this handler method:

public void Handle(DoFooCommand busCommand)
{
    DoFoo();
}

Pretty straight-forward, right? Except what happens when doing foo requires us to publish an event afterwards?

public void Handle(DoFooCommand busCommand)
{
    DoFoo();
    _bus.Publish(new FooWasDoneEvent());
}

That doesn’t look so bad, except that we’ve missed the fact that _bus.Publish actually returns a Task and executes asynchronously. What if doing foo required us to ask a question first?

public void Handle(DoFooCommand busCommand)
{
    var result = await _bus.Request(new WhoLastDidFooRequest());
    DoFoo(result.WhoDunnit);
    _bus.Publish(new FooWasDoneEvent());
}

Now things are a bit more complicated. The above method won’t compile, as it’s not marked as async. But there’s a simple fix, right?

public async void Handle(DoFooCommand busCommand)
{
    var result = await _bus.Request(new WhoLastDidFooRequest());
    DoFoo(result.WhoDunnit);
    await _bus.Publish(new FooWasDoneEvent());
}

Problem solved. Except that it’s not. Because although our code will compile and execute happily, what’s going on under the covers is that the Nimbus command dispatcher has no easy way of waiting for your async handler method to complete. As far as the dispatcher is concerned, your handler executed successfully - and really quickly - and we then mark the message as successfully handled.

Think about what happens in this example case below (courtesy of the immortal Krzysztof Kozmic via this GitHub issue):

public async void Handle(DoFooCommand busCommand)
{
    throw new InvalidOperationException("HA HA HA, you can't catch me!");
}

As far as the dispatcher is concerned, your handler method executed just fine. And now we’ve broken our delivery guarantee. Not so good.

The fix for this is simple:

public async Task Handle(DoFooCommand busCommand)
{
    throw new InvalidOperationException("HA HA HA, you can't catch me!");
}

Done. Your method now returns a Task - which the Nimbus dispatcher can await - and if your handler throws then we know about it and can handle it appropriately. So your actual handler would look like this:

public async Task Handle(DoFooCommand busCommand)
{
    var result = await _bus.Request(new WhoLastDidFooRequest());
    DoFoo(result.WhoDunnit);
    await _bus.Publish(new FooWasDoneEvent());
}

So why is this worth an article? Because in order to make this change, we’ve had to alter the IHandleCommand

, IHandleRequest etc. interfaces to have the handler methods return tasks. This:
public interface IHandleCommand<TBusCommand> where TBusCommand : IBusCommand
{
    void Handle(TBusCommand busCommand);
}

is now this:

public interface IHandleCommand<TBusCommand> where TBusCommand : IBusCommand
{
    Task Handle(TBusCommand busCommand);
}

This means that when you upgrade to the 1.1 versons of Nimbus you’ll need to do a quick Ctrl-Shift-H for all your instances of “void Handle(“ and replace them with “Task Handle(“.