I have moved my active blog over to tumblr. I've maintained this blog for reference but will be posting to http://www.robustsoftware.co.uk instead. I've pointed my Feedburner feed to tumblr so if you're subscribed already you should already have switched with me.

A better domain event raiser

There was a lot of discussion about domain events at AltNetUK around Udi Dahan's recent article. Please read that first so that you’ll know the context of what I’m about to say, I’ll still be here. Might be useful to have them side-by-side.

I couldn’t help when reading it that the code around event dispatching was quite a mess, with code added in for multiple, distinct scenarios (live and test). This may just have been to help with the formatting of the article, but it leaves a lot to be desired.

What I’m going to do here is show my refactored version of the code and explain why I think it is better. What I’m going to be doing is utilising the strategy pattern to separate the logic of dispatching events from the action of requesting an event to be dispatched.

In figure 1 of the testability section Udi shows how the DomainEvents class ends up. Here is how mine looks after the refactoring:

public static class DomainEvent

{

    public static IEventDispatcher Dispatcher { get; set; }

 

    public static void Raise<TEvent>(TEvent eventToRaise) where TEvent : IEvent

    {

        Dispatcher.Dispatch(eventToRaise);

    }

}

This is made possible because I’ve abstracted the dispatching of the event into a separate class implementing the IEventDispatcher interface:

public interface IEventDispatcher

{

    void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IEvent;

}

There would be two implementations of IEventDispatcher in normal projects. One to be used at runtime such as this one utilising StructureMap:

public class StructureMapEventDispatcher : IEventDispatcher

{

    public void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IEvent

    {

        foreach (var handler in ObjectFactory.GetAllInstances<IEventHandler<TEvent>>())

        {

            handler.Handle(eventToDispatch);

        }

    }

}

And one to be used during unit tests such as this one:

public class ActionEventDispatcher : IEventDispatcher

{

    private readonly IDictionary<Type, Delegate> handlers;

 

    public ActionEventDispatcher()

    {

        handlers = new Dictionary<Type, Delegate>();

    }

 

    public void Register<TEvent>(Action<TEvent> eventAction) where TEvent : IEvent

    {

        // assuming you'll only register once per test

        handlers.Add(typeof(TEvent), eventAction);

    }

 

    public void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IEvent

    {

        if (!handlers.ContainsKey(typeof (TEvent))) return;

 

        var handler = (Action<TEvent>) handlers[typeof (TEvent)];

 

        handler.Invoke(eventToDispatch);

    }

}

You’ll notice that the two event dispatchers are the two sides that can be executed in Udi’s implementation. That is the essence of the strategy pattern.

This does modify the test slightly so that you now have to do this:

[Test]

public void DoSomethingShouldMakeCustomerPreferred()

{

    var c = new Customer();

    Customer preferred = null;

 

    var eventDispatcher = new ActionEventDispatcher();

 

    DomainEvent.Dispatcher = eventDispatcher;

 

    eventDispatcher.Register<CustomerBecamePreferred>

        (p => preferred = p.Customer);

 

    c.DoSomething();

    Assert(preferred == c && c.IsPreferred);

}

I would put the attachment of the ActionEventDispatcher into a test base class so that you didn’t have to set it up for every test, reducing the noise added.

You would also have to add a small bit of code to hook up your real event dispatcher when your application started:

public void SetEventDispatcher()

{

    DomainEvent.Dispatcher = new StructureMapEventDispatcher();

}

Still not sure whether I like the use of domain events or not. However, I did notice this potential refactoring whilst reading the article and thought it was interesting enough to blog about. I think it has left the code in a much better state and I think it also addresses some of the concerns that were raised about the static class at AltNetUK.