Having fun with hexagonal sails

Hacking sails.js to avoid framework boundaries

December 25, 2014 - 3 minute read -
sailsjs node

There are a lot of talks in rails community about hexagonal architecture and domain driven design, and almost everyone enjoyed an idea of decoupling from rails using stupid simple ruby objects. In this little post I won’t talk about ruby I would like to introduce how awesome it can be done in sails. Hope everyone have seen this naive example in ruby. If not I would suggest to refer to this article

Let’s have a look at this simple rails controller in ruby, here you can see how you can decouple rails specific code from your actual business logic, is that possible in node?

class TorrentsController < ApplicationController
  def index
    TorrentService.new(self).execute
  end

  def success_state
    render text: "Done!"
  end
end

class TorrentService < Struct.new(:boundary)
  def execute
    boundary.sucess_state
  end
end

So a year ago I’ve seen some HN post with a discussion of new rails-like nodejs framework, and I’ve just naively ignored this. Two days ago, don’t know how, I looked again to sails and started simply naive app. And first thing that I wanted to do is play with my favourite concept borrowed from my daily rails development. And it was a little bit non obvious, but after some experiments I’ve reached to the final point and got I wanted to see. Let’s dive into code examples.

Let’s define a simple sails controller:

// api/controllers/TorrentsController.js

module.exports = {
  start: function(req, res) {

  }
}

And configure router of course:

// config/routes.js 
'/': {
  view: 'homepage'
}

Sails has a very good feature that provides an ability to define custom response object extensions. For example: you need to send custom xml respose in you controller, so just simply create api/responders/customXML.js and implement a functionality in this file, and in controller your response object will “magically” have method customXML, and then you can call it like this:

// config/routes.js 
module.exports = {
  start: function(req, res) {
    res.customXML();
  }
}

Returning to our service-related stuff, how can we provide something that will look like our ruby example? Probably, it will be done using two core concepts that sails gives you out of the box.

First we define a service:

module.exports = {
  execute: function(args) {
    // do some hardcore operations
    //...
    //...
    // and return result object
    return {
      action: "ok",
      message: "Success"
    };
  }
}

And in our controller we just declaratively call our service, following Tell-Don’t-Ask principle.

module.exports = {
  start: function(req, res) {
    // telling service perform action
    var result = TorrentService.execute(res);

    // and sending any response we get
    res[result.action](result.message);
   }
 }

Yes I know that it doesn’t look like as ruby example, but for me it looks even more elegant than in ruby.

PS I hope this post was a little bit useful for you, I’m not a sails expert, and I’m just playing with it using in my hobby node.js projects.