Este tutorial documenta dois refactors realizados no projeto ClinicSync, uma aplicação de gestão de clínicas médicas desenvolvida como parte do meu MBA em Ruby on Rails. O objetivo foi aplicar boas práticas arquiteturais utilizando Service Objects para melhorar a legibilidade, manutenção e escalabilidade do sistema. O que são Service Objects? Service Objects são classes simples usadas para organizar lógicas de negócio que não pertencem diretamente a models, controllers ou views. Eles são uma forma de aplicar o princípio da Responsabilidade Única (SRP) na prática, deixando cada parte do sistema com um papel bem definido. Quando usar um Service Object? Use um Service Object quando: A lógica é complexa demais para ficar dentro de um controller A funcionalidade não pertence naturalmente a nenhum model Você precisa reutilizar a mesma lógica em mais de um lugar Quer escrever código mais limpo e fácil de testar Estrutura básica de um Service Object class NomeDoServico def self.call(params) new(params).call end def initialize(params) @params = params end def call # lógica principal aqui end end Refactor 1: AuthorizeUser Service Objetivo: Retirar dos controllers a lógica repetida de verificação de tipo de usuário. Antes: 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 Depois: 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 criado: class AuthorizeUser def self.call(user, user_type) user.is_a?(user_type) end end Benefícios: Lógica DRY Fácil de manter Código mais claro e reutilizável Refactor 2: AuthenticateUser Service Objetivo: Tirar do SessionsController a lógica de autenticação de usuário. Antes: 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 Depois: 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 criado: class AuthenticateUser def self.call(email_address, password) User.authenticate_by(email_address: email_address, password: password) end end Benefícios: Controller mais limpo Fácil de expandir para login social, 2FA etc. Separacão de responsabilidades clara Conclusão Esses dois pequenos Service Objects deixaram o projeto: Mais limpo Mais testável Pronto para crescer Service Objects são uma forma poderosa de aplicar Clean Code e preparar sua aplicação Rails para o futuro. Escrito por: [Leonardo Quadros Fragozo]

May 1, 2025 - 16:30
 0

A Ruby On Rails Refactoring Code Using Service Objcets

Este tutorial documenta dois refactors realizados no projeto ClinicSync, uma aplicação de gestão de clínicas médicas desenvolvida como parte do meu MBA em Ruby on Rails. O objetivo foi aplicar boas práticas arquiteturais utilizando Service Objects para melhorar a legibilidade, manutenção e escalabilidade do sistema.

O que são Service Objects?

Service Objects são classes simples usadas para organizar lógicas de negócio que não pertencem diretamente a models, controllers ou views.
Eles são uma forma de aplicar o princípio da Responsabilidade Única (SRP) na prática, deixando cada parte do sistema com um papel bem definido.

Quando usar um Service Object?

Use um Service Object quando:

  • A lógica é complexa demais para ficar dentro de um controller
  • A funcionalidade não pertence naturalmente a nenhum model
  • Você precisa reutilizar a mesma lógica em mais de um lugar
  • Quer escrever código mais limpo e fácil de testar

Estrutura básica de um Service Object

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

  def initialize(params)
    @params = params
  end

  def call
    # lógica principal aqui
  end
end

Refactor 1: AuthorizeUser Service

Objetivo:

Retirar dos controllers a lógica repetida de verificação de tipo de usuário.

Antes:

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

Depois:

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 criado:

class AuthorizeUser
  def self.call(user, user_type)
    user.is_a?(user_type)
  end
end

Benefícios:

  • Lógica DRY
  • Fácil de manter
  • Código mais claro e reutilizável

Refactor 2: AuthenticateUser Service

Objetivo:

Tirar do SessionsController a lógica de autenticação de usuário.

Antes:

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

Depois:

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 criado:

class AuthenticateUser
  def self.call(email_address, password)
    User.authenticate_by(email_address: email_address, password: password)
  end
end

Benefícios:

  • Controller mais limpo
  • Fácil de expandir para login social, 2FA etc.
  • Separacão de responsabilidades clara

Conclusão

Esses dois pequenos Service Objects deixaram o projeto:

  • Mais limpo
  • Mais testável
  • Pronto para crescer

Service Objects são uma forma poderosa de aplicar Clean Code e preparar sua aplicação Rails para o futuro.

Escrito por: [Leonardo Quadros Fragozo]