Container-friendly domain events


A lot of times an operation on a single aggregate root needs to result in side effects that are outside the aggregate root boundary.  There are several ways to accomplish this, such as:

  • A return parameter on the method
  • A collecting parameter
  • Domain events

We’ve used the default implementation of domain events for quite a while, but with some recent applications I’ve worked on, we’ve noticed one small design issue:

public static class DomainEvents

It’s that big ol’ “static” piece.  Domain events are then raised explicitly by calling this static method, Raise:

public static IHandlerContainer Container { get; set; }

public static void Raise<TEvent>(TEvent args) where TEvent : IDomainEvent
{
    if (Container != null)
        Container.GetAllHandlers<TEvent>()
            .Cast<IHandler<TEvent>>()
            .ForEach(x => x.Handle(args));

Where Container in this case is just a facade over an IoC container.  The silly Cast is from this being C# 3.0 code, the contravariance of C# 4.0 would fix this.  Again, another design issue here.  The reference to the container is static.  This means that more powerful container patterns such as nested containers are out of the picture.  This is too bad, because nested containers are another great tool in the toolbox that lets us delete a lot of code that sets up contexts for things.

The problem here is that I still want to raise events in a static manner from an entity.  I don’t want to have to reference some event pipeline object thingy in my domain objects, and I’m really not keen to start injecting things.  Instead, I want a true, fire-and-forget event.

###

Contextual containers and disposable actions

What I need to do is allow this static method to work with a contextual, scoped piece of code.  But that’s exactly what the “using” statement allows us to do – create a scoped piece of code, that executes something at the beginning (whatever creates the IDisposable) and something at the end (the Dispose method).

To help us create this scoped context to slip in our nested container, we can take advantage of Ayende’s most brilliant piece of code ever written, the DisposableAction:

public class DisposableAction : IDisposable
{
    private readonly Action _callback;

    public DisposableAction(Action callback)
    {
        _callback = callback;
    }

    public void Dispose()
    {
        _callback();
    }
}

I can then just implement a simple method on the DomainEvents class to allow me to swap out – and then restore – the container reference:

public static IDisposable CreateContext(IHandlerContainer container)
{
    var existingContainer = Container;
    Container = container;

    return new DisposableAction(() =>
    {
        Container = existingContainer;
    });
}

I keep a reference around to the previous container, then swap out the DomainEvents’ container for the one passed in.  When this CreateContext is finished, the DisposableAction restores the previous container with a handy closure.

So how do I use this in real code?  Something like:

public void Process<T>(T message) where T : IMessage
{
    using (var nestedContainer = _container.GetNestedContainer())
    using (var unitOfWork = new UnitOfWork(_sessionSource))
    using (DomainEvents.CreateContext(new HandlerContainer(nestedContainer)))

I have several scoped items I’m using to process a message (part of a batch processing program).  Each line in a file gets processed as a single message, with its own unit of work, its own container etc.  It’s now very plain to see the context I create to process the message because I just use the C# feature that creates bounded, self-cleaning contexts: the “using” statement.

This method still isn’t thread-safe, as it still has static elements.  I’ve just allowed a scoped, nested container to be used instead of a single, global static ontainer.  Some folks mentioned patterns like event aggregators, so there are likely other patterns that can help out with the static nature of this domain events pattern.  But for now, I can harness the power and simplicity of nested containers, and keep my handy domain events around as well.

A github first