DEV Community

JT Dev for JetThoughts LLC

Posted on • Updated on • Originally published at jtway.co

How to avoid callbacks using services.

Image description

Often programmers abuse callbacks, not fully understanding that in the end their code will be confusing and non-obvious. There are several ways to avoid using callbacks. Today I will tell you how to do this using services.

Lets see on a code:

class User < ApplicationController
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to root_path, notice: "User created!"
    else   
      render :new, error: 'Failed to create user!'
    end
  end
end
class User < ApplicationRecord
  before_create :populate_serial_number
  private
  def populate_serial_number
    self.serial_number ||= SecureRandom.uuid
  end
end
Enter fullscreen mode Exit fullscreen mode

What is the problem with this code? We get a non-obvious (magical) action. We do not pass any data about the serial number in the parameters, and we do not explicitly set this value anywhere. This happens automatically with a callback.

Let’s implement the same thing but using a service.

class CreateUser
  def self.call(params)
    @user = User.new(params)

    populate_serial_number(@user)
    # Other actions for the user

    user.save!
  end
  def self.populate_serial_number(user)
    user.serial_number ||= SecureRandom.uuid
  end
end
class User < ApplicationRecord
end
class User < ApplicationController
  def create
    @user = CreateUser.call(user_params)
    if @user
      redirect_to root_path, notice: "User created!"
    else   
      render :new, error: 'Failed to create user!'
    end
  end
  def user_params
    ...
  end
end
Enter fullscreen mode Exit fullscreen mode

What advantages does this approach give us?

  • Suppose we have users can be created from the Admin panel and through the API. Depending on the method of creation, we may have a different set of actions performed with the user. It is very convenient to make two separate services for creating a user. For example: Admin::CreateUser and Api::CreateUser
  • Such services are easy to test.
  • They are easy to expand.
  • The code becomes much clearer and more predictable.

Top comments (0)