Modularizing your Grails Application – Domain Classes


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.

* UPDATE: The example apps have a new home..

Grab the projects at

git clone git://ec2.nslms.com/grails/blog_example_modular

, , , , ,

  • ew

    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
     

  • Pingback:   Modularizando tu aplicación Grails: clases del dominio (inglés) - Observatorio de Grails

  • Patrick Haggood

    Awesome post – have seen many suggestions about how to modularize a Grails app but never so clear an example.  Thanks!

  • http://naleid.com/blog Ted Naleid

    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

  • http://www.nslms.com RyanG

    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.

  • Pingback: What's a plugin-oriented architecture? | SpringSource Team Blog

  • http://www.developer-b.com/ Kim Betti

    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.

  • Noel O’Brien

    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?

  • http://www.nslms.com RyanG

    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!

  • Noel O’Brien

    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

  • kai

    great post for modularizing grails, i’m looking forward as well on how do you resolve the integrity issues on classes.

  • kai

    kai :great post for modularizing grails, i’m looking forward as well on how do you resolve the integrity issues on classes. Thanks thanks

  • http://www.nslms.com RyanG

    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!

  • Kapil M

    Hi Ryan , Thanks for the article.

    About the belongsTo scenario: My q to Kim :How does a plugin compile if you have a Comment belongsTo Article / Blog in the plugin. The plugin Comment is not supposed to know about your Article/Blog. I think you have to forfeit using the benifits of belongsTo if you are using a plugin to share your domains across applications. No ?

    My other q for Ryan: Is it possible to have a create-drop apply to the domain objects of the plugin. I created a dbCreate in the plugin DS.groovy but it doesnt seem to create the tables.

  • http://www.nslms.com RyanG

    Kapil,

    The datasource settings of the parent project dictate the behavior, and the settings in the plugin are ignored. So in order to create-drop the table(s) for the domain classes in your plugin, you’d have to set create-drop for the datasource on the project that consumes the plugin.

    As for the issue of cascading deletes.. I still can’t seem to find a good solution for it. As best I can tell once the grails framework has fired up all of the functionality of the domain beans are pretty much set in stone. In order to manipulate that you’d need to write a plugin.

    It still feels like the better solution would be to just reconsider your inheritance and encapsulation model so that the domains that are dependent upon one another are in the same module.

  • http://www.developer-b.com/ Kim Betti

    Kapil M :
    Hi Ryan , Thanks for the article.
    About the belongsTo scenario: My q to Kim :How does a plugin compile if you have a Comment belongsTo Article / Blog in the plugin. The plugin Comment is not supposed to know about your Article/Blog. I think you have to forfeit using the benifits of belongsTo if you are using a plugin to share your domains across applications. No ?

    Exactly, giving up reference integrity is not such a big loss, but for other use cases it’s more critical.

    It would be great to have the option to develop, test and release individual parts without having to give up features like reference integrity and cascading.

  • http://ww.cavi-studio.fr Lucas T

    Hi,

    Is it possible to run several grails apps on the same server and same port but with different server URLs ? For example to run a Grails project bound to mysite.com and another one bound to admin.mysite.com ?

    Thanks for your help
    Lucas

  • http://www.nslms.com RyanG

    Lucas T :

    Hi,

    Is it possible to run several grails apps on the same server and same port but with different server URLs ? For example to run a Grails project bound to mysite.com and another one bound to admin.mysite.com ?

    Thanks for your help
    Lucas

    Hi Lucas,

    There are a few ways you can do virtual hosting with Tomcat6.

    The first and possibly easiest is to make the appropriate changes to server.xml as described here.

    If you’re using Apache2 in front of Tomcat6 with mod_jk to connect them, you can simply add the correct mod_jk directives to the vhosts in Apache.

    Though it won’t allow you to have different apps on different hostnames, you can also read my blog post describing out how deploy applications at different paths. I.E. yourdomain.com/app1 and yourdomain.com/app2

    Grails and Tomcat6 – Deployment Notes

  • Emmett

    Hi there,

    I need some advice on the architecture of a grails web application.

    At the moment I have encapsulated all my domain classes and services into a plugin. This plugin is used by the web application. The plugin is included in the web application i.e. they run in the same address space. When a controller calls a service or a domain class CRUD method, it is not over the wire (via web service) its a plain and simple call.

    Eventually I envision that I will want to have some other client – e.g. it might be a swing/griffon app or indeed an iphone/android native mobile application. Thus I will need to access services either by webservices or REST

    Im not exactly sure what do do in this case. Do I add a bunch of services to my domain/services plugin and call these directly from the clients ?

    If I do this, then surely my web app should be accessing services in the same way ? and if it did I would probably need to introduce the idea of DTO’s so that the web app doesn’t have direct access to domain objects that have dynamic crud methods. But DTO’s just bloat the whole project and it means I wont be as agile. I know there is a DTO plugin but I didn’t really take to it to be honest.

    Is the better idea to have any new client make restful calls to the web application ? I guess if I want to use REST this is my only option. Is it realistic to use the same Grails web application to serve html to browsers and json to various other client apps at the same time ?

    any advice would be greatly appreciated.

    thanks

  • http://www.saltwebsites.com/ Fletch

    @Emmett (#17) check out posts #1 and #4 which basically give the answer. First choice is whether to use DTO or not, that’s up to you and you have outlined the positives/negatives, it’s independent of this exact topic, really. Whichever way you go you need some kind of object (domain or DTO equivalent) on the front end.

    I think the question from ew (#1) is interesting and though well answered in #4 for JSON, the question still remains for the case of binary remoting between Grails and Swing (or any other Java program). If you can do it, binary remoting (such as Hessian) is much nicer, faster etc than JSON. But then you WILL need to share classes, be they DTO or domain classes, and the interfaces of the services being remote-called.

    I’m currently trying to figure out what to do here. Jar these classes up and copy them across in the build? Have a common project with the common files and reference it from both builds? I suppose a Grails plugin is not going to be useable from a Swing application.

  • http://www.nslms.com RyanG

    Emmet,

    Were I faced with the same scenario, I would probably setup a separate grails application which exposes your domain classes via a SOAP or REST/JSON web service, and leave the main grails application unchanged in the way it accesses your domain classes.

    The underlying reason for writing this article was how to use the same Grails domain classes (including all of the GORM and meta programming magic) in more than one Grails application. Basically making your DBO abstraction portable so that many different systems can access the data model in a shared, and predictable manner.

    If your main application, which consumes the domain classes in your plugin, uses GORM directly on the domain classes, you’d have to do a lot of refactoring to abstract that behavior into services.

    By launching a totally separate app which exposes your domain model via a webservice, you also have the choice to limit that service to only the data access which would be necessary for remote clients. For instance, your main application may need to do a complex query across many domain objects, and have write access to everything, where a remote client may only need to have read access to a few domain objects.

    You can even make the webservice appear to be part of your main application by putting it in an appropriate application context in Tomcat which I talk about in this post. I.E. http://www.yourmainapp.com/api/

    If you’re strict about version control you can make sure that the main application, and the webservice layer are both consuming the same version of the domain classes plugin, and it’ll work seamlessly!

  • http://www.nslms.com RyanG

    Fletch,

    It sounds like for your particular environment, using a Grails plugin to contain your DBO abstraction won’t work out well.

    This article is targeted towards those who are using a 100% Grails stack and want to reuse their domain classes in two or more applications.

    Since some of the applications which you want to share the same data model are in frameworks besides Grails, you’ll have to take another approach.

    Without knowing a lot about your application, I’d suggest that you bundle all of the DBO abstraction into a shared java library. The underlying technology would have to be something like Hibernate or straight JDBC to be most compatible.

    You could (as you noted) also expose your data model via a much richer webservice for other clients. Though, I would advise against this if you need a great deal of access and control over the data model.

  • Emmett

    @RyanG, @Fletch
    thanks for the advice folks. I think I will use the domain plugin in a seperate webapp and use that web app especially for exposing services (probalbly through REST)

    Also one more question, if I may..
    While Ill be using the spring security plugin for security in the web app i’m not sure what the best way would be to manage authentication and authorization to consumers (i.e. a native iphone/android apps) of the restful services exposed by the 2nd web app

    thanks again
    Emmett

  • Adrian Mirjan

    Thank you for a great article Ryan! Really helped. Also thanks to those who left comments as I found a lot of useful info there. :)

  • http://alfonsorv.com Alfonso Rivero

    Hi Ryan, thanks for your article.
    I’m trying to figure out if I should include into the plugin project all controllers, filters, configuration and domains for security (ie spring-secury-core) because is a common functionality for all my dependent projects or into each project.

    Thanks

  • kai

    Hello Ryan
    Any finding on a solution for reference integrity between domain objects? Thanks

  • http://pepeluk.net luk

    Emmett :

    While Ill be using the spring security plugin for security in the web app i’m not sure what the best way would be to manage authentication and authorization to consumers (i.e. a native iphone/android apps) of the restful services exposed by the 2nd web app

    You can still use spring security with basic auth for services and standard web form login for webapp:
    http://burtbeckwith.github.com/grails-spring-security-core/docs/manual/guide/9%20Authentication.html

  • Pingback: An Army of Solipsists » Blog Archive » This Week in Grails (2011-12)

  • Pingback: Grails cette semaine (2011-12) – Traduction de l’article original

  • Luis

    Hi,

    One of the things that worry me when sharing a domain model and of course the same database between two applications is how the 2nd level hibernate cache could mess things up.

    One application could have a different view of the data than another app.

    Did you think about this issue?

  • Pingback: How to implement a “remote” Domain? | PHP Developer Resource