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.
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?
ReplyDeleteMaybe 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