Monday, April 1, 2013

Groovy & Grails Design Best Practices


Grails is designed to be an interactive agile development framework. That means you can start anywhere you like, refactor, make changes, and still end up with a fantastic app. You can start with the UI, domain modeling, service classes, or even the test cases if you like. Grails makes domain modeling so easy that we usually do this bit directly in code without any real data model on paper. That’s the reason modeling is a great place to start your Grails application design & development journey.

A)            Domain-driven design

  • Use domain-driven design. Create your basic domain model classes as the first step in your application, and use scaffolding to get them online. This will help you stay motivated and understand your domain better.
  • Learn the basic modeling options well. You’ll spend a lot of time setting up Grails models in your future development work. Take the time to learn all the basic relationship types presented in this chapter. The test cases will give you valuable experimentation fodder.
  • Use tests to experiment. Domain model test cases provide a great way of experimenting with tricky save() scenarios and testing out your validations.
  • Don’t trust users—validate. Make use of validators to keep your domain objects in order. Custom validators aren’t hard to implement, so don’t be afraid to roll your own if the situation demands. It’s better to encapsulate the validation logic in your domain class than to use dodgy controller hacks

B)            Follow the Grails Convention

  • Understand and stick to Grails conventions, as Grails is a convention driven. Grails is convention driven development which need to follow as per convention means Views should just be views. Controllers should just be controllers.
  • Services and Model objects should contain all of the logic of your application. This means when you invoke an endpoint, you pass the request to Controller and Controller in turn will invoke the service and return concise response as model object or data to the controller.
  • Services should be transactional, so anything that hits the database should go in a service.

C)            Dependency Injection

  • Grail is based on dependency injection on convention so we need focus on folder convention to put component on appropriate grails-app/folder and proper naming convention
  • Services go into the services folder. Controllers go into the controller’s folder.
  • If you have a Xxx model object, and you need a controller and service for it, then your controller and service should be named XxxController and XxxService. Grail will auto wire based naming convention

D)            Presentation/View

  • Apply pagination. Paginating large datasets creates a much better user experience and it’s easy to implement.
  • Develop custom tags. Take the time to develop reusable tag components for common parts of your UI. It will save you time, simplify maintenance, and enable you to reuse them in future projects.
  • Use convention-based layout. Favor convention-based layouts over explicit Meta tags. Often a specific layout for one particular action can make things much more maintainable than doing meta-magic branching. Take advantage of meta tag styles when you need to style a subset of pages for a controller, but use convention layouts for the rest.
  • Layout smarter. Handle basic flash message display in your layout rather than repeating it for each view. Use templates for common HTML fragments, passing in explicit model elements. Inside Ajax calls, resist the urge to render HTML directly, and do any rendering via a template call.
  • Pick a JavaScript library. Gain an appreciation of the strengths and weaknesses of the various JavaScript libraries. They all have different approaches and are worth exploring. Choose an Ajax library that makes sense for the rest of your app. It takes time to download libraries, so minimize the number of libraries in play.
  • 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
  • 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
  • All custom validators of the domain can be put in a shared validator’s 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.
  • We should avoid putting lots of logic in the web layer to make a clear separation between presentation layer business logic.
  • For views, strive make them as simple as possible. Put as much boilerplate in your layouts. Avoid the temptation to put business or database logic in this layer.
  • Avoid conditional logic like the plague.
  • Split out shared content into templates and g:render and build your own taglib for common UI elements
  • 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.
  • Update the scaffolded templates to generate your project specific views & controllers.

