Railsで学ぶ サービスクラスとConcernsの使い分け 方法と実践例

ruby on rails

サービスクラスとConcernsの使い分け は、Railsアプリケーションを効率的に設計するための重要なポイントです。この記事では、サービスクラスとConcernsをどのように使い分けるべきか、その違いを理解し、実際のアプリケーション設計にどのように適用するかを具体的な実践例とともに解説します。


サービスクラスとConcernsの基本

サービスクラスとは?

サービスクラスは、ビジネスロジックを1つの責任に分離し、再利用可能な形にまとめたクラスです。これにより、モデルやコントローラの肥大化を防ぎ、テストが容易になります。例えば、外部API呼び出しや複雑なデータ処理を1つのクラスに集約できます。

Concernsとは?

Concernsは、Railsのモジュールで、複数のモデルやコントローラで共有するコードを簡潔に管理するためのものです。典型的にはスコープやバリデーション、コールバックなどの共通処理を定義する際に使用されます。

使い分けの原則

特徴 サービスクラス Concerns
責任範囲 独立したビジネスロジックを扱う 共通の振る舞いやロジックを共有
適用対象 コントローラやモデルに依存しない処理 複数のモデルやコントローラでの再利用
利用シナリオ 外部API呼び出し、バッチ処理など 共通スコープやバリデーション

サービスクラスを使わない場合の課題

コントローラが肥大化する


def create
  @user = User.new(user_params)
  if @user.save
    NotificationMailer.welcome_email(@user).deliver_now
    Log.create(action: "user_created", user_id: @user.id)
    redirect_to @user
  else
    render :new
  end
end

上記の例では、1つのアクションに複数の処理(ユーザー作成、メール送信、ログ記録)が詰め込まれており、可読性が低く、テストも困難です。

モデルに処理が集中しすぎる

モデルにコールバックやメソッドが集中し、肥大化することで保守が難しくなることがあります。例えば、ユーザー作成時に複数の副作用(メール送信や外部システムとの連携)がある場合、モデル内でそのすべてを処理すると複雑になります。


サービスクラスとConcernsの使い分け

サービスクラスを使うべきケース

  • 外部APIとのやり取り
  • 複雑なバッチ処理やデータ加工
  • 他のクラスに依存しない処理

Concernsを使うべきケース

  • 複数のモデルやコントローラで再利用される処理
  • 共通スコープやバリデーション、コールバックの共有

具体例:サービスクラスの実装

例1: ユーザー登録とメール送信を分離


# app/services/user_registration_service.rb
class UserRegistrationService
  def initialize(user_params)
    @user_params = user_params
  end

  def call
    user = User.new(@user_params)
    if user.save
      NotificationMailer.welcome_email(user).deliver_now
      user
    else
      nil
    end
  end
end

例2: 外部APIのデータ取得


# app/services/weather_service.rb
class WeatherService
  def initialize(location)
    @location = location
  end

  def fetch_weather
    response = RestClient.get("https://api.weather.com/v1/#{@location}")
    JSON.parse(response.body)
  end
end

具体例:Concernsの実装

例1: 共通スコープの共有


# app/models/concerns/publishable.rb
module Publishable
  extend ActiveSupport::Concern

  included do
    scope :published, -> { where(published: true) }
  end
end

class Post < ApplicationRecord
  include Publishable
end

class Article < ApplicationRecord
  include Publishable
end

例2: 共通コールバックの共有


# app/models/concerns/timestampable.rb
module Timestampable
  extend ActiveSupport::Concern

  included do
    before_save :set_timestamps
  end

  private

  def set_timestamps
    self.updated_at = Time.current if self.changed?
  end
end

導入時の注意点

サービスクラスの注意点

  • 単一責任の原則を守る:複数の責任を1つのクラスに詰め込まない。
  • 状態を保持しすぎない:必要なデータは引数として渡し、ステートレスに保つ。
  • 再利用性を意識する:他のプロジェクトや機能でも活用可能な設計にする。

Concernsの注意点

  • Fat Concernsを避ける:モジュールに詰め込みすぎず、分割して管理する。
  • 過度な依存を避ける:モデルに依存しすぎる設計は避ける。

まとめ

サービスクラスとConcernsは、Railsアプリケーションの保守性と可読性を向上させる強力なツールです。それぞれの特徴を理解し、適切に使い分けることで、スケーラブルでメンテナンスしやすいコードを実現できます。本記事の実例をもとに、自身のプロジェクトに最適なアプローチを選択してください。

コメント

タイトルとURLをコピーしました