Monday, January 24, 2011

My own little CQRS framework - that doesn't piss off "the other guys"

 

Git This: git://github.com/elliottohara/Ellemy.CQRS.git

Took a look at NCQRS today. It pretty much rocks. I have one problem though. I having problems selling CQRS at work, mainly because many other engineers just even have a strong grasp of Domain Driven Design, much less CQRS.  Trying to sell something like NCQRS, as awesome as it is at doing what it's designed to do just wouldn't fly in my org.

I figured I'd create a little lightweight solution that is how I typically handle CQRS is my "mainstream" organization where I have to fight the political battles with DBAs and other engineers who think I'm just a alt.net elitist... I'm quite sure I'm not alone.

Requirements:

  • Easy to understand and use- even for developers who have very little understanding of DDD and CQRS
  • Doesn't even pretend to embrace event stores or alternative persistence mechanisms - I live in a world where that'll just create a backlash against the whole idea of CQRS
  • Doesn't depart from the stuff that the CQRS crowd espouses - so we can point to his blog when the slight learning curve is encountered, we can point at the large community, and show that this isn't something that we cooked up. At the same time, let's not scare people off by requiring busses and stuff.
  • Have fun doing it :)

Ok... so lets start. First of all we're ripping off Domain Events - Salvation, except (as silly as this sounds) I'm gunna stick to all the traditional naming conventions. Also I'm making a Factory to locate all handlers, that way we don't have to retouch this when the folks that think IOC containers are "redirection tools intended to confuse pragmatic developers" don't wanna use StructureMap and want to go manually wire up every handler so that they don't get confused.

Here's what my new DomainEvents class looks like.

   1:  using System;
   2:  using System.Collections.Generic;
   3:   
   4:  namespace Ellemy.CQRS.Event
   5:  {
   6:      public static class DomainEvents
   7:      {
   8:          [ThreadStatic] //so that each thread has its own callbacks
   9:          private static List<Delegate> actions;
  10:   
  11:          public static IHandlerFactory Container { get; set; } //as before
  12:   
  13:          //Registers a callback for the given domain event
  14:          public static void Register<T>(Action<T> callback) where T : IDomainEvent
  15:          {
  16:              if (actions == null)
  17:                  actions = new List<Delegate>();
  18:   
  19:              actions.Add(callback);
  20:          }
  21:   
  22:          //Clears callbacks passed to Register on the current thread
  23:          public static void ClearCallbacks()
  24:          {
  25:              actions = null;
  26:          }
  27:   
  28:          //Raises the given domain event
  29:          public static void Raise<T>(T args) where T : IDomainEvent
  30:          {
  31:              if (Container != null)
  32:                  foreach (var handler in Container.GetHandlersFor<T>())
  33:                      handler.Handle(args);
  34:   
  35:              if (actions != null)
  36:                  foreach (Delegate action in actions)
  37:                      if (action is Action<T>)
  38:                          ((Action<T>) action)(args);
  39:          }
  40:      }
  41:  }

and the IHandlerFactory looks like this


   1:  using System.Collections.Generic;
   2:   
   3:  namespace Ellemy.CQRS.Event
   4:  {
   5:      public interface IHandlerFactory
   6:      {
   7:          IEnumerable<IDomainEventHandler<TEvent>> GetHandlersFor<TEvent>() where TEvent : IDomainEvent;
   8:      }
   9:  }

