Things you should know about Rails Active Record callback, an AOP in other language
- Should avoid it when possible because its abstraction is not clear. The method such as
before_save…etc. don’t have a clear purpose of the business logic rather than CRUD. It applies whenever before or after save active record object. Even you have conditional callback, it will exist in model or tight coupling with model itself. It probably better to have service class to do the work which has clear purpose of the business logic.
- If it is used it should be asynchronous, so that the main action like save or update request won’t rely on the the completion of the callback action. Use Sidekiq for example.
after_savefor example, the callback always execute no matter main action is execute successfully or not. With
on:option will tight with database transaction, this ensure that callback only execute when the transaction is complete. Another example using with asynchronous like Sidekiq dealing with
after_commitsidekiq might pickup
idbefore object is committed to database.
- On the other hand to point 1, I debate callback vs service class. Rails active record callback has its own value. For example, if
after_save: do_somethingis a must in any circumstance, we should use call back because new developer might not be able to cover up
do_somethingin any adding new service class they create. More concrete example, if we want to destroy records in table A associated with table B when record in table B is changed, using callback might be a good option because if we have many service classes to trigger changing record in table A, we don’t need to modify all those service classes to destroy record in table A. If new developer create new interface or service class related to changing record in table B, they don’t have to know if they need to destroy record in table B because the callback in model say it all with test as well. However, if you are trying to use callback just in for specific logic rather than logic in model object, having service class is better option.
More best practice using callback in Rails: https://engineering.gusto.com/the-rails-callbacks-best-practices-used-at-gusto/