Service Objects in Ruby on Rails…and you
Tags: rails, ruby
Published: 2017-02-28 22:38:25

---
title: Service Objects in Ruby on Rails…and you
published: true
description: We have all been there when you see your controller action getting way too long and hold too much business logic. Here is where Service Objects come to your rescue
cover_image: https://cdn-images-1.medium.com/max/1600/1*piPSVsuuX-HonHEZpFNEMA.jpeg
tags: rails, ruby
---


*Note: This tutorial is an excerpt for the service object chapter in my upcoming book [Building a SaaS Ruby on Rails 5](https://BuildASaaSAppinRails.com). The book will guide you from humble beginnings through deploying an app to production. If you find this type of content valuable, the book is on pre-sale right now!*


* * *
### Where do I put thisÂ stuff?

We have all been there when you see your controller action getting way too long and hold too much business logic. You know you need to email the user, adjust an account, maybe submit to Stripe and finally ping a Slack Webhook. Well, where should it go? This code needs to exist and doesn't seem to fit in the model. This my friend, is where Service Objects come in!

What's so great about a Service Object? I find in most projects there is nothing easier to reason about or test than a POO(Plain Old Object), which in our case is a PORO(Plain Old Ruby Object). Meaning, the Service Object will be a stand alone class the is not inheriting or extending any other classes from Ruby or Rails.


### Let's begin!

Rails does not create a services folder by default so it is our job to create one. A little side-note though, depending on the size of your app or the complexity of business log you may have the need to break services down even further by domain, type or other qualifiers. For the purpose of this post, you can just create a services folder in your app folder `mkdir app/services`

Restart your rails server to pick up the new folder as Rails will autoload directories in the app directory. Now we can create a file for a user registration service `new_registration_service.rb` and fill it with our business logic.

```ruby
class NewRegistrationService  
  def initialize(params)  
    @user = params[:user]  
    @organization = params[:organization]  
  end

  def perform  
    organization_create  
    send_welcome_email  
    notify_slack  
  end

private

def organization_create  
    post_organization_setup if @organization.save  
end

  def post_organization_setup  
    @user.organization_id = @organization.id  
    @user.save  
    @user.add_role :admin, @organization  
  end

  def send_welcome_email  
    WelcomeEmailMailer.welcome_email(@user).deliver_later  
  end

  def notify_slack  
    notifier = Slack::Notifier.new "https://hooks.slack.com/services/89ypfhuiwquhfwfwef908wefoij"  
    notifier.ping "A New User has appeared! #{@organization.name} -   #{@user.name} || ENV: #{Rails.env}"  
  end
end
```

Ok! Lets go over the Service Object section by section!


```ruby
class NewRegistrationService  
  def initialize(params)  
    @user = params[:user]  
    @organization = params[:organization]  
  end
```

Here we are creating the class and then adding the initialize method to create the needed instance variables upon the object being instantiated. In this case, we will pass the object the User model record object and the Organization record object previously created in a new user signup controller.

```ruby
def perform  
    organization_create  
    send_welcome_email  
    notify_slack  
  end
```

Personally, I like to wrap the functionality of the whole set of business login in a perform method. I have also seen some proponents of calling different methods manually from the controller as well. It's really a matter of your preference.

```ruby
def organization_create  
    post_organization_setup if @organization.save  
end

def post_organization_setup  
    @user.organization_id = @organization.id  
    @user.save  
    @user.add_role :admin, @organization  
 end
```

Here we are creating the organization(which was previously validated in the controller that instantiated this Service Object) and then making the necessary updates to the user such as adding the organization_id needed for the belongs_to relation and assigning a role for the authorization system that is scoped to the previously created organization.


```ruby
def send_welcome_email  
    WelcomeEmailMailer.welcome_email(@user).deliver_later  
end
```

A quick call to a Rail's ActionMailer with the user object…


```ruby
def notify_slack  
    notifier = Slack::Notifier.new "https://hooks.slack.com/services/89ypfhuiwquhfwfwef908wefoij"  
    notifier.ping "A New User has appeared! #{@organization.name} -   #{@user.name} || ENV: #{Rails.env}"  
end
```

…and finally notifying the Slack Webhook I use for sign up notifications.

Now, you may be saying to yourself, this makes sense but how and where do you call this Service Object? The following is the code extracted from my Devise Registration controller handling the incoming sign up form and all it's Devise goodness:

```ruby
NewRegistrationService.new({user: resource, organization: @org}).perform
```

Some readers may start to question some of the aspects of the Service Object in this post. Service Objects do not have a strong convention that I have seen and I have fit this one to match my needs. Earlier, when I said you can call public methods individually, instead of using a perform method, would allow you to wrap some error handling logic. Such as this:

```ruby
class NewRegistrationService  
  def initialize(params)  
    @user = params[:user]  
    @organization = params[:organization]  
  end

def organization_create  
  begin  
    post_organization_setup if @organization.save  
  rescue  
    false  
  end  
end

....
```

..and in the controller, something like:


```ruby
if NewRegistrationService.new({user: resource, organization: @org}).organization_create  
  **success logic**  
else  
  ** redirect_to last_path, notice: 'Error saving record'  
end
```

Regardless of your Service Object structure, simply extracting your business logic out of your controllers and into Service Objects will help keep your controllers â€˜skinny' and your application's code easy to follow.

[Note: I follow up with this post with a refactored version of this Service](https://dev.to/rob__race/going-further-with-service-objects-in-ruby-onrails)