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 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 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 Service Created: class AuthorizeUser def self.call(user, user_type) user.is_a?(user_type) end end 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 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 Service Created: class AuthenticateUser def self.call(email_address, password) User.authenticate_by(email_address: email_address, password: password) end end 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]

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
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
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
Service Created:
class AuthorizeUser
def self.call(user, user_type)
user.is_a?(user_type)
end
end
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
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
Service Created:
class AuthenticateUser
def self.call(email_address, password)
User.authenticate_by(email_address: email_address, password: password)
end
end
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]