I observed that a typical way of doing something in rails, if data-base related changes happen, is to use callbacks.
For eg: if you want to delete a
phone, you need to remove all the
apps installed in the phone.
In monolythic rails app, this is what folks do:
class Phone after_commit :delete_installed_apps, on: :destroy end
This innocent looking code silently introduces a nasty pattern of doing things.
Everytime someone wants to do something when a phone is deleted, they’ll start adding after_commit methods.
class Phone after_commit :delete_installed_apps, on: :destroy after_commit :delete_user_data, on: :destroy after_commit :notify_app_dev, on: :destroy end
The code smell here is that the class
Phone knows about things to do after the phone is destroyed.
It absolutely shouldn’t know this.
Another issue is that these procedural methods are executed one by one.
delete_installed_apps -> delete_user_data -> notify_app_dev
Exception raised in any one of them causes other methods to not execute.
When I faced this problem, I did the following to just get away with the problem.
def DeletePhone def execute(phone) phone.destroy! ensure please_execute_this_method_even_if_callbacks_raises_exceptions end end
This introduces the same pattern. The only difference here is that instead of putting it on the callback, we start
putting it in the
Coming back to the main problem, the code smell.
How can we get rid of the extra information in the Phone class? By sending an event.
Phone’s object can send an event that it’s destroyed, then other objects could just listen to