A Pattern to Simplify Grails Controllers
The WithDomainObject Pattern
Note, the sample code was modified on 5/18/2011. The withPerson method is now private.
I generally follow the same patterns with my controllers. They start with “the big 7 actions” – closures for index, list, show, create, save, edit, update and delete. Then i include any necessary controller-specific actions. After unit testing my controllers for the up-teenth time, I realized that there is a consistent pattern for many of the actions – get the id from params, get the domain object, then use the domain object. I wanted to extract this pattern into its own method, and that extraction evolved into the “WithDomainObject” pattern.
The Problem
Lets say you had a contact manager application, and you had a domain object called Person and a controller called PersonController. In the controllers show, save, edit, update and delete actions all follow the same pattern:
- Get the ID from the params map.
- Get the person from the domain.
- Doing something with the person.
The problem is that steps 1 and 2 are repeated for the 5 actions. Not very DRY.
The Solution
Lets encapsulate steps 1 and 2 into a method called withPerson:
private def withPerson(id="id", Closure c) {
def person = Person.get(params[id])
if(person) {
c.call person
} else {
flash.message = "The person was not found."
redirect action:"list"
}
}
}
and lets put our code from step 3 into its own anonymous closure. Heres a example of a action that uses the withPerson method and the update action:
def update = {
withPerson { person ->
person.properties = params
if(person.validate() && person.save()) {
redirect action:"show", id:person.id
} else {
render view:"edit", model:[person:person]
}
}
}
Example
See the withPerson method at the botttom of the controller.
The Person domain class
package contact
class Person {
String name
static constraints = {
name blank:false, unique:true
}
}
The PersonController controller
package contact
class PersonController {
def index = {
redirect action:"list", params:params
}
def list = {
[ people:Person.list(params), count:Person.count() ]
}
def show = {
withPerson { person ->
[person:person]
}
}
def create = {
[person:new Person()]
}
def save = {
def person = new Person(params)
if(person.validate() && person.save()) {
redirect action:"show", id:person.id
} else {
render view:"create", model:[person:person]
}
}
def edit = {
withPerson { person ->
[person:person]
}
}
def update = {
withPerson { person ->
person.properties = params
if(person.validate() && person.save()) {
redirect action:"show", id:person.id
} else {
render view:"edit", model:[person:person]
}
}
}
def delete = {
withPerson { person ->
person.delete()
redirect action:"list"
}
}
private def withPerson(id="id", Closure c) {
def person = Person.get(params[id])
if(person) {
c.call person
} else {
flash.message = "The person was not found."
redirect action:"list"
}
}
}
The PersonControllerTests unit tests
package contact
import grails.test.*
import org.junit.*
class PersonControllerTests extends ControllerUnitTestCase {
def p7 = new Person(id:7, name:"alpha")
def p9 = new Person(id:9, name:"beta")
@Before
public void setUp() {
super.setUp()
mockDomain Person, [p7,p9]
}
@After
public void tearDown() {
super.tearDown()
}
@Test
public void index() {
controller.index()
assert "list" == controller.redirectArgs.action
}
@Test
public void list() {
def model = controller.list()
assert 2 == model.people.size()
assert p7 == model.people[0]
assert p9 == model.people[1]
assert 2 == model.count
}
@Test
public void show() {
controller.params.id = 7
def model = controller.show()
assert p7 == model.person
}
@Test
public void create() {
def model = controller.create()
assert model.person instanceof Person
}
@Test
public void save_success() {
controller.params.name = "Paul Woods"
controller.save()
assert "show" == controller.redirectArgs.action
assert null != controller.redirectArgs.id
}
@Test
public void save_failure() {
controller.params.name = ""
controller.save()
assert "create" == controller.renderArgs.view
assert controller.renderArgs.model.person instanceof Person
}
@Test
public void edit() {
controller.params.id = 9
def model = controller.edit()
assert p9 == model.person
}
@Test
public void update_success() {
controller.params.name = "Paul Woods"
controller.params.id = 7
controller.update()
assert "show" == controller.redirectArgs.action
assert 7 == controller.redirectArgs.id
}
@Test
public void update_failure() {
controller.params.name = ""
controller.params.id = 9
controller.update()
assert "edit" == controller.renderArgs.view
assert controller.renderArgs.model.person instanceof Person
}
@Test
public void delete() {
controller.params.id = 7
controller.delete()
assert "list" == controller.redirectArgs.action
assert 1 == Person.count()
}
@Test
public void withPerson_success() {
controller.params.id = 7
def person = null
controller.withPerson() { p ->
person = p
}
assert 7 == person.id
}
@Test
public void withPerson_fail() {
controller.params.id = 0
controller.withPerson() { p ->
assert false
}
assert "The person was not found." == controller.flash.message
assert "list" == controller.redirectArgs.action
}
}
Advantages of this Pattern:
- The controller actions are simpler, as they don’t require code to detect not-found domain objects.
- The unit tests can be simpler. There is less code to test.






