It’s been a while since my last post. I was writing my engineer’s thesis which caused general disgust towards writing at all. Anyway, these sad times are over and here comes the shiny new blogpost about introducing service layer in Rails applications. It does not contain any breakthrough thoughts, but is rather a mixture of ideas I learned from great Ruby developers.

Why?

We need a place to store the domain logic of our app. If you follow The Rails Way it can be: a) controller b) model

Controller is an entry point to our application. However, it’s not the only possible entry point. I would like to have my logic accessible from:

Rake tasks

background jobs

console

tests

If I throw my logic into a controller it won’t be accessible from all these places. So let’s try “skinny controller, fat model” approach and move the logic to a model. But which one? If a given piece of logic involves User , Cart and Product models – where should it live?

A class which inherits from ActiveRecord::Base already has a lot of responsibilities. It handles query interface, associations and validations. If you add even more code to your model it will quickly become an unmaintainable mess with hundreds of public methods. How can you change one line of your model’s code with confidence that nothing breaks up? It’s much easier when the whole class fits into a single screen of code.

How to implement a service?

A service is just a regular Ruby object. Its class does not have to inherit from any specific class. Its name is a verb phrase, for example CreateUserAccount rather than UserCreation or UserCreationService . It lives in app/services directory. You have to create this directory by yourself, but Rails will autoload classes inside for you.

app/services/create_user_account.rb 1 2 3 4 5 6 class CreateUserAccount def call ( params ) [. . .] end end

The service does one thing and has one public method. I used to name it perform , but call is slightly better. Lambda also responds to call so in your tests you have the possibility to mock service with lambda, which is quite convenient.

Splitting big services

As you implement a service object you may notice that it grows in size. It is usually a sign that this service has more than one responsibility and should be composed of a few smaller services. How to organize them? Here comes the example:

app/services/create_user_account/generate_token.rb 1 2 3 4 5 6 7 8 class CreateUserAccount class GenerateToken def call ( user ) [. . .] end end end

app/services/create_user_account/send_welcome_email.rb 1 2 3 4 5 6 7 8 class CreateUserAccount class SendWelcomeEmail def call ( user ) [. . .] end end end

app/services/create_user_account.rb 1 2 3 4 5 6 7 8 9 class CreateUserAccount def call ( params ) [. . .] generate_token . call ( user ) send_welcome_email . call ( user ) [. . .] end end

So you have to create a new directory app/services/create_user_account and place there those extracted services. Each of them should be encapsulated in CreateUserAccount namespace. Again, Rails autoloads everything for you.

Dependency injection

How can you obtain instance of this “child service” in your “parent service”? You could write something similar to:

app/services/create_user_account.rb 1 2 3 4 5 6 7 8 9 class CreateUserAccount def call ( params ) [. . .] generate_token = GenerateToken . new generate_token . call ( user ) [. . .] end end

In such a case you hardcode instantiation of GenerateToken service inside CreateUserAccount . Your are not able to easily provide mock implementation of GenerateToken inside your tests so you cannot test CreateUserAccount service in isolation.

The simple yet powerful solution is described here. Let’s apply it in our case:

app/services/create_user_account.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class CreateUserAccount def self . build new ( GenerateToken . build , SendWelcomeEmail . build ) end def initialize ( generate_token , send_welcome_email ) @generate_token = generate_token @send_welcome_email = send_welcome_email end def call ( params ) [. . .] @generate_token . call ( user ) [. . .] end end

app/services/create_user_account/generate_token.rb 1 2 3 4 5 6 7 8 9 10 class CreateUserAccount class GenerateToken def self . build new end [. . .] end end

app/services/create_user_account/send_welcome_email.rb 1 2 3 4 5 6 7 8 9 10 class CreateUserAccount class SendWelcomeEmail def self . build new end [. . .] end end

We pass all dependencies to the constructor of the service. But we also provide a factory method – build which knows the sane way to build the service.

Complete example

Here is the complete example which combines everything:

app/services/create_user_account.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class CreateUserAccount def self . build new ( GenerateToken . build , SendWelcomeEmail . build ) end def initialize ( generate_token , send_welcome_email ) @generate_token = generate_token @send_welcome_email = send_welcome_email end def call ( params ) # code which creates user model [. . .] @generate_token . call ( user ) @send_welcome_email . call ( user ) user end end

app/services/create_user_account/generate_token.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 class CreateUserAccount class GenerateToken # this service has no dependencies def self . build new end def call ( user ) [. . .] end end end

app/services/create_user_account/send_welcome_email.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 class CreateUserAccount class SendWelcomeEmail # this service has no dependencies def self . build new end def call ( user ) [. . .] end end end

Update 27.12.2014

Following suggestions in the comments, I’ve prepared a dummy app to demonstrate usage of service objects. I hope it’s at least a bit more concrete than the examples above. Here is the link: https://github.com/adamniedzielski/service-objects-example

Update 06.05.2019

Four and half years after writing this blog post and the Rails community still does not have a standard way of writing service objects :) You can read what my fellow programmer Paweł thinks about service objects in Rails.