Feb 29, 2016

Scala & Lift and the record lifecycle callbacks

I was digging around the source code of our project when I noticed spread in many snippets, code that perform the same action (such as send a push notification to a user) after a particular object gets persisted in the database (for example when a new chat message is sent) or when a change of state happens (a payment for a particular order has been made).

The tecnhique I'm about to discuss can be applied to many frameworks: Java and Hibernate, Ruby with Active Record, Meteor and its Collection hooks, and for all the homo erectus who are reading, also with some PHP frameworks, just to name a few.

Simplifying and DRYing out the code

The framework-agnostic recipe is the following:

  • Identify repeated code that perform some action after a change of state persisted on the database
  • Cut the previously identified code
  • Paste it in the model and override the lifecycle callback that make sense the most, as explained below

In Lift we accomplish this with the LifecycleCallbacks trait that we can mix in the fields of our record, MongoRecord in our case. This trait make us available the following methods

 
 def beforeValidation {}
 def afterValidation {}
 
 def beforeSave {}
 def beforeCreate {}
 def beforeUpdate {}

 def afterSave {}
 def afterCreate {}
 def afterUpdate {}
 

that we can override in order to perform actions at various points during the lifecycle of a given instance. Their names are self explanatory but a thing to remember is that beforeSave is always called before beforeCreate or beforeUpdate.
afterSave, similarly, is called after afterCreate or afterUpdate.

With this tool in our toolbelt we can centralize the code that was spread all over the codebase, in our model, making our software more mantainable, DRY, and clean.

Here is an example

 class ChatMsg extends MongoRecord[ChatMsg] 
   with ObjectIdPk[ChatMsg]{
     def meta = ChatMsg
     object text extends StringField(this, 500) 
       with LifecycleCallbacks{
         override def afterSave: Unit = {
             //send push notification or whatever
     }
   }
 }

Go and try it out!

Keep Learning.
Until next time emoji-sunglasses