Saturday, November 6, 2010

DDD, CQRS, Object Databases (Db4o), MVC3 Razor, Really?! (1)

Part 1: Register a User (Commands and Handlers)


Part 2 - Events and Event handlers

Well, I haven't posted in a while, but that doesn't mean I haven't been coding.

Ignore what I say and just go git it at git@github.com:elliottohara/myDojo.git if ya wanna.

Been doing a lot of CQRS stuff lately, Dale and I where actually able to convince the powers that be to let us give it a shot at work on a brand new project. Well, I've learned a whole lot from that project, and figured I'd give a shot at one at home and try to put what I've learned to use.

If you're not familiar with CQRS, well the google it... Actually, you can read the next few paragraphs if you wanna, but it's not gunna explain it as good as the first suggestion.

Command Query Responsibility Segregation.  The idea is that the responsibilities of "doing stuff" and "looking at stuff" are fundamentally different. To mix them into one object (some domain entity) brings no value to a domain, actually they needlessly complicate it.  The idea is that our domain entities don't carry state except what is needed to effect behavior, nothing more.  Would what a person's name modify his behavior at all? Probably not, so a person entity in a DDD / CQRS system would NOT have a Name property.

What the hell? How do I know who he is? Glad you asked! That's a query, not a command. To get that information, we have readmodels. The whole point of read models is to build things to display on a screen. They can (and probably should) be de-normalized to make the system easily get just the info it needs to paint a screen.

To keep the read side decoupled from the write (command side) the system communicates via domain events.  Check out Udi's blog on domain events.  It really makes a lot of sense. But it's dramatically departed from N-Tier development. To me, that's a good thing. To other's maybe not. Since this is my blog, I'm right and they're not. In the real world? You tell me...

So, I figured I'd make this cute little site for martial artists.  Being a Brazillian Jiu Jitsu guy, I've wanted to create something to track my BJJ lineage and to let the guys I train with communicate, review other schools. Talk techniques, trash, yada, yada.

