DEV Community

Leonardo Fragozo
Leonardo Fragozo

Posted on

1

πŸ“˜ Refactoring with Service Objects in Ruby on Rails

Image description

This tutorial documents two refactors applied in the ClinicSync project, a medical clinic management application developed as part of my MBA in Ruby on Rails. The goal was to apply architectural best practices using Service Objects to improve code readability, maintainability, and scalability.

What are Service Objects?

Service Objects are simple classes used to organize business logic that doesn't belong directly in models, controllers, or views.
They are a way to apply the Single Responsibility Principle (SRP) in practice, giving each part of the system a clearly defined role.


When to Use a Service Object

Use a Service Object when:

  • The logic is too complex to stay in the controller
  • The functionality doesn't naturally belong to a model
  • You need to reuse the same logic in multiple places
  • You want cleaner, more testable code

Basic Structure of a Service Object

class ExampleService
  def self.call(params)
    new(params).call
  end

  def initialize(params)
    @params = params
  end

  def call
    # main logic here
  end
end
Enter fullscreen mode Exit fullscreen mode

Refactor 1: AuthorizeUser Service

Goal:

Remove repeated user type verification logic from the controllers.

Before:

def require_admin
  unless user_signed_in? && current_user.is_a?(Admin)
    redirect_to root_path, alert: "You are not authorized to access this page."
  end
end
Enter fullscreen mode Exit fullscreen mode

After:

def require_admin
  unless user_signed_in? && AuthorizeUser.call(current_user, Admin)
    redirect_to root_path, alert: "You are not authorized to access this page."
  end
end
Enter fullscreen mode Exit fullscreen mode

Service Created:

class AuthorizeUser
  def self.call(user, user_type)
    user.is_a?(user_type)
  end
end
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • DRY logic
  • Easier to maintain
  • Cleaner and reusable code

Refactor 2: AuthenticateUser Service

Goal:

Remove user authentication logic from SessionsController.

Before:

if (user = User.authenticate_by(params.permit(:email_address, :password)))
  start_new_session_for user
  redirect_to home_path_for(user), notice: "Logged in successfully."
else
  redirect_to new_session_path, alert: "Try another email address or password."
end
Enter fullscreen mode Exit fullscreen mode

After:

user = AuthenticateUser.call(params[:email_address], params[:password])

if user
  start_new_session_for user
  redirect_to home_path_for(user), notice: "Logged in successfully."
else
  redirect_to new_session_path, alert: "Try another email address or password."
end
Enter fullscreen mode Exit fullscreen mode

Service Created:

class AuthenticateUser
  def self.call(email_address, password)
    User.authenticate_by(email_address: email_address, password: password)
  end
end
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Cleaner controller
  • Easy to extend for social login, 2FA, etc.
  • Clear separation of concerns

Conclusion

These two small Service Objects made the project:

  • Cleaner
  • More testable
  • Ready to scale

Service Objects are a powerful way to apply Clean Code and prepare your Rails application for future growth.


Written by: [Leonardo Quadros Fragozo]

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

Top comments (0)

ACI image

ACI.dev: Fully Open-source AI Agent Tool-Use Infra (Composio Alternative)

100% open-source tool-use platform (backend, dev portal, integration library, SDK/MCP) that connects your AI agents to 600+ tools with multi-tenant auth, granular permissions, and access through direct function calling or a unified MCP server.

Check out our GitHub!

AWS Security LIVE! From re:Inforce 2025

Tune into AWS Security LIVE! streaming live from the AWS re:Inforce expo floor in Philadelphia from 8:00 AM ET-6:00 PM ET.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❀️