This is the second installment of my What Grooves You? series of posts, this time discussing how to modularize your Grails application. While Grails does an awesome job of enforcing MVC once your application reaches a certain size, or you have multiple applications which may have shared components, you’re going to have to start thinking about how your going to modularize the reusable parts of your code.
I encountered this pretty quickly because the application I am working on is broken out into two distinct parts, a public facing web form for submitting data (the “Front End”) and a private back office application for managing those user submissions (the “Back End”). These two parts of the application (for compliance reasons) cannot run on the same internet facing system, and the “Back End” must only be available on the internal network. Of course, both of these applications are going to work with the same database and therefore the same Domain classes. It would be dangerous and tedious to try to keep the separate domain classes in each project synchronized with one another.
What won’t work
The RESTful JSON Service
My first thought was to deploy a 3rd application which would be internet visible and act as a proxy for all the database requests. I could then query that application with REST and handle a JSON payload which would be my domain object. This actually seemed pretty elegant since I wouldn’t have to actually share any code between the Front End and Back End applications and I still got a well defined object on either end. The problem of course is that all I get is the data for my domain class, and I don’t have access to any of the functionality that GORM gives me “for free”. I’d have to duplicate search functionality, limits, grouping, sorting, and all sorts of other querying tools in my service. That seemed like an awful lot of work for functionality that is offered by GORM and works very well!
Just JAR it man
The next obvious conclusion is to just toss my domain classes into a library JAR file and reference that library in both of my other applications. This way I actually have the whole domain class and access to the dynamic find methods and all that other good stuff. But, how do you package these? Do you compile the Groovy classes then package the .class files? Will the data source information have to be set for the domain class(es) in the JAR, or will the data source of the application referencing the library be used?
Now some more seasoned Java and Hibernate developers might simply laugh at that barrage of questions, but for me it presented a serious barrier to entry. Fortunately there is a better way.
Just plug it in!
It didn’t take me long to discover that putting my reusable code into a Grails Plugin was the best and most scalable approach. For the sake of demonstration I’m going to take you through an example comment submission and administration application, kinda like blog comments.
The plugin project
First, let’s go ahead and create our plugin project.
grails create-plugin Modular-DAL
Once you’ve got your shiny new plugin created, open it up with your favorite IDE (I use Spring Source Tool Suite) and add a new domain class that you’re going to be sharing.
grails create-domain-class com.nslms.modular.domain.Comment
Now we specify some properties for our new shared domain class.
Comment.groovy
package com.nslms.modular.domain
class Comment {
static constraints = {
name(blank:false)
email(blank:false)
comment(blank:false)
}
String name
String email
String website
String comment
Boolean isApproved = false
}
With our new shared domain class created, we want to package up our plugin so we can load it into the other projects which we’ll be creating in a moment.
grails package-plugin
That’s it, you’ve just created a (very small) module of your application which contains a shared domain class. This could, of course, contain any number of domain classes, controllers, or services, views, javascript, css, etc. that would be used by other parts of your application, or by other applications.
The Front End
Now, lets create an application which will serve as the “front end” or externally facing form for collecting data.
grails create-app Modular-FrontEnd
Then the very important part of installing the plugin we just created
grails install-plugin ../Modular-DAL/grails-modular-dal-0.1.zip
Because you can download the project I created, I’m not going to go into excruciating detail about the controller and view(s) I setup in my front end, but sufficed to say I am accessing the “Comment” domain class that is supplied by the Modular-DAL plugin project!
CommentsController.groovy (snippet)
import com.nslms.modular.domain.*
class CommentsController {
def index = { [comments: Comment.findAllByisApproved(true)] }
}
The result of the front end app should be a list of comments which are approved (by the backend) and a submission form to allow you to submit new comments. Kinda like this.
The Back End
Now we need to create the system which will allow you as the administrator to approve the comments submitted by the unwashed masses.
grails create-app Modular-BackEnd
And install the plugin with the shared domain class.
grails install-plugin ../Modular-DAL/grails-modular-dal-0.1.zip
Again because you can download the project I created, here’s just a snippet of the admin controller showing the juicy bits where we use the shared domain class
AdminController.groovy (snippet)
import com.nslms.modular.domain.*;
class AdminController {
def comments = { [comments: Comment.findAllByisApproved(false)] }
}
The back end app should have a list of all unapproved comments, and a method to approve them. Kinda like this.
Trying it out
Now if you’ve followed along and created your own controllers and views, or downloaded my basic project, you’re going to want to try running both the front end and back end at the same time, persisting data to a common datasource so that you can see the whole thing in action. If you just use the grails run-app command, you’ll find very quickly that you can only run one or the other project, but not both at the same time. This is because they’ll both be trying to run on the common Tomcat port (8080). To overcome this, and run both apps at the same time, try the following starting from the Modular-FrontEnd directory.
grails -Dserver.port=8081 run-app cd ../Modular-BackEnd grails -Dserver.port=8082 run-app
Now you should be able to access both applications at http://localhost:8081/FrontEnd and http://localhost:8082/BackEnd respectively.
Download The Project(s)
If you want to download the project(s) and follow along, fire up your favorite subversion client and export everything at svn://linode.nslms.com/blog/grails/Modular or download it here. A couple things to note if you’re grabbing my project, it’s currently setup to use a MySQL database named “modular” running on the same system as the application. If you don’t already have MySQL setup, give XAMPP a look to get you started quickly. Also, I didn’t include the JDBC driver, go fetch it here and drop it into the “lib” directory of both the FrontEnd and BackEnd applications. Lastly, these projects are all written with Grails 1.2.1 so you’ll have to be using 1.2.1 or newer.
#1 by ew on March 12, 2010 - 3:08 am
great post but I have some questions –
What would you do if you were using the grails application as a backend (e.g. a backend that provides restful services) and had a pure java swing front end that called the services from the grails backend and then needed to be able to marshall the json/xml back into the correct classes ?
You might also have a Griffon frontend that would need to do similar.
Would you just package the plugin into a jar and then add it as a library to the swing or griffon frontend ? and if this is possible what are the steps importing a groovy library into a Java project and then using the Groovy classes 'inside' Java?
I guess you could always write mirror java versions of the classes, just like you would have to do if you were calling Java services from a Flex frontend
thanks
#2 by Patrick Haggood on March 12, 2010 - 8:04 am
Awesome post – have seen many suggestions about how to modularize a Grails app but never so clear an example. Thanks!
#3 by Ted Naleid on March 12, 2010 - 8:59 am
I've used a plugin to hold domain objects so that I can share the same domain across a number of applications a few times now and it works great.
One big shortcut that you can take with this is to create a grails-app/conf/BuildConfig.groovy file and put the plugin name and path to the source in it, I do this with the build-test-data plugin to "install" the build-test-data plugin into a bookstore sample grails app that exercises the plugin:
http://bitbucket.org/tednaleid/grails-test-data/src/tip/bookStore/grails-app/conf/BuildConfig.groovy
If you do this, your applications will see the live changes to your domain/controller/service/etc classes as if they were actually in current app and there isn't any need to repackage and reinstall the plugin when you make changes.
It's mentioned in the grails 1.1 release notes, but I don't think they have it in any other official documentation:
http://www.grails.org/1.1+Release+Notes
#4 by RyanG on March 17, 2010 - 10:41 am
ew.. If your backend is a Grails App that has a restful service with a JSON payload, there isn't any need to share code between the frontend and backend. You'd simply encode and decode JSON, ending up with a DTO object on your frontend which you could access the properties of.
If instead you exposed a SOAP web service from your Grails backend, you could "import" that service into your frontend by evaluating the WSDL file, but there are still no shared libraries to accomplish this.
Ted.. Thanks for the comment, and the link to your blog. Looks like you are quite a bit ahead of me in your Grails expertise. The reason I've chosen not to include building/updating/installing the plugin(s) when I build my application is because I'm considering a more distributed development model. In practice we're likely to have different teams working on the plugins which other teams consume in their applications. As such, those plugins would be stored in a shared repository, and applications would be dependent upon specific versions of the plugins. Therefore you wouldn't want to blanketly update the plugin on every build, since a new version may have become available which breaks the interface that you're expecting.
#5 by Kim Betti on June 2, 2010 - 5:26 am
How do you go about reference integrity and cascading? Lets say that you want to make sure that comments are deleted when their corresponding article, blog entry or what ever is deleted. You can’t really add belongsTo in the Comment domain class as this would cause ‘all’ your domain classes to depend on each other and before you know you have one plugin with domain classes and another on with the actual application logic.
This is really bugging me. At work we have a lot of functionality in one of our projects that we would like to separate out into various plugins, but it’s very difficult because of all the dependencies between different domain classes. We don’t feel like dropping all relationship declarations from our domain classes just to be able to separate some functionality out into plugins.
#6 by Noel O'Brien on June 9, 2010 - 8:22 am
Kim’s question is spot on. Domain classes typically don’t exist in isolation. How would one create a belongsTo like dependency in a plugin with reference to an application domain class?
#7 by RyanG on June 9, 2010 - 4:13 pm
Kim & Noel, The question of reference integrity between domain objects in your plugin(s) and the application which uses it is an excellent one. Sadly I do not have the answer.
My initial reaction is “you’re doing it wrong” if you have domain classes that interact but are in different plugins or applications. But I realize that’s not a real life scenario. I’ll do some research and make an effort to answer the question in another blog article!
#8 by Noel O'Brien on June 10, 2010 - 1:29 am
I’m not aware of Kim’s usage but mine is very simple. I have a ‘producer’ of data to be stored and I have a ‘consumer’ of that same data. I could put both in the same application but that seems wrong for many reasons, not least performance. The ‘consumer’ owns the domain class and the relationships really exist within the ‘consumer’. The ‘producer’ does’nt really care about the relationships, it just wants to insert new data into the database. This seems like an ideal scenario for a plugin approach but unfortunately it does’nt seem to be possible.
Thanks for your help
#9 by kai on July 11, 2010 - 7:13 pm
great post for modularizing grails, i’m looking forward as well on how do you resolve the integrity issues on classes.
#10 by kai on July 11, 2010 - 7:47 pm
#11 by RyanG on July 22, 2010 - 8:35 pm
I’ve been looking into the topic of manipulating the “belongsTo” relationships, and cascading deletes of Domain artifacts in plugins.
Sadly, I haven’t had a lot of time to devote to research lately, and I don’t yet have an answer.
I am, however planning a blog post to reveal what I have found so far, so stay tuned for some information on the topic. Thanks for the patience!