The Command pattern applied to your Ember.js application

The Command pattern is one of the patterns for object-oriented software design that is treated in the famous GoF book. The essence of the pattern is to encapsulate the actions in your application into separate objects. Using this pattern appropriately can make your code cleaner and easier to test. I had a situation in the HSTRY application (built on Ember.js) where it was well applicable and I found out that it actually fits well into the architecture of an Ember.js application. Let me elaborate on how to use the Command pattern in an Ember.js application.

Performing an action from different routes

To illustrate my use-case, I’ll give an example. I’m feeling particularly inspired tonight so let’s assume we have an Ember.js application that is a blog. You have a route for all your blog posts, /posts, and a route for a particular blog post, e.g. /post/1. Imagine that you also have a commenting system. So somewhere in your code you have a function addComment(). You can comment on a post from both the /posts route and the route for an individual post. Now where do you put the code for this function? There are several options.

In a common blog-post component

This is the best solution :-). You render the same component for each post on both the /posts route and the route for an individual post and put the implementation of the addComment() function there. But for the sake of this example I want to discard this solution by imagining that the functionality of a blog post is wildly different from whether you view it from the /posts route or /post/:post_id route. So a common component is not possible.

We have a couple of other solutions left.

In one of the controllers for the routes

Let’s say you put it in the controller for /posts. You can then inject this controller into the controller for /post/:post_id. You would have as follows:


// controllers/posts.js

export default Ember.Controller.extend({
  addComment() {
    // The implementation
  },

  actions: {
    postComment() {
      this.addComment();
    }
  }
});

// controllers/post/index.js

export default Ember.Controller.extend({
  postsController: Ember.inject.controller('posts'),

  actions: {
    postComment() {
      this.get('postsController').addComment();
    }
  }
});

There are two problems with this solution:

  • There is not really a reason why you would choose one controller over the other to contain the addComment() function.
  • It creates an unnecessary dependency between the two controllers.

We can find something better. Let’s try with a mixin.

In a mixin that you mix into the controllers

You create a mixin PostMixin that contains the addComment() function and you include it in both of your controllers. So it would look like this:


// mixins/post.js

export default Ember.Mixin.create({
  addComment() {
    // The implementation
  }
});

// controllers/posts.js

import PostMixin from 'mixins/post';

export default Ember.Controller.extend(PostMixin, {
  actions: {
    postComment() {
      this.addComment();
    }
  }
});

// controllers/post/index.js

import PostMixin from 'mixins/post';

export default Ember.Controller.extend(PostMixin, {
  actions: {
    postComment() {
      this.addComment();
    }
  }
});

This is a nicer solution because you isolate the logic of adding a comment in a separate place. You can easily unit-test the mixin which is also an improvement compared to the previous solution. However, the main problems I have with mixins in Ember.js is that:

  • They pollute the namespace of the controller and potentially can create conflicts (although this is unlikely in your own application, you still have to be careful).
  • You have to be very careful with hooks such as activate, deactivate, didInsertElement, beforeModel, etc… They can easily be overridden and their effect nullified if you don’t call this._super();.
  • When you include many mixins, it becomes hard to remember where a particular function is defined. Is it on the controller? Or in a mixin? Which one then? Keeping track of this requires some mental effort and that by itself is an indication that it’s probably not good for the readability and maintainability of your code.

A Command object!

Instead of a mixin, you create a separate object, AddCommentCommand, that has a single function execute(). This function contains the implementation of the addComment() function. This is the essence of the Command pattern. Let’s see how this works for our example.


// commands/add-comment.js

export default Ember.Object.extend({
  execute() {
    // The implementation
  }
});

// controllers/posts.js

import PostCommentCommand from 'commands/post-comment';

export default Ember.Controller.extend({
  actions: {
    postComment() {
      PostCommentCommand.create({
        // Whatever parameters the command needs to execute,
        // for instance the post model
        post: this.get('model')
      }).execute();
    }
  }
});

// controllers/post/index.js

import PostCommentCommand from 'commands/post-comment';

export default Ember.Controller.extend({
  actions: {
    postComment() {
      PostCommentCommand.create({
        // Whatever parameters the command needs to execute,
        // for instance the post model
        post: this.get('model')
      }).execute();
    }
  }
});