For the project here's what I wanted to do.

  • Use strong DDD with CQRS
  • Use an Object Database for persistence (no RDBMS at all)
  • Play with MVC3 and the new Razor view engine.
  • Use the MVC stack to automagically execute handlers for commands, so controllers are nice thing (see Jimmy Bogard's blog on MVC Action Results)

Well, first things first. People gotta sign up, right?

In keeping with Udi's don't create aggrigate roots principle. I created a simple Referrer object. Since Members don't just appear, I have this object create my Members - since that is the way it happens real world right? People get to the site somehow, and then they register.

   1:  using Db4objects.Db4o;
   2:  using myDojo.Infrastructure;
   3:  using myDojo.Infrastructure.Db4o;
   4:  using User = myDojo.Domain.Users.User;
   5:   
   6:  namespace myDojo.Domain
   7:  {
   8:      public class RefererRepository : Db4OAggrigateRootRepository<Referer>, IAggrigateRootRepository<Referer>
   9:      {
  10:          public RefererRepository(IObjectContainer db) : base(db)
  11:          {
  12:          }
  13:   
  14:          #region IAggrigateRootRepository<Referer> Members
  15:   
  16:          public void Store(Referer entity)
  17:          {
  18:              // only care about the user
  19:              foreach (User user in entity.UsersBrought)
  20:              {
  21:                  Db.Store(user);
  22:              }
  23:          }
  24:   
  25:          #endregion
  26:   
  27:          public Referer WithUrl(string url)
  28:          {
  29:              return new Referer(url);
  30:          }
  31:      }
  32:  }

What does all this do? So we just have a list of Users that we append to when Referer.BroughtUser(string) is called. Are we gunna store every url that everyone visits from? No, we're not. If I where using NHibernate, I could just write an interceptor. I'm not for this project though. I'm using Db4o. To my knowledge, Db4o doesn't support interceptors like this. I have NO interest in writing some proxy class for this so I just created a special repo for Referrers. Here she is...


   1:  using System;
   2:  using Db4objects.Db4o;
   3:  using myDojo.Infrastructure;
   4:   
   5:  namespace myDojo.Domain
   6:  {
   7:      public class RefererRepository : IAggrigateRootRepository<Referer>
   8:      {
   9:          private readonly IObjectContainer _db;
  10:   
  11:          public RefererRepository(IObjectContainer db)
  12:          {
  13:              _db = db;
  14:          }
  15:   
  16:          public void Store(Referer entity)
  17:          {
  18:              // only care about the user
  19:              foreach (var user in entity.UsersBrought)
  20:              {
  21:                  _db.Store(user);
  22:              }
  23:          }
  24:          public Referer WithUrl(string url)
  25:          {
  26:              return new Referer(url);
  27:          }
  28:          public Referer GetById(Guid id)
  29:          {
  30:              return new Referer(null){Id = id};
  31:          }
  32:      }
  33:  }
 

So that's the only way we add Members to our system. By getting the Referrer from a repo (that always returns something) and we just store the member when the referrer calls BroughtUser.  Ok, simple enough... Now, in keeping with the CQRS pattern we're going to make a command for the UI to consume.  I made a marker interface.


   1:  namespace myDojo.Infrastructure.CQRS.Commands
   2:  {
   3:      public interface ICommand
   4:      {
   5:          
   6:      }
   7:  }

Why the iface? Ok, you win... It's not really needed, but I just find it easier to mark my commands with a interface. Yeah, it's technically not POCO anymore, but this is my code and my blog, so get over it.


Here's the command.


   1:  using myDojo.Infrastructure.CQRS.Commands;
   2:   
   3:  namespace myDojo.Commands.Users
   4:  {
   5:      public class RegisterUser : ICommand
   6:      {
   7:          public string EmailAddress { get; set; }
   8:          public string ReferrerUrl { get; set; }
   9:   
  10:          public RegisterUser(string emailAddress,string referrerUrl)
  11:          {
  12:              EmailAddress = emailAddress;
  13:              ReferrerUrl = referrerUrl;
  14:          }
  15:      }
  16:  }

For every command, we have 1 and only one handler. I made the following interface for my handlers.


   1:  namespace myDojo.Infrastructure.CQRS.Commands
   2:  {
   3:      public interface ICommandHandler<in T> where T:ICommand
   4:      {
   5:          ICommandHandlerResult Handle(T command);
   6:      }
   7:  }

Oh yeah, that Result thingy... Ignore it for now... Just pretend it's a void.Let's look at the handler for Register user.


   1:  using System;
   2:  using myDojo.Commands.Users;
   3:  using myDojo.Domain;
   4:  using myDojo.Infrastructure.CQRS.Commands;
   5:  namespace myDojo.CommandHandlers.Users
   6:  {
   7:      public class RegisterUserHandler : ICommandHandler<RegisterUser>
   8:      {
   9:          private readonly RefererRepository _refererRepository;
  10:   
  11:          public RegisterUserHandler(RefererRepository refererRepository)
  12:          {
  13:              _refererRepository = refererRepository;
  14:          }
  15:   
  16:          public ICommandHandlerResult Handle(RegisterUser command)
  17:          {
  18:              var referrer = _refererRepository.WithUrl(command.ReferrerUrl);
  19:              referrer.BroughtUser(command.EmailAddress);
  20:              _refererRepository.Store(referrer);
  21:              return Results.Success();
  22:          }
  23:      }
  24:  }

Ok, now we've got a Command and a Handler. But this would totally suck without some infrastructure in place to just know what handler to use for the command right? I shamelessly ripped off some Jimmy Bogart stuff for this. It's not a copy of his code, but I got the idea from here, so he gets the credit. Here's my CommandActionResult class


   1:  using System;
   2:  using System.Web.Mvc;
   3:  using myDojo.Infrastructure.CQRS.Commands;
   4:  using StructureMap;
   5:   
   6:  namespace myDojo.Infrastructure.Web
   7:  {
   8:      public class CommandActionResult<TCommand> : ActionResult where TCommand:ICommand
   9:      {
  10:          private static IContainer Container
  11:          {
  12:              get
  13:              {
  14:                  return ServiceLocation.CurrentContainer;
  15:              }
  16:          }
  17:          public Func<ActionResult> Success { get; set; }
  18:          public Func<ICommandHandlerResult, ActionResult> CommandFailedResult { get; set; }
  19:          public Func<ActionResult> ValidationFailedResult { get; set; }
  20:          public TCommand Command { get; set; }
  21:          public override void ExecuteResult(ControllerContext context)
  22:          {
  23:              
  24:              if (!context.Controller.ViewData.ModelState.IsValid)
  25:              {
  26:                  ValidationFailedResult().ExecuteResult(context);
  27:                  return;
  28:              }
  29:              ICommandHandlerResult result = null;
  30:             
  31:              var handler = Container.GetInstance<ICommandHandler<TCommand>>();
  32:              result = handler.Handle(Command);
  33:              if(result is Success)
  34:              {
  35:                  Success().ExecuteResult(context);
  36:                  return;
  37:              }
  38:              CommandFailedResult(result).ExecuteResult(context);
  39:          }
  40:      }
  41:   
  42:      
  43:  }

So when a CommandMethodResult is used, it'll use StructureMap to find the ICommandHandler for that command, and call the Handle method. It also checks that the form elements pass validation (thanks Jimmy!). Then dependending on what the command returns (remember me saying ignore that return Success stuff?) it'll either execute the Success or CommandFailedResult. Pretty nice huh? Except that newing up one of those puppies in a controller would be ugly as the south end of a north bound mule. Since I'm not into code that looks like an asses ass, I just wrote (copied) a convience method in my default controller.


   1:  using System;
   2:  using System.Web.Mvc;
   3:  using myDojo.Infrastructure.CQRS.Commands;
   4:   
   5:  namespace myDojo.Infrastructure.Web
   6:  {
   7:      public class DefaultController : Controller
   8:      {
   9:          
  10:          public DefaultController()
  11:          {
  12:              DefaultFailureResult = () => View();
  13:              DefaultCommandFailedResult = c => View();
  14:          }
  15:          protected Func<ActionResult> DefaultFailureResult { get; set; }
  16:          protected Func<ICommandHandlerResult, ActionResult> DefaultCommandFailedResult { get; set; }
  17:          public CommandActionResult<TCommand> Command<TCommand>(TCommand command,Func<ICommandHandlerResult,ActionResult> commandFailedResult, Func<ActionResult> validationFailedResult, Func<ActionResult> successResult) where TCommand:ICommand
  18:          {
  19:              var commandActionResult = new CommandActionResult<TCommand>
  20:                                            {
  21:                                                Command = command,
  22:                                                CommandFailedResult = commandFailedResult,
  23:                                                ValidationFailedResult = validationFailedResult,
  24:                                                Success = successResult
  25:                                            };
  26:              return commandActionResult;
  27:          }
  28:          public CommandActionResult<TCommand> Command<TCommand>(TCommand command,Func<ActionResult> success) where TCommand:ICommand
  29:          {
  30:              return new CommandActionResult<TCommand>
  31:                         {
  32:                             Command = command,
  33:                             CommandFailedResult = DefaultCommandFailedResult,
  34:                             ValidationFailedResult = DefaultFailureResult,
  35:                             Success = success,
  36:                         };
  37:          }
  38:          
  39:      }
  40:  }

So, lets see it in action. Just the method we care about for now.


   1:   [HttpPost]
   2:          public CommandActionResult<RegisterUser> Register(string email)
   3:          {
   4:              return Command(new RegisterUser(email, null), () => RedirectToAction("Edit", new {email}));
   5:          }

So, there ya go... We're now actually firing the command's handler. Next blog, I'll show you how I'm using Domain Events to talk to my Query assembly to talk to the read models and get them updated. Or, you can just go read Udi's blog on how it's done - his is much better than anything I'll write, but whatever.

2 comments:

  1. hi there:
    Nice post, tho I started reading it and I though, the definition of CQRS is different to what I understand of it. AFAIK CQRS is simply a pattern for describing the separation between Command (any method that can change state) and Query( pretty much a method that can only query data) and in your definition you are implying that you need entities and that they must not carry state (I think thats called Ask dont tell), which it s a good idea, but its a different thing althogher.
    What i m trying to say is that you could do CQRS when entities do carry state, you can even do CQRS without entities at all.
    I m looking forward to to more posts

    ReplyDelete
  2. Thanks for the comment @roundcrisis.

    Yeah, you're right, I didn't mean to imply that CQRS by definition means that entities can't carry read model state. Reading the post again, I certainly did say that.

    maybe I should have said "does not need a" instead of "would not have a" :)

    You're also entirely correct that CQRS by definition really has nothing to do with entities at all.

    I guess this post is more my opinions on a well designed system and what we can do because we use CQRS.

    Thanks for the input!

    ReplyDelete