Its very easy to build an application using Grails framework. But, its hard build an application the right way. We tend to make mistakes, thinking that, its the right way to do it. So listing down the “Grails best practices”.
- Controller logic should as simple as possible. It should accept incoming requests, check permissions, 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. 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”. When naming actions, consider how the url would be. For example, a user registration page should be in UserController and in the action named “registration”. So that, the link to this page would be “https://<www.site.com>/user/registration”.
- Avoid code duplication. Common operations should be extracted as a closure or a method. See this blog entry for more information.
- Use Grails Command Objects for data binding and server side validation.
- Place the Command Objects in “src/groovy” folder. This would prevent Controller class growing huge in lines. Use @Validatable annotation to make them validatable. More details here.
- Data can be transferred from Command class to Domain objects(or reverse) using Grails properties. To transfer data to a more complex domain model, create functions in Command class.
- 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.
- When saving data, set failOnError to true. This would help us to catch errors in saving objects, which otherwise could go unnoticed. More details on failOnError can be found here. This could also be set in Config.groovy.
- Avoid using def in methods(As parameter, return type etc..). Instead, try using actual Classes. This would give more clarity and readability to your code.
- Avoid multiple queries during read operations and avoid unwanted data being fetched during the same. This would improve performance and reduce load on server.
- Keep views as simple as possible. Avoid the temptation to put business or database logic in this layer. Use taglib for any business logic.
- Use layouts to ensure a consistent look across all pages.
- Keep your views DRY (“Don’t Repeat Yourself”). Split the repeated content into templates.
- Use custom TagLibs for common UI elements.
- Move all text messages in view to messages.properties
- You could also use messages.properties files to save you date-formats. This would help you to have multiple formats for different countries.
- Also make sure that, you have a naming convention that you follow for message code.
- Favor placing model domain specific logic in its own domain. Anything that applies to a single domain with few dependencies can 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, use named queries and chain them together as required.
- Don’t mix any other common utility classes or Constants in the domain folder, rather they can go in src/groovy. If these classes need to support validation, annotate them with @Validateable.
- 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. Avoid direct interaction with Domain class from the taglib. For unavoidable situation, interact with Domain class through Service class.
- Taglib should contain more of logic than rendering, although a little bit of rendering (a line or two) is fine. If more rendering is required in taglib, move the HTML code to a template gsp.
- 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.
- 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.
- 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.