I think I also wanna move that handler.Handle(args) into another concept at some point in this. It would be nice to at least async this (or yeah, just publish to a bus, but yeah, let's not scare people!).


Simple enough. Gunna do the exact same thing for the command side of the house.


Made a maker for Commands named ICommand, and this


   1:  namespace Ellemy.CQRS.Command
   2:  {
   3:      public interface ICommandExecutor<TCommand> where TCommand:ICommand
   4:      {
   5:          void Execute(TCommand command);
   6:      }
   7:  }

Once again, lets create a Factory interface so that, given a command, we can locate its single executor - we'll create an implementation that uses my IOC container of choice in a bit.


   1:  namespace Ellemy.CQRS.Command
   2:  {
   3:      public interface ICommandExecutorFactory
   4:      {
   5:          ICommandExecutor<TCommand> GetExecutorFor<TCommand>() where TCommand : ICommand;
   6:      }
   7:  }

 


Really, that's all the abstract stuff we're gunna need to make it work. Now we need to wire it all up so that it'll actually work. I'm gunna gunna create some kinda bootstrap thingy that'll set the factories that the command and event side need. I really like the extension model that NServiceBus and NCQRS use, so I'll give something like that a shot.


Here's what I came up with.


   1:  using System;
   2:  using Ellemy.CQRS.Command;
   3:  using Ellemy.CQRS.Event;
   4:   
   5:  namespace Ellemy.CQRS
   6:  {
   7:      public static class Configure
   8:      {
   9:          private static Configuration _currentConfig;
  10:          internal static Configuration CurrentConfig{get { return _currentConfig ?? (_currentConfig = new Configuration()); }
  11:          }
  12:          public static Configuration With()
  13:          {
  14:              return _currentConfig;
  15:          }
  16:          
  17:      }
  18:      public class Configuration
  19:      {
  20:          public Configuration HandlerFactoryOf(IHandlerFactory handlerFactory)
  21:          {
  22:              HandlerFactory = handlerFactory;
  23:              return this;
  24:          }
  25:          public Configuration CommandExecutorFactoryOf(ICommandExecutorFactory commandExecutorFactory)
  26:          {
  27:              CommandExecutorFactory = commandExecutorFactory;
  28:              return this;
  29:          }
  30:   
  31:          internal ICommandExecutorFactory CommandExecutorFactory { get; private set; }
  32:   
  33:          internal IHandlerFactory HandlerFactory { get; private set; }
  34:      }
  35:  }

 


The idea is that we'll make a new assembly that'll have the concrete implementations of the two factories, and then create Extension methods on Configuration that'll simply call HandlerFactoryOf and CommandExecutorFactoryOf.


But, lets slow down first, and write some concrete factories. I'm gunna use StructureMap. This is really simple... Here we go.


   1:  using System;
   2:  using System.Collections.Generic;
   3:  using Ellemy.CQRS.Command;
   4:  using Ellemy.CQRS.Event;
   5:  using StructureMap;
   6:   
   7:  namespace Ellemy.CQRS.Implementations.StructureMap
   8:  {
   9:      public class StructureMapBuilder : IHandlerFactory, ICommandExecutorFactory
  10:      {
  11:          private readonly IContainer _container;
  12:   
  13:          public StructureMapBuilder(IContainer container)
  14:          {
  15:              _container = container;
  16:          }
  17:   
  18:          public IEnumerable<IDomainEventHandler<TEvent>> GetHandlersFor<TEvent>() where TEvent : IDomainEvent
  19:          {
  20:              return _container.GetAllInstances<IDomainEventHandler<TEvent>>();
  21:          }
  22:   
  23:          public ICommandExecutor<TCommand> GetExecutorFor<TCommand>() where TCommand : ICommand
  24:          {
  25:              return _container.GetInstance<ICommandExecutor<TCommand>>();
  26:          }
  27:      }
  28:  }
Now, lets go ahead and build some extensions on Configuration that specifies this object as both the Handler and CommandExecutor factories. Kinda like this.
 

   1:  using System;
   2:  using StructureMap;
   3:   
   4:  namespace Ellemy.CQRS.Implementations.StructureMap
   5:  {
   6:      public static class ConfigurationExtensions
   7:      {
   8:          public static Configuration StructureMapBuilder(this Configuration config)
   9:          {
  10:              return config.StructureMapBuilder(ObjectFactory.Container);
  11:          }
  12:          public static Configuration StructureMapBuilder(this Configuration config, IContainer container)
  13:          {
  14:              var builder = new StructureMapBuilder(container);
  15:              return config
  16:                  .CommandExecutorFactoryOf(builder)
  17:                  .HandlerFactoryOf(builder);
  18:          }
  19:      }
  20:  }
 
 
Ok, now lets use it, but I’ll blog that next. Or you can just git the code… there’s a little example in there of using it.
 
Have fun with it!

2 comments: