DEV Community

JT Dev
JT Dev

Posted on

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.

Oldest comments (0)