Self-Configuring Domain Event Handlers

I recently implemented Domain Events as a way of organising domain logic in our application. To summarise how they work:

  1. Every event in your application is modelled as a class which implements the empty IDomainEvent interface
     
  2. IDomainEvents are raised by a domain object calling a static DomainEvents.Raise<T>(T domainEvent) where T : IDomainEvent method
     
  3. The DomainEvents class passes raised IDomainEvent objects to the Handle<T>(T domainEvent) method of classes which implement the IDomainEventHandler<T> interface for the type of IDomainEvent raised

One task more or less left to the reader in the blog above is how the DomainEvents class finds all the IDomainEventHandlers to which it has to pass IDomainEvents. I (as usual) didn't want to bother registering handlers, so I made some changes to have them register themselves.

So as an example, here's an IDomainEvent which signals that an Order has been placed:

public class OrderPlaced : IDomainEvent
{
    private readonly Order _order;

    public OrderPlaced(Order order)
    {
        this._order = order;
    }

    public Order PlacedOrder
    {
        get { return this._placedOrder; }
    }
}

...and an IDomainEventHandler which sends a confirmation email when the event is raised:

public class NotificationEmailDomainEventHandler : IDomainEventHandler<OrderPlaced>
{
    public void Handle(OrderPlaced domainEvent)
    {
        // send an email using domainEvent.PlacedOrder;
    {
}

The static DomainEvents class is a singleton which uses an IDomainEventHandlerLibrary to access the IDomainEventHandlers to which it should pass raised IDomainEvents. The singleton instance is created by the InjectionService; a static class which wraps the application's dependency injection container and supplies the IDomainEventHandlerLibrary. The IDomainEventHandlerLibrary interface is as follows:

public interface IDomainEventHandlerLibrary
{
    IEnumerable<IDomainEventHandler<T>> GetEventHandlers<T>(T domainEvent);
}

...and the DomainEvents class looks like this:

public class DomainEvents
{
    private static readonly DomainEvents _instance = InjectionService.Resolve<DomainEvents>();

    private readonly IDomainEventHandlerLibrary _handlerLibrary;

    public DomainEvents(IDomainEventHandlerLibrary handlerLibrary)
    {
        this._handlerLibrary = handlerLibrary;
    }

    public static void Raise<T>(T domainEvent) where T : IDomainEvent
    {
        _instance.InstanceRaise(domainEvent);
    }

    private void InstanceRaise<T>(T domainEvent) where T : IDomainEvent
    {
        IEnumerable<IDomainEventHandler<T>> eventHandlers =
            this._handlerLibrary.GetEventHandlers(domainEvent);

        if (eventHandlers != null)
        {
            eventHandlers.ForEach(eh => eh.Handle(domainEvent));
        }
    }
}

A self-configuring implementation of IDomainEventHandlerLibrary then looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

public class SelfConfiguredDomainEventHandlerLibrary : IDomainEventHandlerLibrary
{
    private static Dictionary<Type, List<IDomainEventHandler>> _eventHandlerCache =
        CreateEventHandlerCache();

    public IEnumerable<IDomainEventHandler<T>> GetEventHandlers<T>(T domainEvent)
        where T : IDomainEvent
    {
        if (!_eventHandlerCache.ContainsKey(typeof(T)))
        {
            return null;
        }

        return _eventHandlerCache[typeof(T)].Cast<IDomainEventHandler<T>>().ToArray();
    }

    private static Dictionary<Type, List<IDomainEventHandler>> CreateEventHandlerCache()
    {
        var eventHandlerCache = new Dictionary<Type, List<IDomainEventHandler>>();

        Assembly.GetExecutingAssembly().GetAvailableTypes(
            typeFilter: t => !t.IsInterface && typeof(IDomainEventHandler).IsAssignableFrom(t))
            .Select(t => (IDomainEventHandler)InjectionService.Resolve(t))
            .ForEach(eh =>
            {
                eh.GetType()
                    .GetInterfaces()
                    .Where(it =>
                        it.IsGenericType
                        &&
                        typeof(IDomainEventHandler).IsAssignableFrom(it))
                    .ForEach(it =>
                    {
                        Type handledDomainEventType = it.GetGenericArguments().First();

                        if (!eventHandlerCache.ContainsKey(handledDomainEventType))
                        {
                            eventHandlerCache.Add(
                                handledDomainEventType,
                                new List<IDomainEventHandler>());
                        }

                        eventHandlerCache[handledDomainEventType].Add(eh);
                    });
            });

        return eventHandlerCache;
    }
}

A couple of things to note:

  1. The generic IDomainEventHandler<T> interface is derived from an empty, non-generic IDomainEventHandler interface in order for this to work
     
  2. The Assembly.GetAvailableTypes() method is an extension method of mine

Print | posted @ Thursday, July 21, 2011 8:05 PM

Comments on this entry:

Gravatar # re: Self-Configuring Domain Event Handlers
by Tasio at 7/12/2013 1:13 PM

Hi,
Nice article.
Is the InjectionService class available somewhere?

Thanks
Gravatar # re: Self-Configuring Domain Event Handlers
by Steve Wilkes at 7/12/2013 1:35 PM

Hi Tasio,

Thanks :)

I've not got the InjectionService code anywhere public at the moment, but it's just a static wrapper class for an injection container; you set the container up in the InjectionService static constructor, then call the container's instance methods via the InjectionService's static methods.
Gravatar # re: Self-Configuring Domain Event Handlers
by Tasio at 7/12/2013 3:26 PM

Got it, thanks!
Post A Comment
Title:
Name:
Email:
Comment:
Verification: