/note/tech

今改めてServiceクラスについて考える 〜あるRails開発者の10年〜

要約:

■ 1. 過去の経緯と問題意識

  • パーフェクトRailsでの記述: 2014年発売の『パーフェクトRuby on Rails』において、筆者はServiceクラスをコントローラーとモデルの中間に位置し、モデルの処理をまとめるインターフェースとして定義した。
  • 後の見解の変化: 2016年末に「サービス クラスとか作るのは止めてくれ」という記事を公開し、わずか2年半で記述内容と異なる意見を表明した。
  • 再議論のきっかけ: 2024年の懇親会での議論を機に、Serviceクラスの是非について改めて考察するに至った。
  • Fat Modelの問題: Railsがビジネスの現場で多用されるにつれ、ActiveRecordの特性からくるFat Model(巨大化したモデル)の問題が顕在化し、その解決策としてServiceクラスが言及されるようになった。

■ 2. Serviceクラスの定義と乱用の難しさ

  • DDDにおけるService: エンティティや値オブジェクトに責務を持たせると不自然になる場合に、振る舞いに着目して命名し責務を定義するものであり、命名はユビキタス言語に由来する必要がある。
  • パーフェクトRailsでの役割: 処理そのものをカプセル化し、業務知識と実装のメンタルモデルを一致させる役割があると記述した。
  • 乱用の危険性: 「乱用は危ないから考えてから使う」という基準の度合いが人によってバラバラであり、単純に複雑な処理に名前を付けて良いわけではないという理解が共有されていなかった。
  • チーム開発での課題: チーム開発では共通認識が重要であり、Serviceクラスの基準が不明確だと一貫性が失われ、コードの全体像の把握や保守が困難になる。

■ 3. 現実的な解決策としてのFormクラス

  • Railsの基本的な層: Controller、Model、Viewの基本的なレイヤーを守ることで、共通認識の維持と想像のしやすさというフレームワークの利点が得られる。
  • Formクラスの役割: Serviceクラスと同じく複雑な処理の置き場所だが、Webアプリケーションにおけるパラメーターハンドリングとトランザクションコントロールを扱うという、より限定的な文脈に対処する。
  • Serviceクラスとの違い: Formクラスは画面やユースケース(アクション)と紐づき、ActiveModel/ActiveRecordのインターフェースと親和性が高いという、名前から具体的なイメージが想像できる利点がある。
  • 共通の難しさ: ServiceやFormを利用しても、結局ActiveRecordのメソッドレベルの境界をどこに設定するかという、モデルの整理から逃れられない。

■ 4. 根本的な重要事項とServiceクラスの今後

  • 最も重要なこと: ServiceやFormといった表現技法は重要ではなく、RDBと向き合い、ActiveRecordを使いこなし、ドメインコンテキストの理解を深めることが重要である。
  • ドメインコンテキストの理解: 複雑なシステム設計では、コンテキストマップ(境界づけられたコンテキスト)を作成し、システム内のコンポーネントと相互作用を設計する。
  • モジュラーモノリスの導入: コンテキストの境界を明確にし、依存関係を一方向に保つための現実的な選択肢として、モジュラーモノリスが挙げられる。
  • Serviceクラスの復活: モジュラーモノリスによってコンポーネントの境界が明確になった時、Webインターフェースに限定されないコンポーネントレベルの公開エントリポイントとしてServiceクラス(またはそれに類するもの)の利用は意味を持つ。
  • 継続的な開発の難しさ: Serviceクラスを使う場合は、チームメンバー全員で利用ケースと言語化された必要性を共有することが不可欠である。ソフトウェア設計は、ビジネスの発展を継続的にサポートするために何度も判断を下し、改善し続ける営みである。