Posts
1150
Comments
891
Trackbacks
1
A way to handle open generics using dynamics in Simple Injector

Now we get a little more complicated.  Note that there will be a lot of tight coupling, and I’m not talking about CQRS, and I’m sure you can do everything better in many different ways.

Here’s the deal.  I wanted a specific way to handle (pun intended) processing commands and events that wasn’t so manual.  Often times, you create a command, it gets handled, one and only one type of event is created (or an exception is thrown), and then that gets handled.

In which case, I wanted something like this:

Command –> Command Handled –> Command is automatically responded to by an event –> event is handled

When I say “command is automatically responded to by an event”, I mean something like this:

public interface RespondsTo<T> where T : Command.Command { }

public interface RespondsTo<T, TReturnValue> where T : Command<TReturnValue> { }

And this:

public class Event : IEvent, RespondsTo<Command.Command>

public class Event<TReturnValue> : IEvent, RespondsTo<Command<TReturnValue>, TReturnValue>

And this:

public interface IEventBaseBus
    {
        void Dispatch<TEvent>(TEvent @event) where TEvent : Event;
        void Dispatch<TEvent, TReturnValue>(TEvent @event) where TEvent : Event<TReturnValue>;
        void Process<TCommand>(TCommand command) where TCommand : Command.Command;
        void Process<TCommand, TReturnValue>(TCommand command) where TCommand : Command<TReturnValue>;
    }
  

A few notes.  Events never return values, but they can record the returned value of the command they are tied to.  And they are tied to specific commands.  Events don’t just materialize from nothing, or randomly, but from specific things.  This only works if you have halfway decently defined events (so no ProductChangedEvent).  And, more often than not, the properties on your events just come from the command anyway, so you can typically do a lot of auto-mapping (whether you use AutoMapper or not) between them.

So, once a command is handled, it is placed on the event bus for processing, and then, something like this happens:

public void Process<TCommand>(TCommand command) where TCommand : Command.Command
        {
            var regs = _container.GetAllInstances<RespondsTo<TCommand>>();

            foreach (var r in regs)
            {
                Type t = r.GetType();
                dynamic obj = Activator.CreateInstance(t, new object[] { command });
                if (obj is Event)
                {
                    Dispatch(obj);
                }
            }

            return;
        }

You register things in your event bus in largely the same way listed before.  You need the dynamic stuff because you need to be able to dispatch something of type Event.  The registrations give you all those things that respond to the right command, and then get dispatched so that they can be handled:

public void Dispatch<TEvent>(TEvent @event) where TEvent : Event
        {  
            var regs = _container.GetAllInstances<Handles<Event>>();

            foreach (var r in regs)
            {
                r.Handle(@event);
            }
        }

Dealing with commands that have return values is similar.  Processing them is nearly identical:

public void Process<TCommand, TReturnValue>(TCommand command) where TCommand : Command<TReturnValue>
        {
            var regs = _container.GetAllInstances<RespondsTo<TCommand, TReturnValue>>();

            foreach (var r in regs)
            {
                Type t = r.GetType();
                dynamic obj = Activator.CreateInstance(t, new object[] { command });
                if (obj is Event<TReturnValue>)
                {
                    Dispatch<Event<TReturnValue>, TReturnValue>(obj);
                }
            }

        }

But the dispatch code is a bit more complicated:

public void Dispatch<TEvent, TReturnValue>(TEvent @event) where TEvent : Event<TReturnValue>
        {
            Type t = @event.GetType().UnderlyingSystemType;
            Type trv = typeof(TReturnValue);

            Type type = typeof(Handles<,>).MakeGenericType(t, trv);

            var regs = _container.GetAllInstances(type);           

            foreach (var r in regs)
            {
                dynamic hnd = r;
                dynamic objEvent = Convert.ChangeType(@event, t);
                hnd.Handle(objEvent);
            }
        }

In order to get the exact registrations that you need, you have to make sure that you get the proper implementations, the closed type, not the open type.

No doubt all of this could be done in 3 or 4 lines of Ruby (or JavaScript), but this serves my current purposes.

posted on Thursday, January 01, 2015 6:37 PM Print
Comments
No comments posted yet.

Post Comment

Title *
Name *
Email
Url
Comment *  
Please add 1 and 8 and type the answer here: