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
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
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
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
Top comments (0)