Service Objects in Rails
As Rubyists, we always aim to keep our controllers clean and DRY. But often controllers do need to talk to other parts of the application--models, mailers, etc. So, when it comes to running a search function or sending an email, we often wonder where to put that code. The answer may be in the model, but our models too aim to be skinny.
Enter, the service object.
Service Objects at your service
A service object is responsible for implementing the user's interaction with the application. In the example of a rails app that allows users to subscribe to musical artists and receive updates about their upcoming events, the user's search for bands to follow is a good case for a service object.
By searching our applications database of bands for a band to follow, the user is interacting directly with our database. This interaction can be handled by a service object--a sort of secretary for our application that accepts input from the user, performs work with that input and returns a result.
How do you create a service object?
First, created a 'services' directory in the top level of your app. In this directory, you can create plain old ruby classes that represent your service objects. For this example, we'll make a service object called 'SearchBands'. Our file,
search_bands.rb will live in the services directory.
class SearchBands attr_accessor :band_params def initialize(band_params) @band_params = band_params end def search Band.find_by(self.band_params) end end
The SearchBands class initializes with the search parameters sent to the UserBands controller by the user's action of submitting the search form. It has a method,
search, that dips into our database to return the band that the user is attempting to search for.
How do you implement service objects
We call on our service objects in the appropriate controller. In this case, the UserBands controller is responsible for associating a user with a new band they want to 'follow'. This takes place in the 'create' action of this controller. Before a user can be subscribed to a particular band, they must search for that band. By submitting that search form, they are requested to be subscribed. So, the create action of the UserBands controller needs to find the band the user searched for, then associate the user to the band. Here is where our service object comes in.
class UserBandsController < ApplicationController def create @band = SearchBands.new(band_params).search @user = current_user @user.bands << @band @user.save end end
Why should you use a service object?
Service objects are helpful for a number of reasons. They keep your controller-side code skinny and DRY and the make the intention of your code more explicit. By abstracting certain functions, it makes your code more maintainable and flexible as your application grows.
This has been a super simple into to the usage of service objects. For a more in depth look, check out this excellent blog post from netguru.