Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

multiple types for SetHandlerInterface() with membus and ioc container

Tags:

cqrs

membus

Going over demo CQRS code here the command and event handlers are wired up separately as below:

public interface CommandHandler<in T>
{
    void Handle(T command);
}

public interface EventHandler<in T>
{
    void Handle(T @event);
}

bus = BusSetup.StartWith<Conservative>()
       .Apply<FlexibleSubscribeAdapter>(a =>
       {
           a.ByInterface(typeof(IHandleEvent<>));
           a.ByInterface(typeof(IHandleCommand<>));
       })
       .Construct();

I am using an IoC container hooked in with membus and it works a dream by implementing the IEnumerable<object> GetAllInstances(Type desiredType) interface with my container, however unlike the demo with this method of registration I cannot split out the interfaces for separate commands and events:

this.Bus = BusSetup.StartWith<Conservative>()
    .Apply <IoCSupport>(c =>
        {
            c
            .SetAdapter(SimpleInjectorWiring.Instance)
            .SetHandlerInterface(typeof(CommandHandler<>))
            /*.SetHandlerInterface(typeof(EventHandler<>))*/;
            // only CommandHandler or EventHandler can be used - not both
        })
    .Construct();

Can anyone please let me know if there any way around this so we can register for an arbitrary number of types?

like image 734
morleyc Avatar asked Apr 20 '13 21:04

morleyc


2 Answers

I am afraid that the current version of MemBus cannot do that - for no particular reason, mind you. I know that it makes sense to be able to distinguish between Events and Commands, even though the underlying infrastructure is the same.

The only workaround right now is to use a single interface to hook the IOC into MemBus.

If such a feature should be introduced into MemBus one has to think how the lookup mechanism into the IOC Container should look like. It would probably have to request all handlers of all interfaces, or some way to categorize / distinguish between event and command "messages" would have to be introduced.

like image 191
flq Avatar answered Jan 04 '23 00:01

flq


I just set this up for a proof of concept because I use the following convention per the Microsoft CQRS Journey guidelines:

  • There can be only one CommandHandler per Command
  • There can be many EventHandlers per Event

I wanted use the IOC container's (SimpleInjector) API to enforce the convention as it forces you to make single vs multi handlers registrations explicit by design. Now, my IOC container will throw an Exception anytime two handlers for the same command are accidentally registered.

To get MemBus to support this convention, I needed to create my own ISetup, ISubscriptionResolver, and IoCAdapter (starting with the code from IoCSupport, IoCBasedResolver, and IocAdapter respectively). I also had to create a new IocAdapter interface that also supports the singular GetInstance() method; now the interface roughly matches the System.Web.Http.Dependencies.IDependencyScope interface (GetService and GetServices).

// Setup
return BusSetup
    .StartWith<Conservative>()
    .Apply<CommandingEventingSupport>(
        adapter =>
            {
                adapter.SetAdapter(new MemBusSimpleInjectorAdapter(container));
                adapter.SetCommandHandlerInterface(typeof(IHandleCommand<>));
                adapter.SetEventHandlerInterface(typeof(IHandleEvent<>));
                adapter.SetCommandTest(obj => obj is IDomainCommand);
            })
    .Construct();

And then the resolver dispatches commands to the GetInstance and events to GetAllInstances...

    // Command vs Event
    public IEnumerable<ISubscription> GetSubscriptionsFor(object message)
    {
        bool isCommand = _isCommandFunc(message);
        Type typeToCreate = isCommand
            ? _commandHandlerType 
            : _eventHandlerType;
        var handlesType = ConstructHandlesType(typeToCreate, message.GetType());
        var mi = handlesType.GetRuntimeMethods().First();
        return isCommand
                   ? new[] { mi.ConstructSubscription(_adapter.GetInstance(handlesType)) }
                   : _adapter.GetAllInstances(handlesType).Select(mi.ConstructSubscription);
    }
like image 30
Jason Avatar answered Jan 04 '23 00:01

Jason