👏

📘 サービスオブジェクトによるRuby on Railsのリファクタリングチュートリアル

に公開

このチュートリアルでは、ClinicSync プロジェクトで行った2つのリファクタリングについて解説します。このアプリケーションは、私のRuby on Rails MBAの一環として開発された医療クリニック管理システムです。目的は、サービスオブジェクト(Service Objects) を活用して、コードの可読性、保守性、拡張性を向上させることです。

サービスオブジェクトとは?

サービスオブジェクト は、モデルやコントローラー、ビューに属さない ビジネスロジックを整理するためのシンプルなクラス です。
これは、単一責任の原則(Single Responsibility Principle, SRP) を実践する方法の1つであり、システムの各部分に明確な役割を与えることができます。


サービスオブジェクトを使うべきタイミング

次のような場合にサービスオブジェクトの導入を検討しましょう:

  • ロジックが複雑でコントローラーに収まりきらないとき
  • 機能が特定のモデルに自然に属さないとき
  • 同じロジックを複数箇所で再利用したいとき
  • よりクリーンでテストしやすいコードを目指したいとき

サービスオブジェクトの基本構造

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

  def initialize(params)
    @params = params
  end

  def call
    # メインのロジックをここに記述
  end
end

リファクタリング①:AuthorizeUser サービス

目的:

コントローラーに繰り返し書かれていたユーザータイプの判定処理を外部化する。

リファクタリング前:

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

リファクタリング後:

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

作成されたサービス:

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

メリット:

  • ロジックの重複排除(DRY)
  • 保守性の向上
  • クリーンで再利用可能なコード

リファクタリング②:AuthenticateUser サービス

目的:

ユーザー認証処理を SessionsController から分離する。

リファクタリング前:

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

リファクタリング後:

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

作成されたサービス:

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

メリット:

  • コントローラーの簡素化
  • ソーシャルログインや二要素認証などの拡張が容易
  • 責務の明確な分離

まとめ

この2つの小さなサービスオブジェクトにより、プロジェクトは:

  • よりクリーンに
  • テストしやすく
  • 拡張しやすくなった

サービスオブジェクトは、クリーンコードを実現し、Railsアプリケーションを将来の拡張に備えるための強力な手法です。


作成者: [レオナルド・フラゴーゾ]

Discussion