Domain Events with Ninject and ASP.Net Web API

This post looks at hooking up domain events to implementations using an IoC container and the ASP.Net Web API. This post is part of a blog series ASP.Net 10 Years On. Even though this is part of a series each post tries to be standalone. In the previous post we created the .Net Web API.

In an earlier post the topic of Domain Events in a domain model was discussed. This post revisits this and looks at how the concrete implementations can be injected using Ninject.

To recap, we had the DomainEvents class as follows:
public static class DomainEvents
{
    /// <summary>
    /// The _actions.
    /// </summary>
    /// <remarks>Marked as ThreadStatic that each thread has its own callbacks</remarks>
    [ThreadStatic]
    private static List<Delegate> _actions;

    /// <summary>
    /// The container
    /// </summary>
    public static IEventContainer Container;

    /// <summary>
    /// Registers the specified callback for the given domain event.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="callback">The callback.</param>
    public static void Register<T>(Action<T> callback) where T : IDomainEvent
    {
        if (_actions == null)
            _actions = new List<Delegate>();

        _actions.Add(callback);
    }
    /// <summary>
    /// Raises the specified domain event and calls the event handlers.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="domainEvent">The domain event.</param>
    public static void Raise<T>(T domainEvent) where T : IDomainEvent
    {
        if (Container != null)
            foreach (var handler in Container.Handlers(domainEvent))
                handler.Handle(domainEvent);

        // registered actions, typically used for unit tests.
        if (_actions != null)
            foreach (var action in _actions)
                if (action is Action<T>)
                    ((Action<T>)action)(domainEvent);
    }
}
Since the initial post on domain events, the following has been added:
public static IEventContainer Container;
Plus the following code in the Raise method that checks the Container property for any handlers to be called:
   
if (Container != null)
            foreach (var handler in Container.Handlers(domainEvent))
                handler.Handle(domainEvent);
These two additions provide the link between this class and the IoC container of choice, which in this case is Ninject.
The IEventContainer is as follows:
public interface IEventContainer
{
    IEnumerable<IDomainEventHandler<T>> Handlers<T>(T domainEvent)
                                                        where T : IDomainEvent;
}
To get up and running using Ninject for the Web API we firstly need to go ahead and install Ninject via the Nuget Package Manager (see the post titled creating your first API under the heading "dependency injection" for more details on how to set this up). Next add the following class. This class allows us to interface the domain event handlers with Ninject:
public class NinjectEventContainer : IEventContainer
{
    private readonly IKernel _kernerl;

    public NinjectEventContainer(IKernel kernal)
    {
        _kernerl = kernal;
    }

    public IEnumerable<IDomainEventHandler<T>> Handlers<T>(T domainEvent) where T : IDomainEvent
    {
        return _kernerl.GetAll<IDomainEventHandler<T>>();
    }
}
Next we add the following line to the NinjectWebCommon.CreateKernel() method and the concrete implementations can be hooked up using dependency injection:
private static IKernel CreateKernel()
{
    var kernel = new StandardKernel();
    kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
    kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

    // this is our concrete implementation of handler for winner selected event
    kernel.Bind<IDomainEventHandler<WinnerSelectedEvent>>().To<WinnerSelectedHandler>();

    GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);

    // ** WIRE UP DOMAIN EVENTS CONTAINER **
    DomainEvents.Container = new NinjectEventContainer(kernel);

    return kernel;
}
Here's the structure of the WinnerSelectedHandler class:
public class WinnerSelectedHandler : IDomainEventHandler<WinnerSelectedEvent>
{
    public void Handle(WinnerSelectedEvent domainEvent)
    {
        // send a message here.
    }
}
The source code for this project can be found at: github.com/bbraithwaite/smsquiz.
Key code elements discussed in this post: