0

For a project I'm working on, we're practicing domain driven design and using ninject as our IOC container. I'm trying to implement domain events similar to the approach described by Tony Truong here. I'm trying to use ninject without having to have a static service or referencing the Kernel outside the composition root. I'm trying to do something like this

/// <summary>
/// Service to dispatch domain events to handlers.
/// </summary>
public class NinjectDomainEventDispatcher : IDomainEventDispatcher
{
    /// <summary>
    /// Array containing domain event handler registrations.
    /// </summary>
    private readonly IDomainEventHandler<IDomainEvent>[] _handlers;

    /// <summary>
    /// Initializes a new instance of the <see cref="NinjectDomainEventDispatcher"/> class.
    /// </summary>
    /// <param name="handlers">Registered domain event handlers.</param>
    public NinjectDomainEventDispatcher(IDomainEventHandler<IDomainEvent>[] handlers)
    {
        _handlers = handlers;
    }

    /// <summary>
    /// Dispatch domain event to subscribed handlers.
    /// </summary>
    /// <typeparam name="T">Type of domain event to dispatch.</typeparam>
    /// <param name="domainEvent">Domain event to dispatch.</param>
    public void Dispatch<T>(T domainEvent) where T : IDomainEvent
    {
        foreach (var handler in _handlers.Where(x => x.GetType() == typeof(IDomainEventHandler<T>)))
        {
            handler.Handle(domainEvent);
        }
    }
}

/// <summary>
/// Module that will be used for the registration of the domain event handlers
/// </summary>
public class DomainEventHandlerModule : NinjectModule
{
    /// <summary>
    /// The method that will be used to load the ninject registration information.
    /// </summary>
    public override void Load()
    {
        Bind<IDomainEventDispatcher>().To<NinjectDomainEventDispatcher>();
        Bind<IDomainEventHandler<ConcreteDomainEvent>>().To<ConcreteDomainEventHandler1>();
        Bind<IDomainEventHandler<ConcreteDomainEvent>>().To<ConcreteDomainEventHandler2>();
        Bind<IDomainEventHandler<AnotherConcreteDomainEvent>>().To<AnotherConcreteDomainEventHandler1>();
        Bind<IDomainEventHandler<AnotherConcreteDomainEvent>>().To<AnotherConcreteDomainEventHandler2>();
    }
}

The goal is to call all handlers registered for the type of the domain event instance passed in to the Dispatch method. My problem is, the handlers array is empty when injected (I'm guessing because it's specifically looking for handlers bound to IDomainEventHandler<IDomainEvent>, not all handlers of types implementing IDomainEvent like I need).

ancapn8
  • 9
  • 1
  • this recent answer may help you as the question seems really similar to yours https://stackoverflow.com/a/46729694/1236044 – jbl Oct 20 '17 at 20:11
  • I have moved away from a static event dispatcher and have my domain rather return events. The application/integration layer would publish/raise events since chances are that another BC may also be interested in an event in this BC and not just other aggregates in the same BC. You would still require some mechanism to perform the publishing but that would be injected into your controller/application layer and be used from there. Just a though :) – Eben Roux Oct 23 '17 at 09:29

1 Answers1

0

Instead of filtering for the correct handlers in your own code, you can use ninject to do that for you:

public class NinjectDomainEventDispatcher : IDomainEventDispatcher
{
    private readonly IResolutionRoot resolutionRoot;

    public NinjectDomainEventDispatcher(IResolutionRoot resolutionRoot)
    {
        this.resolutionRoot = resolutionRoot;
    }

    public void Dispatch<T>(T domainEvent) where T : IDomainEvent
    {
        var handlers = this.resolutionRoot.GetAll<IDomainEventHandler<T>>();
        foreach (var handler in handlers)
        {
            handler.Handle(domainEvent);
        }
    }
}

Pro-Tip: recommended design practice is to move the code dependent on IResolutionRoot (Ninject) to a factory-implementation in the composition root. Instead of doing this manually, you can also make use of Ninject.Extensions.Factory

BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68