Using Transients for Validations in Grails

Craig Burke (@craigburke1) had a tweet on July 16 that mentioned Grails transients and a mini-conversation ensued.  I mentioned that transients come in handy for “faux validations” (my term; I literally made it up on the spot) but couldn’t explain in enough clarity what I was doing with transients in 140 chars on Twitter.  This post will try to explain things.

Summary

A transient comes in handy to validate a field that won’t be saved in Object A, but needs to be saved in Object B, and Object B doesn’t have a form for editing.  It needs to be valid for Object B, so validate it on the form for Object A, but just don’t save it as part of Object A.

Details

About a week before that Twitter conversation, transients came in very handy for me.  The timing was uncanny, hence my tweets engaging him on it.  For purposes of this post “faux validations” are standard validations against a domain property via the constraints closure, but where the property is never saved.  It’s fake.

This is a piece of my constraints closure, with actual comments for the next dev behind me that has to maintain this app.

static constraints = {
    /*This style of validation is needed for Effective Date because it's transient.
      We validate it, we just don't save it (the saving will be done at the assetTransaction level). By the time we get there, we want
      a valid date to use.  We can't validate there because by that time the assetInstance has been saved and a
      validation round trip is not possible. This situation arose when we added an AssetChangedEvent and Effective Date was requested to be
      on the form. Effective Date doesn't exist on the Asset, hence it's transient.
     */
    effectiveDate(nullable: false, bindable: true, validator: { val, obj ->
                      //only validate the effectiveDate field when the entity is not yet persistent.
                      obj.id || val } )

Per the comments there, here’s the contextual situation of how transients for validation came in handy.

  • There was a form that managed CRUD for an Asset.  Nothing special here.
  • The Asset domain object does not have a property called Effective Date.  This will be important and where the transient comes in.
  • An Asset hasMany Asset Transactions.
  • An AssetTransaction belongsTo a number of different Asset Events, but for purposes of this conversation, it belongs to AssetChangedEvent.  Other Asset Events include AssetMovedEvent, AssetTransferredEvent, AssetCheckedOutEvent, and so on.
  • A recent request for an Audit Trail (I didn’t use the plugin) means I am adding potentially multiple AssetTransaction records for a single AssetChangedEvent.

To recap:  I can edit an Asset, change a bunch of fields (say, 7 fields) and have 7 entries in the Audit Trail/Transaction History.  Hold that thought.

Here’s the kicker:  For the other Asset Events, it’s not a pure CRUD situation, per se.  It’s not like you’re editing an existing record.  You’re performing an event (i.e. you’ll move an Asset or Retire an Asset, etc.)  In the case of changing an Asset, you’re doing both:  you’re editing the underlying Asset domain object, and you’re indirectly creating AssetTransaction records.

With that context, the technical point is this: Effective Date is a required field, and it needs to be a valid Date.  That’s easy…but remember that form doing the Asset editing is not binding to the AssetTransaction domain object — it’s binding to the Asset domain object.

That’s where the transient comes in.  We attach a transient EffectiveField property to Asset and now the constraints for it are used in validations.  Since the Asset won’t save unless validations pass, we know that when it does save, EffectiveDate is a valid Date.  With that field in hand, once a few other things happen, we eventually take that value and attach it to the AssetTransaction.effectiveDate field.  And because we might have multiple AssetTransactions per edit (one per field changed), we’ll stick that single EffectiveDate onto each AssetTransaction.effectiveDate like so:

//Update AssetTransaction Record
changesMade.each {
    def fieldChanged = it.key
    def originalValue = it.value.originalValue
    def newValue = it.value.newValue

    def assetChangedEventInstance = new AssetChangedEvent(params)
    AssetTransaction assetTransaction = new AssetTransaction()
    assetChangedEventInstance.assetTransaction = assetTransaction
    assetChangedEventInstance?.assetTransaction?.effectiveDate = assetInstance?.effectiveDate
    assetChangedEventInstance?.assetTransaction?.transactionType = AssetTransactionTypeEnum.CHANGED.key
    assetChangedEventInstance?.assetTransaction?.note = "Field $fieldChanged changed:  from [$originalValue] to [$newValue]"
    assetChangedEventInstance?.assetTransaction?.asset = assetInstance
    assetChangedEventInstance.accessControlGroup = assetInstance.accessControlGroup
    assetChangedEventInstance?.assetTransaction?.accessControlGroup = assetInstance.accessControlGroup
    assetChangedEventInstance.save(flush: true)
}

This functionality was just added a few days ago and I’m sure it could be more elegant.  It feels a little “bulky” or verbose for Groovy, but it works.

It’s also important to mention that transients don’t automatically bind, so the bindable constraint had to be set:

effectiveDate(nullable: false, bindable: true, validator: { val, obj ->
          //only validate the effectiveDate field when the entity is not yet persistent.
          obj.id || val } )

And, in the interest completeness, here’s the effectiveDate field in the Asset object.

String assetTagGroup                //Transient
String assetTagRangeStart           //Transient
String assetTagRangeQty             //Transient
Date effectiveDate = new Date()     //Transient

static transients = ['assetTagGroup', 'assetTagRangeQty', 'assetTagRangeStart', 'effectiveDate']

That’s it.