Warning: Use of undefined constant minor - assumed 'minor' (this will throw an Error in a future version of PHP) in /nfsmnt/hosting1_1/2/b/2bfe6843-ec81-43e4-bed4-bdf0e428cf7c/dendroid.sk/web/wp-config.php on line 5
Start using Command Objects in Grails - dendroid

Using Grails forces you to implement MVC architecture (Grails uses Spring MVC internally). Which is nice. Convention says, views communicate with controllers, which then uses services to alter models.This set-up is quite straightforward for simple applications, but having complex business logic with huge user input can make this a mess. That, and much more can be solved using Command Objects.

Controller without Command Objects

Let’s illustrate on a controller from my previous post Grails – Groovy On Rails first project.

class UserController {
  def userService 

  def add() {
    def user = userService.add(params.firstName, params.lastName) 
    user ? render (text: "OK") : render (status: 400) 
  }
}

This is simple user addition. No validations, no logic used. Let’s add some to it so we can work with it.

class UserController {
  def userService 

  def add() {
    def gender
    if (!params.firstName || params.firstName.size() < 3 ||
        !params.lastName || params.lastName.size() < 3) {
      render (text: "name should be at least 3 characters long", status 400)
      return
    }
    if (!params.gender || !params.gender in ["M", "F"]) {
      render (text: "gender should be M/F", status 400)
      return
    } else {
      gender = params.gender == "M" ? "male" : "female"
    }
    if (userService.findByFirstAndLastName(params.firstName, params.lastName)) {
      render (text: "user already exists", status 400)
      return
    }

    def user = userService.add(params.firstName, params.lastName, gender) 
    user ? render (text: "OK") : render (status: 400)    
  }
}

Adding input validations to the method does not make it more readable. Input is not our domain, so we can not validate it directly there. Also in case you need to transform inputs into other objects (in this case M/F into male/female) someplace outside the controller, you will probably end up with utility class or service. That is not a way to go. Rather than filling controllers with validations and transformations, there is a elegant way to do so.

Adding Command Object

It's something between domain and service, served as layer between view and controller. Most important thing is, that it will clean the controller. Command object will automatically bind the input, whether it is url encoded param or post body as json or xml. For now, it is not possible to bind url attributes along the body object, but there is a official bug issued for quite some time, so i hope it will be taken care of eventually.

You can add constraints to it like you are used to with domains and also you can add util methods that are directly relevant to the input. Also bean injection works, so you can use services within. It is a pretty neat tool to make the code more clear and readable. You can read a bit more on grails wiki.

This is simple implementation for our previous example:

class UserCommand {
  String firstName
  String lastName
  String gender

  def userService 

  def getGenderString() {
    gender.toUpperCase() == "M" ? "male" : "female"
  }
  
  static constraints = {
    firstName(minSize: 3, blank: false)
    lastName(validator: { value, obj ->
      if (!value || value.length() < 3) {
        return "invalid.last.name"  
          //this can be key from messages.properties
      }
      if (userService.findByFirstAndLastName(firstName, lastName))
        return "user.already.exists"
    })
    gender(matches: /^[MFmf]{1}$/)
  }
}
class UserController {
  def userService 

  def add(userCommand) {
    if (userCommand.validate()) {
      def user = userService.add(userCommand.firstName, userCommand.lastName, userCommand.genderString) 
      user ? render (text: "OK") : render (status: 400)   
    } else {
      render (status: 400, text: userCommand.errors)   
    }
  }
}

And if we want ot get rid of userService from controller alltogether, move "add" execution directly there as follows:

class UserController {
  def add(userCommand) {
    if (userCommand.validate()) {
      userCommand.addUser() ? render (text: "OK") : render (status: 400)   
    } else {
      render (status: 400, text: userCommand.errors)   
    }
  }
}
class UserCommand {
  ...
  def addUser() {
    userService.add(firstName, lastName, genderString)
  }
}

That will add much clarity to your code as now, input is firstly taken care of in command object, then as an object it is used in controller, where you can focus on more important stuff than validation. Also constraints are the same as used in domains, which work fantastic. You can use predefined keywords or model whole validator yourself with message outputs (which may or may not be included in message.properties if you don't need text output). Anyway, more on constraints can be found on grails wiki.

Start using command objects now!

Facebook Comments
Start using Command Objects in Grails
Tagged on:             

Leave a Reply

Your email address will not be published. Required fields are marked *