This solution offers the same advantages as the mixin solution:

  • It is easily testable. You can test the whole PostCommentCommand object in isolation.
  • It encapsulates the logic of the action into a separate entity instead of being arbitrarily included in one of the two controllers.

However, it does not have the inconveniences of the mixin that I mentioned. In fact, it is a clear example of where composition wins over inheritance.

Now let’s enhance our PostCommentCommand object a bit by having it access services defined in your application.

Accessing the Store and other services from the Command

Because the PostCommentCommand is not registered in the container, it cannot access services in your application. For instance, the Store is a service you would typically use in a Command object. We can easily solve this by registering the Command object in the registry. We do this in an initializer.


// initializers/commands.js

import PostCommentCommand from 'commands/post-comment';

export default {
  name: 'commands',
  initialize: function(container, application) {
    application.register('command:post-comment', PostCommentCommand);
  }
};

Now we also have to instantiate the command through the registry when we use it in the controllers.


// controllers/posts.js

export default Ember.Controller.extend({
  actions: {
    postComment() {
      this.get('container').lookupFactory('command:post-comment').create({
        // Whatever parameters the command needs to execute,
        // for instance the post model
        post: this.get('model')
      }).execute();
    }
  }
});

// controllers/post/index.js

export default Ember.Controller.extend({
  actions: {
    postComment() {
      this.get('container').lookupFactory('command:post-comment').create({
        // Whatever parameters the command needs to execute,
        // for instance the post model
        post: this.get('model')
      }).execute();
    }
  }
});

And now we can inject the Store, or any other service, into the command.


// commands/add-comment.js

export default Ember.Object.extend({
  store: Ember.inject.service(),

  execute() {
    // The implementation
  }
});

There we have it! We have a nicely isolated piece of functionality that has one clear responsibility and that:

  • is easily testable.
  • can access the objects it needs by giving parameters during creation.
  • can access any services you have in your application, i.e. the Store, a logger, the session object,…
  • through the execute() function, defines an interface and a pattern that we can re-use for all actions in our application.

An undo-redo system with the Command pattern

Another use-case where the Command pattern is nicely applicable is a undo-redo system. For that, we need two additional things on top of what we have:

  • Besides an execute() function, each Command needs to have an undo() function that undoes the operations performed in execute().
  • A history of Command objects. This can nicely be implemented as a singleton Ember.js service.

If you encapsulate every action a user can perform in your application as a Command, you keep track of the history by updating a stack of Command objects. When the user presses the undo action, e.g. ‘ctrl+z’, you simply take the latest Command from the stack, call the undo() function on it and move a currentCommand reference to one Command back. For a redo you would take the current Command, apply the execute() function on it and move the currentCommand one Command ahead.

Implementation details are left to the reader but I think this example gives a nice idea of how the Command pattern can be used. You can find more uses on its Wikipedia page.

Conclusion

The Command pattern provides a nice abstraction to encapsulate actions into isolated objects. It allows for clean unit tests and also fits nicely into the Ember.js framework and its way of thinking. Maybe this pattern can help you refactor some of your code into slightly nicer code!

