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