Leveraging Services in Rails: Advantages and Implementation

Alessio Bussolari
2 min readNov 1, 2023

--

Ruby on Rails is renowned for its convention-over-configuration approach, which emphasizes a specific way of structuring applications. As applications grow, models and controllers can become bloated, making the code harder to maintain and understand. That’s where service objects come into play.

Advantages of Using Services in Rails:

  1. Single Responsibility Principle:
    Service objects follow the Single Responsibility Principle (SRP), meaning each service has one job. This makes the code more maintainable and readable.
  2. Reduced Model and Controller Bloat:
    By moving complex business logic from models and controllers to services, you can keep models focused on data relationships and controllers on handling HTTP requests.
  3. Reusability:
    Services can be reused in different parts of the application, promoting the DRY (Don’t Repeat Yourself) principle.
  4. Testability:
    With a singular focus, services are easier to test, ensuring that they perform their specific task correctly.
  5. Modularity:
    Services can be composed or called within other services, promoting modularity and a layered architecture.

Implementation Example in Rails:

Imagine you have a blog application, and you want to create a service for publishing articles. Here’s how you can structure and implement a `PublishArticleService`.

Setup the Service Object:

# app/services/publish_article_service.rb

class PublishArticleService
def initialize(article)
@article = article
end

def call
return false unless @article.valid?

@article.published = true
@article.published_at = Time.now
@article.save
end
end

Using the Service in a Controller:

# app/controllers/articles_controller.rb

def publish
article = Article.find(params[:id])
service = PublishArticleService.new(article)

if service.call
redirect_to article_path(article), notice: 'Article was successfully published.'
else
redirect_to article_path(article), alert: 'Article could not be published.'
end
end

Writing Tests for the Service:

# test/services/publish_article_service_test.rb

require 'test_helper'

class PublishArticleServiceTest < ActiveSupport::TestCase
test 'should publish a valid article' do
article = articles(:one) # Assuming you have fixtures set up
service = PublishArticleService.new(article)
assert service.call
assert article.published
assert_not_nil article.published_at
end

# ... additional tests for edge cases ...
end

In conclusion, while Rails doesn’t enforce the use of service objects, they can significantly enhance the architecture of larger applications, making them cleaner, more maintainable, and modular.

--

--

Alessio Bussolari

Ruby on Rails programmer since 2009. Current CTO at COSMIC SRL, where I lead the team in creating innovative solutions.