E)            Controller

  •  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.
  • Keep your controllers as simple as possible e.g. queries and object retrieval logic can go in domain objects. If you see withTransaction() and need transactional behavior, it's a good candidate for a service.
  • 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
  • Keep Controllers brief. If logic can be expressed in a terms of a specific class, move the logic there or create a new class. Test it afterwards
  • If a controller represents a single domain class, use the standard naming convention of “<DomainClass>Controller”.
  • Use flash scope. Flash scope is ideal for passing messages to the user (when a redirect is involved).
  • Use the errors object wisely. Make use of the errors object on your domain class to display validation messages. Take advantage of resource bundles to make error messages relevant to your application use cases.
  • Use command objects. Take advantage of command objects for form submissions. Don’t just use them for validation—they can also be handy for encapsulating tricky business logic.  Understand data binding. Data-binding options in Grails are plentiful and subtle.
  • Understand how data binding works for child objects when form parameters are prefixed. Use whitelisting to ensure that data binding doesn’t compromise sensitive fields.
  • Be forgiving with URLs. Use default actions to make URLs more forgiving, and do the same for custom URL mappings. Permalink-style URLs are much easier to remember and not difficult to implement.
  • Apply filters. Employ filters when you need to selectively fire backend logic based on URLs or controller-actions combos          

F)                 Services

  • 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

G)            Domain

  •  Take advantage of the ability to override setters and getters to make properties easier to work with. Creating short named queries and chaining them together is an excellent way to decompose complex query logic. Anything that applies to a single object of that type with few dependencies should go in the domain object class. Keep the logic specific to that object, though. More complex business logic that deals with groups of objects belongs in services
  • If in doubt, it can go in a service. Services should be stateless. If you find you need to store state, you probably need a new domain object.
  • Do use domain objects to model domain logic. Moving domain logic to Services is a hangover of inconvenient persistence layers
  • 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


H)            TagLib

  •  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.

I)            Plug in

  • 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.
  • Use fixtures plugin to bootstrap your data during development
  • 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.
  • If your plugin adds dynamic methods or properties, make sure that it also implements the onChange hook, so that those methods and properties are retained after a reload.
  • Use local plugin repositories for teams. Local plugin repositories serve several purposes.
  • Modularize large or complex applications. The Separation of Concerns pattern is powerful, and plugins allow you to apply it to your Grails applications. You can either use the in-place plugin mechanism (via the grails.plugin.location.* configuration setting) or a local plugin repository to manage the modularization.
  • Write functional tests for your plugins. For plugins to be reliable, you should write functional tests.

J)         Testing

  • Test first, test often By writing your tests first, you not only ensure that your code is covered, but you also have to think clearly about what you want the code to do. If you run the tests often, you get quicker feedback when something breaks, which makes it easier to fix. This helps speed up the development cycle.
  • Maintain test coverage and avoid gap in test coverage as much as possible
  • Test as much as you can with unit tests, not integration tests. Unit tests are ways faster to run/debug and enforce low coupling better. Use mockDomain(), mockLogging() etc.
  • Use grails console to test and explore your code in the wild. Embed a Groovy console into your web UI - it's an invaluable tool to examine insides of a running application.
  • 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.
  • Use a continuous integration (CI) system to ensure that breakages due to multiple developer are picked up early and often.
  • Make sure that you’re testing at both the unit and functional levels. Even if there is some overlap and redundancy, that’s better than having gaps in your test coverage

K)         Deployment

  • Use the release (previously known as maven-publisher) plugin to deploy in-house plugins to your Maven repository
  • Make yourself familiar with the resources plugin for handling of static resources
  • Write scripts for any repetitive task that you can. Automation is important in reducing errors and improving reproducibility.
  • Use continuous integration. This is almost mandatory for any team bigger than one to catch those bugs that appear when changes from different people are merged together. 

1 comment:

  1. Many have heard the buzz around Grails (a full-stack web-app platform that “attempts to solve as many pieces of the web development puzzle”) and the Groovy language (since 2004, one of the most powerful dynamic languages for the JVM). However, how many can leverage the full power of the platform?
    Maybe you've heard about frameworks such as Ruby on Rails, Django or TurboGears and would like to achieve similar benefits in your development shop? Don’t let DHH and the Rails community have all the fun...
    This workshop is intended to be a solid and pragmatic introduction to using Grails & Groovy at work, or for those advocating or promoting such rapid development

    ReplyDelete