2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Serviceクラスについて

2
Last updated at Posted at 2026-02-10

初めに

この記事ではServiceクラスとは何かについてまとめます。

Serviceクラスとは

サービスクラスは、複数のモデルにまたがる処理や、特定のビジネスロジックをカプセル化するためのクラスです。

2つの視点からサービスクラスを見る

1. DDD(ドメイン駆動設計)の観点 ※DDDとは2003年にエリック・エヴァンスが提唱した設計アプローチのこと。

DDDにおけるサービスクラスは、エンティティや値オブジェクトに責務を持たせると不自然になる振る舞いを表現するものです。

重要なポイント:
  • ユビキタス言語(業務用語)に由来した命名
  • 「何が実行できるか」という振る舞いに着目
  • エンティティの定義を歪めないための手段

例えば、銀行システムでの送金処理で見ると


class Account < ApplicationRecord
  def transfer_to(destination_account, amount)
    # 送金元の残高チェック
    # 送金元から引き落とし
    # 送金先に入金
    # トランザクション記録
  end
end

Accountモデルに送金処理を書くと不自然。
この場合、「送金」という行為は 2つのアカウント間の関係性 を表すものであり、どちらか一方のAccountに責務を持たせるのは不自然になる。


class MoneyTransferService
  def initialize(source_account, destination_account)
    @source_account = source_account
    @destination_account = destination_account
  end
  
  def transfer(amount)
    ActiveRecord::Base.transaction do
      @source_account.withdraw(amount)
      @destination_account.deposit(amount)
      TransactionLog.create!(
        source: @source_account,
        destination: @destination_account,
        amount: amount
      )
    end
  end
end
2. パーフェクトRailsの観点 ※技術評論社が出したrailsに関する書籍

パーフェクトRailsでは、サービスクラスをコントローラーとモデルの中間に立つものとして位置づけています。

役割分担:
  • コントローラー: HTTPリクエストをハンドリングするインターフェース
  • サービス: モデルが行う処理を取りまとめるインターフェース
  • モデル: データとそのデータに直接関連する振る舞い
重要なのは:
  • 処理そのものをカプセル化する
  • 業務知識と実装のメンタルモデルを一致させる
# app/services/user_registration_service.rb
class UserRegistrationService
  def initialize(user_params)
    @user_params = user_params
  end
  
  def register
    ActiveRecord::Base.transaction do
      user = User.create!(@user_params)
      Profile.create!(user: user)
      UserMailer.welcome_email(user).deliver_later
      user
    end
  rescue ActiveRecord::RecordInvalid => e
    Rails.logger.error("User registration failed: #{e.message}")
    nil
  end
end
# コントローラー
class UsersController < ApplicationController
  def create
    service = UserRegistrationService.new(user_params)
    @user = service.register
    
    if @user
      redirect_to @user, notice: '登録が完了しました'
    else
      render :new
    end
  end
end

サービスクラスを使うべき場合

少人数チームで開発している
  • すぐにコミュニケーションが取れる
  • コードレビューが密に行える
チームの技術水準が高い
  • 対象業務を十分に理解している
  • 読みやすいコードを維持することへの意識が高い
明確な基準がある
  • チーム内でサービスクラスの使い方が統一されている
  • どんな処理をサービスクラスに切り出すか明確

サービスクラスの問題点

基準が作りにくい
  • 「一つのモデルに収まらない複雑な処理を表す振る舞いに名前をつける」

これは基準として曖昧すぎる。

問題として、
  • どこまでをモデルに書き、どこからをサービスクラスに書くべきか不明確
  • 開発者によって判断基準がバラバラになる
  • 結果的にコードの一貫性が失われる

このようなことから、個人開発レベルなら問題ないが、業務で複数人で開発をしていくとなった時にサービスクラスは無闇に使うべきではない。
かといって、Fat Modelのままにするかといったらそういうわけにもいかない。
ここで折衷案として出るのがForm Model

Formモデルとは

Formモデルとは、複雑なフォーム処理をモデルから切り離して管理するためのクラス


通常、Railsでは1つのフォームが1つのモデルに対応しますが、以下のような場合に困ります

複数のモデルを同時に作成・更新したい

例:ユーザー登録と同時にプロフィールも作成

データベースに保存しない一時的なフォーム

例:検索フォーム、お問い合わせフォーム

複雑なバリデーションが必要

例:利用規約への同意チェックなど

Formモデルの実装例

例:ユーザー登録フォーム(ユーザー + プロフィールを同時作成)
# テーブル構成は以下のようになっているとします。
# users テーブル: name, email, password, password_confirmation
# profiles テーブル: user_id, bio

# app/forms/user_registration_form.rb
class UserRegistrationForm
  include ActiveModel::Model
  include ActiveModel::Attributes

  # フォームで受け取る属性
  attribute :name, :string
  attribute :email, :string
  attribute :password, :string
  attribute :password_confirmation, :string
  attribute :bio, :string  # プロフィールの自己紹介

  # バリデーション
  validates :name, presence: true
  validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
  validates :password, presence: true, length: { minimum: 8 }
  validates :password_confirmation, presence: true
  validates :bio, length: { maximum: 500 }

  # 保存処理
  def save
    return false unless valid?

    ActiveRecord::Base.transaction do
      user = User.create!(
        name: name,
        email: email,
        password: password,
        password_confirmation: password_confirmation
      )

      Profile.create!(
        user: user,
        bio: bio
      )
    end

    true
  rescue ActiveRecord::RecordInvalid
    false
  end
end

このように2つのテーブルの内容をuser_registration_form.rbとしてユーザー登録フォームで入力する内容でまとめることができます。


しかし、ここまでFormモデルに関して説明しましたが、これはあくまで入力されたデータの妥当性に関するロジックというかなり限定的な部分のみ肩代わりできるというだけなので、Serviceモデルの代わりになるというわけではありません。

結論

難しい!
個人開発レベルならいいですが、チームで開発するとなると、このようなビジネスロジックはどこに書いたらいいとか、Serviceモデル使うべきかどうか、といったことを決めるのは難しいです。結局ServiceモデルもFormモデルも単なる技法に過ぎません。メンバー同士で、どういう場面で使って、なぜ必要なのかなどを話し合うしかないと思います。
 具体的な解決策を期待していた方申し訳ございません。

終わりに

この記事の中で何か間違っているところなどあれば教えていただけると幸いです。
参考にしたサイト。もっと詳しいことが知りたい場合はこちらへ

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?