How I use domain events with fubuMVC–part one

My fubuMVC series is back and with an interesting one hopefully – how I use domain events and work it into my fubuMVC application.
To illustrate this I’m going to use the registration process as a prime example of domain events in action within an application.

When a user registers, I want to create a new user who is persisted to the database and a confirmation email sent to the user.
Using domain events for this I want the UserCreated event to signal that the user needs persisting so that this is loosely coupled but I only want the email sent if the entire previous transaction was successful.

So I’m going to start with an event that has an empty marker interface IDomainEvent:

    public class UserCreated : IDomainEvent
    {
        public UserCreated(User user)
        {
            User = user;
        }

        public User User { get; private set; }
    }

In order to handle this event, I’m going to create a domain event handler abstraction:

    public interface IHandleDomainEvents { }

    public interface IHandleDomainEvent<T> : IHandleDomainEvents
    {
        AfterEventConfirmationAction Handle(T domainEvent);
    }

    public delegate void AfterEventConfirmationAction();

This interface can handle a domain event T and has the option of returning an action that will happen once the event has been confirmed (i.e. transaction is complete).
I chose a strong delegate as opposed to Action so that is has a recognisable name throughout the code. Here are two pseudo implementations from the registration example:

    public class PersistTheCreatedUserHandler : IHandleDomainEvent<UserCreated>
    {
        public AfterEventConfirmationAction Handle(UserCreated domainEvent)
        {
            // persist the user
            session.Save(user);

            return Do.Nothing();
        }
    }

    public class EmailForTheCreatedUserHandler : IHandleDomainEvent<UserCreated>
    {
        public AfterEventConfirmationAction Handle(UserCreated domainEvent)
        {
            return () => SendEmail();
        }
    }

The difference between the two is that the persistence will happen within a transaction, yet the send email will occur only once all events have been confirmed.

Now onto the domain events processor – I want it injected with all the available handlers so that it can cycle through the appropriate ones and ask them to handle the event:

    public class DomainEventProcessor : IProcessDomainEvents
    {
        readonly IEnumerable<IHandleDomainEvents> _domainEventHandlers;
        readonly List<AfterEventConfirmationAction> _temporaryHolderForAfterEventConfirmationActions = 
                                                              new List<AfterEventConfirmationAction>();

        public DomainEventProcessor(IEnumerable<IHandleDomainEvents> domainEventHandlers)
        {
            _domainEventHandlers = domainEventHandlers;
        }

        public void ProcessEvent<T>(T domainEvent) where T : IDomainEvent
        {
            _domainEventHandlers.OfType<IHandleDomainEvent<T>>()
                                .ForEach(handler => HandleTheDomainEvent(domainEvent, handler));
        }

        void HandleTheDomainEvent<T>(T domainEvent, IHandleDomainEvent<T> handler) where T : IDomainEvent
        {
            var afterEventConfirmationAction = handler.Handle(domainEvent);

            _temporaryHolderForAfterEventConfirmationActions.Add(afterEventConfirmationAction);
        }
    }

The processor ensures the domain events get handled and I don't have error handling to ensure all are fired, I personally let the error roll up and fail the entire transaction.
Temporarily I’m storing the event confirmation actions and doing nothing with them – the next post will illustrate how I hook this into fubuMVC.

The final piece is an easy way for domain objects to raise events:

    public static class DomainEvents
    {
        static Func<IProcessDomainEvents> _domainEventProcessorProvider;

        public static void SetDomainEventProcessorProvider(Func<IProcessDomainEvents> 
                                 domainEventProcessorProvider)
        {
            _domainEventProcessorProvider = domainEventProcessorProvider;
        }

        public static void Raise<TEvent>(TEvent domainEvent) where TEvent : IDomainEvent
        {
            var domainEventProcessor = _domainEventProcessorProvider();
            domainEventProcessor.ProcessEvent(domainEvent);
        }
    }

I do not like implementations in my static classes and methods, rather they delegate out as this one does using a static provider/factory to obtain the processor to use. Here's an example of this static in use:

        DomainEvents.Raise(new UserCreated(user));

In the next part I’ll show how these get hooked into the fubuMVC request and interact with my unit of work behaviour.



0 comments: