Search code examples
c#genericsinversion-of-controlsimple-injector

Implementing Domain Event Handler pattern in C# with Simple Injector


I am trying to implement the Domain Event pattern in C# using Simple Injector.

I have simplified my code to be in one file that can be ran as a console app and have excluded the Simple Injector code to keep things clear for the purpose of this question.

The problem I am coming up against is that each event could have multiple event handlers and multiple events could be raised but I want to restrict my Dispatcher to only handle events that implement the IEvent interface so I put that restraint on my Dispatch method.

This caused problems as to how to get the instance from Simple Injector as each time the Dispatch method is called TEvent is of type IEvent (as I would expect) but I need to get the type of the event passed in so I can get the relevant handlers from Simple Injector.

Hopefully my code will explain this a little bit better:

interface IEvent 
{
}

interface IEventHandler<T> where T : IEvent
{
    void Handle(T @event);
}

class StandardEvent : IEvent
{
}

class AnotherEvent : IEvent
{
}

class StandardEventHandler : IEventHandler<StandardEvent>
{
    public void Handle(StandardEvent @event)
    {
        Console.WriteLine("StandardEvent handled");
    }
}

class AnotherEventHandler : IEventHandler<AnotherEvent>
{
    public void Handle(AnotherEvent @event)
    {
        Console.WriteLine("AnotherEvent handled");
    }
}

Here's my dispatcher:

static class Dispatcher
{
    // I need to get the type of @event here so I can get the registered instance from the
    // IoC container (SimpleInjector), however TEvent is of type IEvent (as expected). 
    // What I need to do here is Get the registered instance from Simple Injector for each
    // Event Type i.e. Container.GetAllInstances<IEventHandler<StandardEvent>>()
    // and Container.GetAllInstances<IEventHandler<AnotherEvent>>()
    public static void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent
    {
    }
}

class PlainOldObject
{
    public ICollection<IEvent> Events = new List<IEvent>
    {
        new StandardEvent(),
        new AnotherEvent()
    };
}

class StandAlone
{
    static void Main(string[] args)
    {
        var poco = new PlainOldObject();
        foreach (var @event in poco.Events)
        {
            Dispatcher.Dispatch(@event);
        }
    }
}

I have commented in the Dispatch method what my issue is. Does anyone have any idea on how I should resolve this?

Regards, Gary


Solution

  • The solution you need is a bit dependent on how the consumer of the Dispatcher calls events. If the consumer always knows the exact type of the event at compile time, you can use the generic Dispatch<TEvent>(TEvent) method as you show above. In that case the Dispatcher's implementation will be really straightforward.

    If on the other hand, consumers might not always know the exact type, but simply work with the IEvent interface, the generic type argument in Dispatch<TEvent>(TEvent) becomes useless, and you would be better off defining a Dispatch(IEvent) method. This makes the implementation a bit more sophisticated, because you will need to use reflection to solve this.

    Also note that it would be good to introduce an IEventDispatcher abstraction. Don't call a static class from within your code. Even Udi Dahan (who initially described such static class a long time ago) now considers this an anti-pattern. Instead, inject an IEventDispatcher abstraction into classes that require event dispatching.

    In case all consumers work with event types that are known at compile time, your implementation will look as follows:

    public interface IEventDispatcher
    {
        void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent;
    }
    
    private sealed class Dispatcher : IEventDispatcher
    {
        private readonly Container container;
        public Dispatcher(Container container) {
            this.container = container;
        }
    
        public void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent {
            if (@event == null) throw new ArgumentNullException("event");
            
            var handlers = this.container.GetAllInstances<IEventHandler<TEvent>>();
            
            foreach (var handler in handlers) {
                handler.Handle(@event);
            }
        }
    }
    

    If on the other hand, event types are unknown, you can use the following code:

    public interface IEventDispatcher
    {
        void Dispatch(IEvent @event);
    }
    
    private sealed class Dispatcher : IEventDispatcher
    {
        private readonly Container container;
        public Dispatcher(Container container) {
            this.container = container;
        }
    
        public void Dispatch(IEvent @event) {
            if (@event == null) throw new ArgumentNullException("event");
            
            Type handlerType = typeof(IEventHandler<>).MakeGenericType(@event.GetType());
            
            var handlers = this.container.GetAllInstances(handlerType);
            
            foreach (dynamic handler in handlers) {
                handler.Handle((dynamic)@event);
            }
        }
    }
    

    Do note that the use of the dynamic keyword has a few unobvious advantages over using the .NET reflection API. For instance, when calling the handler's Handle method using dynamic, any exception thrown from the handle will bubble up directly. When using MethodInfo.Invoke on the other hand, the exception will be wrapped in a new exception. This makes catching and debugging harder.

    Your event handlers can be registered as follows:

    container.Collection.Register(typeof(IEventHandler<>), listOfAssembliesToSearch);