🛠️

Serviceクラスの肥大化を防ぐためのPORO導入

に公開

株式会社ウェイブで国内向けアニメ配信サービス「AnimeFesta」のエンジニアをしているflat山です

RailsのPORO導入のリファクタリングに至った記録です

はじめに

  • RailsのServiceクラスが増加し、クラスの役割が分かりにくくなった。
  • この状況をなんとかしたくて、PORO(Plain Old Ruby Object)を導入してみた。

PORO導入の背景

  • Serviceクラスは便利だが、責務が曖昧になりやすい。
  • テーブルに紐づかないロジックの整理に悩むことが多かった。
  • ActiveModelを活用し、バリデーションや属性を持つシンプルなRubyオブジェクト(PORO)を試してみた。

実装例

# 従来のServiceクラス
class CsvExportService
  def generate_csv(user, date_range)
    # データ抽出とCSV生成が混在
  end
end

# PORO+ActiveModelによる分離
class CsvExportData
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :user_id, :integer
  attribute :name, :string
  attribute :email, :string
  attribute :order_count, :integer

  validates :user_id, :name, :email, presence: true
end

# 利用例
user_data = CsvExportData.new(user_id: 1, name: "山田", email: "yamada@example.com", order_count: 3)
if user_data.valid?
  # CSV出力処理
end

導入による効果

  • クラスの責務が明確になった。
  • 可読性が上がり、コードの意図が伝わりやすくなった。
  • バリデーションや属性管理がPORO内で完結するので、テストも楽になった。

課題・注意点

  • ActiveModelに不慣れなメンバーには、最初ちょっと学習コストがあった。
  • どこまでPORO化するかは、チームで話し合って決めるのが大事だと感じた。

チームでの反応

  • クラスの役割が明確になり、設計やテストの話がしやすくなった。
  • ActiveModelの使い方に慣れるまでは、サンプル実装を共有してフォローした。

まとめ

  • Serviceクラスの肥大化や責務の曖昧さに悩んでいるなら、POROとActiveModelの導入はおすすめできる方法。
  • まずは新しい機能や小さなリファクタから、気軽に試してみるのが良いと感じた。
  • この記事が、同じような悩みを持つ方のヒントになれば幸いです。

参考

wwwave's Techblog

Discussion