Grails: Separating the database access from the domain classes
Some days ago I started working on my second project involving Groovy & Grails.
Grails is my first contact with server side development with dynamic languages, and, despite some small problems (mostly due to the fact that the framework is still not so mature compared to others out there), I really, really enjoy it.
One of the things that I like is how using Groovy’s dynamic features the framework injects database access technicalities into our domain classes.
The domain is therefore smart enough to retrieve/update its relationships and even to find/update itself, all without the need to pass around repositories dependencies whatsoever, or to force the domain classes to extend any other classes.
But sometimes we need to write HQL (or criterias) ourselves, and then in our first project we ended up having some domain classes quite bloated with custom queries and a lot of other database related operations mixed with pure domain-specific logic operations.
Something like this:
class Book {
// properties, mappings, constraints
// domain methods
def findAllBooksFromTheSameAuthor() {
return findAll("some HQL")
}
def findSimilarPurchasedBooks() {
return findAll("some HQL")
}
}
This code is just a silly example to illustrate the problem.
Of course we could have used Grails’ dynamic finders for those queries too, but we would be externalizing the internals of our domain class so we like this approach of adding custom query methods better.
One thing we didn’t like though, is that we had many methods like the ones above, mixed with the domain logic and properties.
So we decided to move these methods somewhere else and inject them into our domain classes, like Grails does with its dynamic generic finders.
A very nice way to do it seemed to be using Groovy’s Delegate annotation, having something similar to:
class Book {
@Delegate BookQueries bookQueries = new BookQueries(this)
// properties
// mappings, constraints, domain methods
}
class BookQueries {
Book book
BookQueries(Book book) {
this.book = book
}
def findAllBooksFromTheSameAuthor() {
return Book.findAll("some HQL")
}
def findSimilarPurchasedBooks() {
return Book.findAll("some HQL")
}
}
We have to pass the book object to BookQueries because we are gonna need its properties to build our queries.
For instance, we need its author to find all books from the same author.
And using the delegate we can call methods from BookQueries with a Book object:
book.findSimilarPurchasedBooks()
The only problem with this is that we also had some static methods to first retrieve our objects.
These are queries that are not related to any relationship or similarities between objects, so they are static methods in the Book class:
static def findBestSellersInMonth() {
findAll("some HQL")
}
The problem is that static methods are not delegated.
In our project we decided to scrap the @Delegate for good, move all custom query methods to BookQueries, including the static ones, transform them into closures and inject them manually, one by one, into the Book class during the application BootStrap, using Groovy’s Expando metaclass.
But I didn’t really like this approach.
I think I’d rather keep the @Delegate, and then move the static methods to a BookRepository class:
class BookRepository {
def findBestSellersInMonth() {
return Book.findAll("some HQL")
}
}
It might seem as a step backwards.. “why do you need repositories if you have your rich domain that can retrieve itself?”
But the static modifier already tells that these methods don’t belong to our domain *objects*.
They are there to initialize the objects, retrieve them from somewhere (the database).
And, well, isn’t this part of the definition of repositories? =P
Update — May 10th ———————————
Peter Ledbrook suggested using Grails’ named queries (see comments). It looks indeed very nice, but we really wanted to move our queries out of our domain class, to make it more readable and, well, prettier
With the named queries we could move a static block containing the static queries to the BooksQueries class and assign this static block to a static nameQueries block in the Book class, and this would import the static queries.
Today I was messing with this a bit more and I realized that I could just inject the static methods into my domain class from inside my delegate itself.
It’s so simple I don’t understand how come I didn’t think about that before ![]()
So, if you don’t like the repository approach, you can always have something like:
class Book {
@Delegate BookQueries bookQueries = new BookQueries(this)
// all Book properties, constraints, domain methods, etc
}
class BookQueries {
static {
BookQueries.metaClass.methods.each {
if (it.static) {
Book.metaClass.static."$it.name" = owner.&"$it.name"
}
}
}
Book book
BookQueries(Book book) {
this.book = book
}
def findAllBooksFromTheSameAuthor() {
return Book.findAll("some HQL")
}
static def findBestSellersInMonth() {
return Book.findAll("some HQL")
}
}
Leave a Reply Cancel reply
Categories
- Agile (1)
- Art (1)
- Development (3)
- English (16)
- estonia (6)
- Gadgets (1)
- Internet (1)
- Java (3)
- JavaScript (3)
- Jeez (5)
- Linux (9)
- Misc (13)
- Music (5)
- Portuguese (35)
- Programming (16)
- Testing (2)
- Trips (1)
- UFSCar (4)
- Videos (1)
Blogroll
Links
Archives
- May 2012 (1)
- April 2012 (1)
- June 2011 (1)
- January 2011 (1)
- December 2010 (1)
- September 2010 (1)
- June 2010 (1)
- May 2010 (2)
- April 2010 (2)
- July 2008 (1)
- June 2008 (1)
- May 2008 (8)
- April 2008 (1)
- December 2007 (3)
- June 2007 (2)
- May 2007 (3)
- April 2007 (1)
- March 2007 (3)
- February 2007 (1)
- December 2006 (5)
- November 2006 (7)
- September 2006 (2)
An alternative approach is to use Grails’ named queries:
http://grails.org/doc/latest/ref/Domain%20Classes/namedQueries.html
Available since Grails 1.2.
Peter
Oh, right! I didn’t know about these named queries. Our previous project was all done with Grails 1.1, I guess that’s why. Thank you, Peter!
So I am thinking mixing @Delegate and the named queries should do it in a much nicer way. Or can I use the named queries for the instance methods too? (Without having to pass the objects as parameters).
I will update the post later adding the information about named queries.
Actually, I just figured that we can use the delegate annotation and inject only the static methods manually.
I think this is a pretty nice solution, I will update later the post adding it.