This is very cool and could actually make a very useful plugin.
Putting in a plugin would mean that instead of adding the method manually, you could write a generic withDomain{} method that resolves domain names via GrailsNameUtils.
Tomas
January 24, 2011
Interesting idea. It would work great when the Domains match up with the controllers (eg: Person and PersonController, EMail and EMailController), but how should it work if the controller doesn’t match a domain?
Thanks
Paul Woods
mrpaulwoods
January 24, 2011
In the default scaffolding, you can specify the domain class to scaffold against. I would imagine setting a variable at the controller level. static useDomain = ‘car’.
Additionally, withDomain should also take an argument, i.e.
withDomain( ‘car’ ){ car code here }
Tomas
January 24, 2011
I see. I like your idea. I may attempt it over the weekend.
Thanks,
Paul Woods
mrpaulwoods
January 24, 2011
In rails the common patterns for this seems to be before_filters. i.e, define a finder method the domain object and call it using before for relevant actions. Wouldn’t the same apply for grails?
Twice
January 24, 2011
Twice, maybe – I know just a little about the details of Rails.
In Grails you have to manually fetch your domain object in each controller action, and therefore you have to handle any error cases involving not finding that domain object.
My code adds a method to do the fetching and handle errors.
The screencasts on Rails that I have seen all show a 1:1 correlation between Domain Objects and Controllers. How does it work in Rails when a controller manages multiple domain objects?
Thanks,
Paul
mrpaulwoods
January 24, 2011
Pretty cool idea. Could be really helpful ‘out of the box’ if generated by Grails, but I guess the other comments might prevent that – about what if the ‘convention’ was not followed between domain object and controller.
Mike Miller
January 24, 2011
Nice idea.
You could make the withPerson work like this:
withPerson{
properties = params
if(validate)…
…
}
by changing withPerson() to something like:
def withPerson(id=”id”, Closure c) {
def person = Person.get(params[id])
if(person) {
c.delegate = person
c.resolveStrategy = closure.DELEGATE_FIRST
c.call
} else {
flash.message = “The person was not found.”
redirect action:”list”
}
Peter McNeil
January 24, 2011
Peter
How would this work:
def show = {
withPerson { person ->
[person:person]
}
}
How do you get the person object, if its the delegate?
Paul
mrpaulwoods
January 24, 2011
Good question… try
def person = withPerson {
… do stuff
}
[person: person]
and return person at the end of withPerson… kinda spoils it I know ;-/
Peter McNeil
January 24, 2011
this works too:
withPerson {
hairColour = ‘blond’
render(view: “changeColour”, model: [person: delegate])
}
Peter McNeil
January 24, 2011
full code example https://gist.github.com/794542
Peter McNeil
January 25, 2011
[...] A Pattern to Simplify Grails Controllers « Mr Paul Woods’s Weblog [...]
Blog bookmarks 01/25/2011 « My Diigo bookmarks
January 24, 2011
Peter, While I like your code sample and it works well, it is not my style. I prefer explicitly defined variable instead of using delegate. For me it is easier to understand and easier to teach.
Thanks,
Paul Woods
mrpaulwoods
January 25, 2011
[...] A Pattern to Simplify Grails Controllers « Mr Paul Woods’s Weblog – Grails und Coding by Convention, Großartiges Beispiel wie es noch mehr "Jedes-Mal-Aufs-Neue-Arbeit" zusammengefasst und eingespart wird [...]
Skurt Sebastian Kurt – Berliner Gründer, Unternehmer, Sportler » Blog Archive » Bookmarks for 25. Januar 2011 from 16:46 to 16:46
January 25, 2011
Very nice. I tried doing this in my Controller template (src/templates/scaffolding/Controller.groovy) and it worked with one minor fix. The withPerson method (with${className} in the template) can’t access the controller params object directly. However, a it’s possible to get to it through the passed in closure. Replacing params[id] with c.owner.params[id] fixed it.
Andrew
Andrew Taylor
February 1, 2011
Interesting. I didn’t think about editing the scaffolding controller.groovy file. I’ll remember that trick.
Thanks, Paul
mrpaulwoods
February 1, 2011
Love it. Am going to implement this immediately.
Owen Rubel
May 18, 2011
Ugh… know of one reason why not to implement this (at least not as a closure); ACL’s. You’d probably want to lock this down securely to avoid people grabbing variables or data from your domains when they may not have privileges. This is basically a backdoor (unless I’m misunderstanding something).
Now if you rewrite this as a private function, this will work.
Owen Rubel
May 18, 2011
Owen, you are correct. the method should be private. I will update the example code.
Thanks,
Paul Woods
mrpaulwoods
May 18, 2011
Thanks a great idea. Thanks!
Kevin C. Dorff
August 19, 2011
Just saw this now. Great idea!
Giancarlo Angulo (@Neoryder)
March 29, 2012
Reblogged this on Oele Geirnaert and commented:
Usefull information i think!
Oele Geirnaert
April 25, 2012
The pattern is really great. Is there already a plugin for it or does anybody have a scaffolding example?
Thanks
flotsch
flotsch (@flotsch)
December 22, 2012
Thanks, flotsch. I have not made a plugin for it (and most likely will not). I don’t think anyone else has either.
Paul
mrpaulwoods
December 24, 2012