MvcPowerTools Preview: Query and Command Controllers

By Mike on 7 April 2014

MPT gives you a helping hand if you want to use Query or Command Handlers  . While not exactly the same implementation as Jimmy's, it's the same idea, only combined with the Handler Controller approach (in a nutshell, the Controller is the action and its methods handle GET/POST, very similar to WebApi or REST approach). It also makes use of the One Model In, One Model Out convention.

Note that these are opinionated choices, alternatives and not yet another silver bullet solution. Just another way to do things, that might suit your style. Enough said, let's see some code (pasted directly from an app I did recently, and yes, that's ALL the controller and handler code).

public class TagsListController : QueryController<PagedInput, TagsListModel>
    {
        public override ActionResult Get(PagedInput input)
        {
            return Handle(input);
        }
    }

 public class TagsListHandler : IHandleQuery<PagedInput, TagsListModel>
    {
        private readonly IQueryTaxonomyStats _stats;

        public TagsListHandler(IQueryTaxonomyStats stats)
        {
            _stats = stats;
        }

        public TagsListModel Handle(PagedInput input)
        {
            var model = new TagsListModel();
            model.Page = input.Page;
            model.Resources = _stats.GetTagList(input.ToPagination());            
            return model;
        }
    }

Let's see: you have an input model (PagedInput) received by the controller. The controller just asks the base QueryController to handle it, in essence, to return the view (output)model for that input. All that the query controller does is to use the Dependency Resolver to invoke the defined handler that accepts PagedInput and returns TagsListModel. In this specific scenario I'm using a repository, but I could have just used an ORM directly. The handler's job is to assemble the view model and here it's quite trivial.

You may ask yourself why should we bother with this when we could've put that handler behaviour directly in the controller. The answer is: decoupling. TagsListHandler is somewhere outside asp.net mvc, UI, in my app is part of the QueryModel project . Also, as you can see, it doesn't depend on asp.net, controllers, HttpContext etc. It's totally decoupled from the UI and this means it easy to test and it can be reused outside asp.net mvc.

So, TagsListController's job is simply to build the input model (in this case it doesn't have to do anything) that will be use by the query handler. And all controller handling queries look like this one. The important part is the query handler itself, which is just a class from a querying layer.

Now let's see a command controller.

public class AddInformationsController : CommandController<AddInformationsModel,NoResult>
    {

        public ActionResult Get()
        {
            var model = new AddInformationsModel();
            return View(model);
        }

        public override ActionResult Post(AddInformationsModel model)
        {
            return Handle(model,
                d =>
                    this.RedirectToController<BrowseResourceController>(
                        c => c.Get(new BrowseResourceInput() {Type = ResourceType.Informations})));           
        }
	}
	
	 public class AddInformationsHandler:IHandleCommand<AddInformationsModel,NoResult>
    {
        private readonly IDispatchMessages _bus;

        public AddInformationsHandler(IDispatchMessages bus)
        {
            _bus = bus;
        }

        public NoResult Handle(AddInformationsModel model)
        {
            //map model to domain entity
			//add entity to storage
			//publish domain event
            return new NoResult();
        }
    }

 So, I want to add an information. The Get method returns a view with an empty view model. Then, the form is submitted and the Post method is invoked. The command controller will use the DependencyResolver to request and execute a handler which takes an AddInformationsModel input (command) and returns NoResult (if you want to return let's say an id, define a class SomethingId encapsulating the id and use that instead of NoResult). Please excuse the pseudocode comments, the actual code is irrelevant here. The controller's Handle method second argument is a lambda for what to do after the command is handled successfully, in this case a redirect. Again, the advantage is that we keep the command handler outside of UI, decoupled from asp.net mvc. In this example, the handler is part of the Domain (I don't have a Services layer).

As you can see, the controllers are as slim as they can get (1-2 lines of code) while the real logic is in the proper layer. And if you followed closely, you could see a convention at work here:  TagsListController, TagsListModel, TagsListHandler. If the input model had more then a 'page' property, I would have had a TagsListInput class as well. It's easy, even now, after a few good months to go directly to the class I need, when I want to change something.

A final note, this approach really require the use of a DI Container and of course, the handlers need to be registered with one.

Comments (2) -

trailmax
trailmax
7 April 2014 #

Your concept is not new, but your controllers are restrained to have only one query or one command. I'd say you'll run out of names for controllers pretty quickly.

Imagine you have Tag (as for tag-cloud). For Create, Update, Delete you'd have a command each: CreateTagCommand, UpdateTagCommand, DeleteTagCommand. And for read action you'll have a query or two (get single tag and get all tags). In my mind all these actions belong to one controller, not to 2 or 3 controllers.

For these scenarios I'd rather use Mediator pattern. Mediator class is passed a command or a query, then it finds in DI container required command/query handler and executes the handling.

This way your controllers are not restricted with a specific type of command and you can process many commands within the same controller. Something like this: github.com/.../ReportGroupController.cs

Reply

Admin
Admin
7 April 2014 #

I like the controller handler approach and probably I won't run out of names. Btw, the Query/Command controllers are using the mediator. They are helpers for people who want to use both the controller handler approach and command/query. As I've said in the post, these are OPTIONS, ALTERNATIVES not some silver bullet.

Reply

Add comment

biuquote
Loading