Write us your thoughts about this post. Be kind & Play nice.
  1. Joe Zim says:

    Gotta love composition over inheritance! At first I was confused as to why you would bother using `Ember.object.extend…` in commands/add-comment.js instead of just exporting a function, but it made more sense later. If you have a simple case, functions are a great solution that isn’t so reminiscent of the classic OO languages and instead more like functional languages. I’m always in favor of using functional practices when they simplify things, but you apparently need an object in order to get the injection you needed.

    Reply
    • Yoran Brondsema says:

      I agree Joe! There is definitely such a thing as over-engineering. But in this case indeed, a simple Javascript object nor a function are enough to have the service injections.

      Reply
  2. David Tang says:

    Great post Yoran! Do you find yourself creating lots of custom classes/objects that aren’t part of the standard Ember types (service, component, model, etc)?

    Reply
    • Yoran Brondsema says:

      Glad you like it! There are a couple we found ourselves making. One is Factory objects, to delegate the creation of more complex models (e.g. when it requires more than just a `createRecord()` call). Also we had a use for Presenter objects, which are similar to the decorators or presenters in Rails. Here we needed to augment a model that was passed on to a component with properties that were dependent on a query param. We could have passed the query param to the component and defined the properties on the component. But we needed it in several places from different components so we defined a Presenter object for that model, which we passed on to the component instead of the model. The component “thinks” that he’s accessing properties on the model (the Presenter acts as a proxy) but in fact he’s accessing properties on the Presenter. It works quite well!

      Reply
      • Miguel says:

        That’s interesting, I used proxies a lot of the same purpose, but I never thought of them as presenters. It’s nice seeing them there and it makes the communication easier.

        Do you have separate folders like `app/presenters/` and `app/factories`? I think I dumped them into `utils` or `models`, but giving them their own place could help formalize their role.

        Thanks for the info.

        Reply
        • Yoran Brondsema says:

          Yes, we have separate folders `app/presenters`, `app/factories`, `app/commands`, etc… I think that definitely helps navigating the code.

          Reply
  3. vasilakisfil says:

    Seems like what Rails developers call Service Objects 🙂

    Reply
    • Yoran Brondsema says:

      Exactly :-). I didn’t mention it to avoid confusion with the Ember.js Service objects but Command objects have the same purpose as Rails Service objects.

      Reply
  4. Adnan says:

    I think you may have missed one possible option, which is to implement the action on the Posts route (or any route above that). When you call an action from handlebars, Ember will look up the hierarchy starting from the controller, to the parent route, and up the routes from there for a matching action name. Additionally, you have the option to bubble up parent actions with the same name by returning true.

    Reply
    • Yoran Brondsema says:

      That’s true! But it depends on how you define your routes and I think in this case it would be perfectly legit for the to not share a common Post route. Of course, you can define the action on your ApplicationRoute but assigning such responsibilities to the ApplicationRoute is not a good practice.

      Reply
  5. Jack says:

    Great article, thanks.

    Reply
  6. Dixon says:

    I like the approach but I think you can use a service object and inject it into the component/controllers you need rather than creating a new top level directory for commands. That detail aside, I’m really glad that you wrote about this pattern. I feel like the Ember Way(tm) is to make every object perform a few specific tasks. This pattern is a great way to describe that style.

    Reply
    • Yoran Brondsema says:

      The main problem with that is that Service objects are singletons, so you can’t use them to keep track of a history of actions for instance, which you need for undo-redo. However, if your application doesn’t have that requirement, a Service object can indeed work.

      Reply
      • Miguel says:

        I’m wondering if a “similar” injection API could work. For example:

        “`
        // controllers/posts.js
        export default Ember.Controller.extend({
        actions: {
        postCommentCommand: Ember.inject.command(),
        postComment() {
        this.get(‘postCommentCommand’).execute();
        }
        }
        });
        “`

        I have a few other use cases for something similar, so far we made them services, but we found the same issue with lifetime. I still feel it’s better to use dependency injection instead of using the container as a service locator.

        Reply
  7. This is a very nice pattern. We’ve done the same thing at Dollar Shave Club, but taken it a step further by leveraging ember-data for even less code and more conventional solutions.

    We started off using the mixin approach you described, but that was a problem because the success and failure handlers often needed to do different things based on where they were being used. That logic belonged in the individual controllers.

    We’ve also found that using $.ajax is generally an anti-pattern, since it mixes up adapter and serializer logic with controller logic in the success and failure handlers.

    We now have action models which are equivalent to your command objects. Instead of command.execute() we have model.save(). Though we don’t do this, your command.undo() would become model.delete().

    model:action-add-comment would have an adapter pointing to /api/posts/123/add_comment and the serializer would have payloadKeyFromModelName: () => ‘post’, (for a Rails backend). The controller sets the model properties and handles success and failure.

    The adapter has the endpoint logic, the serializer constructs the payload. Controller actions assemble the payload and handle success and failure. Everything in its right place. =)

    Reply
    • Yoran Brondsema says:

      This is a very interesting use case. Thanks for sharing! I’m wondering, what kind of operations do your controller action objects perform when there’s a failure for instance? I’m interesting because we currently override the `save()` function on models to have our custom error handling but obviously this is not ideal. You brought me to an idea :-).

      By the way, we are using your ember-responds-to addon in our project, very useful!

      Reply
  8. Gary Wilson says:

    How would this work with Ember 2.3 and getOwners?

    Reply