« Back
in DomainEvents ninject DDD C# read.

Domain Events Pattern Example.

Domain Events Pattern with Ninject and MVC WebApi Project
This piece is about keeping raising events in your domain models without polluting it with services. We’ll be working off of Udi Dahan’s Domain Events Salvation piece. Hopefully this will be useful to someone out there to see how it’s all tied together with a very minimal amount of code as an example.

You can download the source code on bitbucket here.
I've generally laid out the projects using the onion architecture and should be easy to follow along.

Context

The provided solution is about an online survey. Imagine that a user completes the survey and we’ll want to notify everyone that the survey has ended and do something like send off an email notification and assign a quality assurance checker to go over the survey manually.

The Domain Object

    public class Survey
    {
        public Guid Id { get; private set; }
        public DateTime EndTime { get; private set; }
        public string QualityChecker { get; set; }

        public Survey()
        {
            this.Id = Guid.NewGuid();
        }

        public void EndSurvey()
        {
            EndTime = DateTime.Now;
            DomainEvent.Raise(new EndOfSurvey() { Survey = this });
        }
    }

Our domain object is pretty straight forward with one piece of behaviour: EndSurvey

So where is DomainEvent coming from? It's actually a static class and it's raising a new event which is defined as EndOfSurvey. If you're following along with the source code, i've put all events for the domain objects under an Events folder. Domain objects sit under the Domain folder.

The EndOfSurvey Event

So now our Survey object wishes to raise an event that's called EndOfSurvey. Let's see what that looks like:

    public class EndOfSurvey : IDomainEvent
    {
        public Survey Survey { get; set; }
    }

EndOfSurvey contains an instance of the Survey object so that we can get access to its states and properties. Notice that it implements IDomainEvent so that we know it's a domain event type.
That interface is simply defined as:

public interface IDomainEvent { }  

The DomainEvent Class

Now the all important DomainEvent class which the domain classes use for raising important events:

    public static class DomainEvent
    {
        public static IEventDispatcher Dispatcher { get; set; }

        public static void Raise<T>(T @event) where T : IDomainEvent
        {
            Dispatcher.Dispatch(@event);
        }

    }

The demo itself has a few extra methods but this is the most important bit.

The IEventDispatcher is actually going to be the IoC container such as Ninject or CastleWindsor etc. It is responsible for finding the right handlers to deal with the EndOfSurvey event. There can potentially be more than one and it will find them all.

We don't even have to use an IoC container if we don't want to for dispatching. As long as there's a way for it to attach the appropriate handlers. It simply has to implement the interface below:

    public interface IEventDispatcher
    {
        void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent;
    }

The generic method Raise<T> will allow us to create any number of events and the Dispatcher will sort out the handlers later automatically. This gives us a lot of room for extensibility when a business rule changes and our Survey domain object needs to raise a new type of event e.g. SurveyStarted, UserTimedOut etc.

Lastly we want to define an interface that all the event handlers should implement:

    public interface IDomainHandler<T> where T : IDomainEvent
    {
        void Handle(T @event);
    }

I've put both IEventDispatcher.cs and IDomainHandler.cs in a folder called Services so that other projects must provide a concrete implementation on dispatching the events and handling them.

That's it for the core domain assembly. All POCO classes and no dependencies on other services. So now how do we deal with the domain services bit such as emailing or storing to a database etc?

Defining The Domain Event Handlers

I've created another project which clearly separates out the different event handlers into their own classes. Each time there's a new handler required, I just simply add to this project.

I've made one handler for the EndOfSurvey event called EndOfSurveyHandler.cs as follows:

    public class EndOfSurveyHandler:IDomainHandler<EndOfSurvey>
    {
        public void Handle(EndOfSurvey args)
        {
            args.Survey.QualityChecker = "Ivan Amalo";
            //Send Email To Ivan to check the survey
        }
    }

Pretty simple. We can even use dependency injection in the constructor here if we wanted to for storing things in a repository or any other services required. Clean and separated from the domain object assembly. In this instance, we've statically assigned a person named Ivan Amalo to check this survey.

But how did this EndOfSurveyHandler.cs get attached to that original EndOfSurvey event?

Putting Everything Together

The final project Survey.FrontEnd is the MVC + WebApi application that provides the original DomainEvent.cs with its Dispatcher and wires up the event handlers.

The project depends on the nuget package Ninject.MVC3 and you can read more about how to wire up on Peter Provost's blog here.

So now we need something that implements that dispatcher service we had in the original domain objects assembly. Then we need to wire it into our DomainEvent class as the dispatcher of choice. We'll start by making a concrete implementation of the IEventDispatcher called NinjectEventContainer.cs:

    public class NinjectEventContainer : IEventDispatcher
    {
        private readonly IKernel _kernel;

        public NinjectEventContainer(IKernel kernel)
        {
            _kernel = kernel;
        }

        public void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent
        {
            foreach (var handler in _kernel.GetAll<IDomainHandler<TEvent>>())
            {
                handler.Handle(eventToDispatch);
            }
        }
    }

The Dispatch method will use the kernel in the NinjectWebCommon.cs and find all the handlers that implement IDomainHandler. In our case it's going to be EndOfSurveyHandler and execute its Handle() method.

Inside NinjectWebCommon.cs we define the handlers to the events:

        private static void RegisterServices(IKernel kernel)
        {
            DomainEvent.Dispatcher = new NinjectEventContainer(kernel);
            kernel.Bind<IDomainHandler<EndOfSurvey>>().To<EndOfSurveyHandler>();
        }   

That's the final piece of glue we need to tie everything together.

Testing The Results

I put a break point on the EndOfSurveyHandler.cs file and in the Survey object's Raise event method tomake sure that the event is getting raised and that the appropriate handler is being executed in my MVC solution.

In my MVC Controller I simply have:

        public ActionResult Index()
        {
            var survey = new Core.Domain.Survey();
            survey.EndSurvey();

            return View(survey);
        }

The view should have Ivan Amalo assigned and the current DateTime for EndDate.

Thanks for reading.

comments powered by Disqus