Moving on from MVC: CQRS

Aug 19, 2015 Joseph Villafranca

Do you sometimes forget what your models model? Are your controllers getting out of control? Modern MVC frameworks like Ruby on Rails and Laravel have made it extremely easy to get full-fledged web applications production-ready at blazingly fast speeds. With the help of CRUD style resourceful controllers a small team, sometimes consisting of one, can launch a ReSTful web application complete with users, blog posts, comments, and administrative abilities within a matter of hours. It's a beautiful thing when it bears the weight of a project's scope, but we all know projects can quickly grow into codebases that become nightmares to expand and maintain without solid organization. Command Query Responsibility Segregation (CQRS) is one of the patterns we at Grok have used when our MVC based applications evolve into more sophisticated pieces of software.

CQRS is a simple, yet powerful design pattern you can use to keep your models and controllers (and views, if you like to abuse every part of the MVC acronym) dry. The main idea behind it is that all actions being done in those hundreds (thousands?) of lines of code in your controllers and models should be separated into Commands -- actions that write data, and Queries -- actions that read data. Don't ever mix the two, EVER. Doing so would be breaking the one and only rule of CQRS. Commands should always have a void return type, thus altering data but not returning anything, and Queries should always return some type of data without making any changes to the system.

There seems to be some debate (what a surprise) between proponents of CQRS over whether or not it requires the use of different models for writing and reading data. Separate models at this layer in your application can help begin to pave the way into other design patterns that further the versatility of your software such as Task-based UI, Event Sourcing, and Domain Objects.

Every layer of software adds to the complexity of development and upkeep though, so making use of the same model for reads/writes while still tasking different objects to command and query may be the most viable solution for certain projects:

Consider the following code you might see in a Laravel controller:

<?php class UserController { public function index() { if ($user_preferences = Auth::user()->preferences) { $users = User::where('region', $user_preferences->region) ->where('group', $user_preferences->group) ->where('role', $user_preferences->role) ->paginate(20); } else { $users = User::all()->paginate(20); } return View::make('users.index')->with('users', $users); } public function store() { $input = Input::all(); $user = User::where('email', $input['email'])->first(); // Check to see if email exists if ($user) { $message = 'Email already exists'; return Redirect::back()->with('message', $message); } // Validate input $user_attributes = array_intersect($input, User::$rules); $validator = Validator::make($user, $user_attributes); if ($validator->fails()) { return Redirect::back()->withErrors($validator); } // Store User $user = new User; $user->first_name = $input['first_name']; $user->last_name = $input['last_name']; $user->email = $input['email']; $user->password = Hash::make($input['password']); $user->region = $input['region']; if (!$user->save()) { $message = 'Unknown Error Occurred'; return Redirect::back()->with('message', $message); } return Redirect::action('Controller@index'); } }

There isn't a lot of logic being executed here. The index method queries with the User model for users with pagination and optional search preferences if a relational model for search preferences is found. The store method does a couple of basic validation steps and then attempts to create the new user with attributes set in the POST request.

It still amounts to over 50 lines of code! That may not seem like much of a problem to you, but it also does nothing to limit the potential growth of these methods over time. What happens when the user entity doubles in size?

Now lets look at the same controller using separate command and query services as injected dependencies:

<?php use app\Users\UserCommandService; use app\Users\UserQueryService; class UserController { public function construct(UserCommandService $user_command_service, UserQueryService $user_query_service) { $this->user_command_service = $user_command_service; $this->user_query_service = $user_query_service; } public function index() { $users = $this->user_query_service->getAllUsersWithSearchPreferences(); return View::make('users.index')->with('users', $users); } public function store() { $this->user_command_service->createUserCommand(); return Redirect::action('Controller@index'); } }

Simply moving all logic from the controller to service classes separated by reading from and writing to our storage has trimmed the code down by more than half the amount of lines, and better yet, has helped define a single action being taken in each of our controller actions.

If you search the web for CQRS you will no doubt run into larger software design concepts like Domain Driven Design or Event Sourcing and quickly be tempted to jump into the wormhole of information covering them (or jump off the nearest ledge). Don't do either! At least for the time being. Stick to understanding the simple building block that is CQRS and you will improve your code dramatically.

Sources