DEV Community

loading...
Main Street

The "What" and "Why" or How We are hiding those ugly IF's in our Service Objects

ortegacmanuel profile image Manuel Ortega Updated on ・2 min read

We use Service Objects to encapsulate and organize business domain logic in our Rails applications. But how do we organize the "What" and "Why" within a single Service Object?

The "What" and "Why"? What do I mean? I hope the following code bites makes clear my point here.

Before

class Customers::CreateService < ApplicationService
  def initialize(customer_params)
    @customer_params = customer_params
  end

  def perform
    @customer = Customer.new(@assignment_params)
    if @customer.save
      sync_customer if TestApp.config[:sync_enabled]     
      create_qualified_notification if @customer.status_qualified?
      create_twilio_proxy_session if TestApp.config[:proxy_enabled]
      Result.new(@customer, true)
    else
      Result.new(@customer, false)
    end
  end

  private

  def sync_customer
    ...
  end

  def create_qualified_notification
    ...
  end

  def create_twilio_proxy_session
    ...
  end
end
Enter fullscreen mode Exit fullscreen mode

As you can see in this before version of our Service Object we are mixing the "What" and "Why" within the perform public method. We are putting what the perform method does when a customer is created, for example, it creates a qualified notification, but we are also putting the "Why" here, it is, we are creating the qualified notification because the customer is being created with qualified status.

The "What" is what the Service Object does and the "Why" is the control-flow logic.

After

class Customers::CreateService < ApplicationService
  def initialize(customer_params)
    @customer_params = customer_params
  end

  def perform
    @customer = Customer.new(@assignment_params)
    if @customer.save
      sync_customer     
      create_qualified_notification
      create_twilio_proxy_session
      Result.new(@customer, true)
    else
      Result.new(@customer, false)
    end
  end

  private

  def sync_customer
    if TestApp.config[:sync_enabled]
      ...
    end
  end

  def create_qualified_notification
    if @customer.status_qualified?
      ...
    end
  end

  def create_twilio_proxy_session
    if TestApp.config[:proxy_enabled]
      ...
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

As you can see in the after version we are moving the control-flow logic, the "Why", to the private methods and keeping only the "What" in the perform public method:

 sync_customer     
 create_qualified_notification
 create_twilio_proxy_session
Enter fullscreen mode Exit fullscreen mode

The argument for this is that anyone from our team or future me can read the perform method and reason about what it is trying to accomplish faster because it's just the "What".

Much of the credit goes to JP for recommending this "What" and "Why" separation in a code review

Discussion (0)

Forem Open with the Forem app