Most of the domain objects our company uses will have some common properties. These represent the user that created the object, the user that last updated the object, and the program they used to do it.
In the interest of DRYing out my domain classes, I want to find some way to add the same beforeInsert and beforeUpdate logic to all domain classes that have these columns without interfering with those that don't.
How I'd like to do it is using a Mixin with its own beforeInsert and beforeUpdate methods. I know you can use Mixins on domain classes.
package my.com
import my.com.DomainMixin
@Mixin(DomainMixin)
class MyClass {
String foo
String creator
String updater
static constraints = {
creator nullable:false
updater nullable:false
}
}
package my.com
class DomainMixin {
def beforeInsert() {
this.creator = 'foo'
this.updater = 'foo'
}
def beforeUpdate() {
this.updater = 'bar'
}
}
Unit tests would indicate that the beforeInsert method isn't actually getting fired when implemented this way.
Side note: I also know it's possible to add the methods in a BootStrap.groovy file using the metaClass, but my curiosity has gotten the better of me and I really want to see if the mixin works. Feel free to tell me that this is the better way to do it and I ought not muddle where man ought not.
FYI, use of
groovy.lang.Mixin
is strongly discouraged (by the Groovy project leader, for one). If you have to use mixins you should usegrails.util.Mixin
instead. One thing I don't like about your mixin approach is the implicit and unenforced assumption that the target of the mixin hascreator
andupdater
propertiesPersonally, I would probably just use plain-old inheritance for this, e.g.
any domain classes that need to be audited would simply extend
Audit
. An alternative (and preferable) approach would be to use a trait rather than an abstract base class, but you'll need to be using a fairly recent Grails version in order to do this.Instead of mixins you can use the Event Bus of the Grails Platform plugin to make your app more DRY. The plugin supports adding listeners to GORM events and they can be applied to any given class or interface instance. You can also attach more than one listener and write more concise unit tests.
If you want to share some logic accross domains, they must implement an interface and then create a service with the following method: