Grails: Bootstrapping data with DomainBuilder - No Fluff Just Stuff

Grails: Bootstrapping data with DomainBuilder

Posted by: Andres Almiray on January 23, 2012

A few days ago I was discussing the topic of builders during a Grails training session. After surveying the usual suspects found in the standard Groovy distribution (MarkupBuilder, SwingBuilder, Antbuilder and ObjectGraphBuilder) we jumped into Grails' DomainBuilder. Once we got familiar with it the team seized the opportunity to refactor an existing application they've been working on for a few weeks. The idea was to remove a very verbose data setup during the bootstrap sequence.

Like many other applications out there, this one requires setting up users and roles to secure access to some areas. The User and Role classes looked like the following ones

Nothing complex really. Now, during bootstrap there were a handful on instances of both classes being created and saved. A few users would share roles which meant keeping a reference to the common role to later use Grails relationship methods. This caused the code to be not so much DRY, and while it's good to be WET from time to time this wasn't the case. Let me show you how the code looked like before we added the builder

We could have saved a few lines by collecting all domain classes in Lists then applying *.save() on the lists, however that would still have left the relationship methods being defined explicitly. This is where DomainBuilder came in. With it we were able to define the domain instances and the relationships at the same time. We ended up with code looking like the following one

DomainBuilder understands perfectly well the relationships between domain class instances. It also makes some assumptions on how the model is setup, but we still need to give it a few hints. In line 7 we can see a classNameResolver set on the builder. By convention the builder will use an strategy to construct fully qualified classnames out of node names. If the classes happen to be defined inside a package then you must define a custom classNameResolver. In our case the classes we're interested in live under the same package so we only need to specify it, the builder will do the rest to figure out the correct class name.

Next, we must set a custom identifierResolver because the id property is of semantic meaning to domain classes. The builder can keep references to all instantiated nodes, it will use the id property by default, assuming that's a synthetic property. This means it will treated in a different way than the rest of properties. Because id is used by domain classes we set a different synthetic property named nodeId. Finally we define a custom factory to serve as the root of the object graph. This custom factory requires additional setup to handle its children, which is why we also register a custom ChildPropertySetter on the builder. These two helper classes can be seen in the following snippets

Basically the code inspects the type of the parent node. If it's a List then we append the child to it, otherwise we let the standard behavior take control. You can read more information on the builders used at FactoryBuilderSupport and ObjectGraphBuilder.


Keep on Groovying!
Andres Almiray

About Andres Almiray

Andres is a Java/Groovy developer and a Java Champion with more than 20 years of experience in software design and development. He has been involved in web and desktop application development since the early days of Java. Andres is a true believer in open source and has participated on popular projects like Groovy, Griffon, and DbUnit, as well as starting his own projects (Json-lib, EZMorph, GraphicsBuilder, JideBuilder). Founding member of the Griffon framework and Hackergarten community event. https://ch.linkedin.com/in/aalmiray

Why Attend the NFJS Tour?

  • » Cutting-Edge Technologies
  • » Agile Practices
  • » Peer Exchange

Current Topics:

  • Languages on the JVM: Scala, Groovy, Clojure
  • Enterprise Java
  • Core Java, Java 8
  • Agility
  • Testing: Geb, Spock, Easyb
  • REST
  • NoSQL: MongoDB, Cassandra
  • Hadoop
  • Spring 4
  • Cloud
  • Automation Tools: Gradle, Git, Jenkins, Sonar
  • HTML5, CSS3, AngularJS, jQuery, Usability
  • Mobile Apps - iPhone and Android
  • More...
Learn More »