Grails Best Practices
I work at IntelliGrape, a company which specializes in Groovy & Grails development. This article is a basic list of best practices that our Grails projects follow, gathered from mailing lists, Stack Overflow, blogs, podcasts and internal discussions at IntelliGrape. They are categorized under controller, service, domain, views, taglibs, testing and general.
The advice here is specifically for Grails 2.0, although much of it is generally applicable.
- Don’t allow the controller to take over another role. The role of a controller is to accept incoming requests, check permissions etc, ask a domain or a service for a result, give the result back to the requester in the desired format such as HTML, JSON, or XML. Keep the controller as thin as possible. Don’t perform business logic, queries, or updates within controllers.
- If a controller represents a single domain class, use the standard naming convention of “<DomainClass>Controller”.
- Avoid code duplication - common operations should be extracted as a closure or a method. See this blog entry for more information.
- Split complex data binding into a command object. You can make command objects rich (just like rich domain classes). Creating a hierarchy of command objects can also be useful in some scenarios.
- A service is the right candidate for complex business logic or coarse grained code. If required, the service API can easily be exposed as a RESTful/SOAP web service.
- Services are transactional by default, but can be made non-transactional if none of their methods update the persistence store.
- Keep views as simple as possible - avoid the temptation to put business or database logic in this layer.
- Use layouts to ensure a consistent look across all, or a sub-set of, the application's pages.
- Keep your views DRY ("Don't Repeat Yourself"). Split the repeated content into templates.
- Use custom TagLibs for common UI elements.
- Favor placing model domain specific logic in its own domain. Anything that applies to a single domain with few dependencies should go in its domain class. But keep it restricted to the logic specific to that domain only - more complex business logic that deals with a group of domains belongs to a service.
- To reuse common partial queries or decompose the complex logic, use named queries and chain them together as required, just like one commonly chains jQuery function calls.
- Don't mix any other common utility classes or value objects in the domain folder, rather they can go in src/groovy. If these classes need to support validation, one can annotate them with @Validateable.
- Use sensible constructors for instantiating domain objects, to avoid any unwanted state and to construct only valid objects.
- Keep an individual tag light. A tag can call other tags, and it is acceptable to break a tag into reusable sub-tags if required.
- The TagLib is considered part of the view layer in the MVC architecture, but it is acceptable to dig into the domain as required to assemble or format the data for display. Still follow the approach to minimize (i.e not to blanket ban) direct interaction with the domain.
- It should contain more of logic than rendering; although a little bit of rendering is fine.
- Use multiple custom taglibs for better organization.
- Favor units tests over integration tests. As well as being faster to run/debug they enforce loose coupling better. An exception is for service testing, where integration testing is generally more useful.
- In unit tests, use save(validate:false) to save objects which are not completely loaded.
- Place all environment specific settings in Config.groovy, such as serverURL, constants which differ per environment, etc.
- Keep personal settings (such as local database username or passwords, etc) in a <Local>Config.groovy file and add to version control ignore list, so that each team member can override configuration as per their specific needs.
- Somewhat contentious, but we advise setting grails.gorm.failOnError = true so that an exception is thrown as soon as domain validation fails while saving an object. Given this, you no longer need to check whether the save was successful.
- In Grails 2.0 “grails.hibernate.cache.queries = true" by default, which caches queries automatically without a need to add cache:true. Set it to false, and cache only when it genuinely helps performance.
Some Other Tips
- Understand and stick to Grails conventions, as Grails is convention driven. Using these conventions will make life easier for you as a developer.
- To organize Grails artifacts in different packages, don't do this com.businessname.appname.domain and com.businessname.appname.controller. Otherwise being in the FooController, we would end up importing Foo class. Since Grails already keeps these artifacts in different folders, they don't need to be separated further. Refer to this blog.
- The fixtures plugin may be used to bootstrap your data during development.
- Develop re-usable parts of your application as Grails plugins. These plugins can be tested individually and will remove complexity from your main application(s) using them. Consider publishing the plugins in the public plugin repository if you think others can benefit from them.
- Update the scaffolded templates to generate your project specific views & controllers.
- Prefer dynamic scaffolding to static scaffolding until the former no longer satisfies your requirements. For example, if only “save” action needs to be modified, you can override just that “save” action and generate scaffolded code dynamically at runtime.
- It's good to always provide database re-connection properties in DataSource.groovy.
- Always ensure that you include an externalized config file (even if it's an empty file), so that any configuration that needs to be overridden on production can be done without even generating a new war file.
- If you need to make a small change to the plugin you are using, for example change list.gsp of the quartz monitor plugin to go with your application theme, then instead of making the plugin inline for this small change, you can override these files by following the same directory structure or package. This works since the application gets higher priority over the plugins used.
- All custom validators of the domain can be put in a shared validators file, to support re-usability of these constraints amongst other domains. See here for an example.
- To install any plugin in your application, it's better to declare it in BuildConfig.groovy rather than using the install-plugin command. Read this thread for a detailed explanation.
Have I missed anything? What are the Grails development related best practices that you follow in your project or organization? Please share them as comments so that we can enrich the article further.
About the Author
Amit Jain is a Software Engineer with more than 4 years of experience in Java and Grails development. He is an Agile enthusiast and a certified Scrum Master. For the last 3 years he has been working with IntelliGrape, a company which specializes in Groovy & Grails development, Agile offshoring & projects. See the IntelliGrape website.
Jonatas Emidio Souza
I just don't understand the Tips #2 (about packages), because we have this kind of patterns in the "grails-core". So what do you mean about ( don't do this "com.businessname.appname.domain").
To conclude, as Donald's observation, could you put some examples about Tips #8.
great article but little stuck with point 9 of Tips
Adjévi Pedros KOVI
If you need to make a small change to the plugin you are using, for example change list.gsp of the quartz monitor plugin to go with your application theme, then instead of making the plugin inline for this small change, you can override these files by following the same directory structure or package. This works since the application gets higher priority over the plugins used.
can you give an example plz.
York Xyander, Bodo Junglas Jul 31, 2015