今回はRails界隈でもいくつかの議論がある「Serviceクラス」について
一つの考え方を書いていこうと思います
Serviceクラスとは
MVCやDDDにおいて分離させているレイヤーの責務が大きくなった際に活躍の場がある概念です
DDDでは「ドメインサービス」と「アプリケーションサービス」とそれぞれのレイヤーで定義されていますが
MVCにおいても「モデルサービス」「ビューサービス」「コントローラーサービス」みたいな考え方ができますね
ただし、「ビューサービス」ではなく「ビューヘルパー」だったり、別に置き換えられたりします
それ故、世間一般にいうServiceクラスは「ドメイン知識を持った手続きクラス」を指すことが多いです
意義
MVCなど完成された概念になぜServiceクラスが必要なのか?
その答えは肥大化したクラスを整理することにあります
処理系をまとめて、カプセル化し、責務を分けるといったことが意義になりますね
DDDの考え方ではオブジェクトとしてモデル化すると不自然になる場合に
サービスとして宣言される独立したインターフェイスとしてモデルに追加する=Serviceクラスの追加
が有効であるとしています
制約
Serviceクラスは手続きをまとめているだけなので状態を持ちません
イメージすればわかりますが「口座振り込み手続きサービス」が状態を持つのはおかしいですね
状態は「口座」「ユーザ」「窓口」が持つべきで手続きはステートレスである必要があります
注意点
上記の内容だけ聞くと入れたほうがいい概念な気がしてきますね
しかし、導入には注意が必要です
やたらめったら入れると以下のような問題が発生します
- 重要な知識がドメインモデルから漏れ出す
- 設計の柔軟さが失われる
- 再利用性の低いコードが増えやすい
なぜ、そのような問題が発生するのか?
それは以下の理由があります
- Serviceクラスがドメインモデルに近いため
- 状態を持たないように手続きを記述するため
- 個々のモデル手続きをまとめるため
一概には言えませんが、Railsのようなフレームワークを使うのであれば
極力Serviceクラスの出現は抑えるべきと言えます
勘所
以上を踏まえてServiceクラスはいろいろ考慮し、利用しましょう
個人的にServiceクラスを導入する場合は以下の問答をしています
- どうしてもドメインモデルが表現するには苦しい
- ValueObjectの導入で吸収できなさそう
- 状態を持たずに実装できそうな手続きか?
- 他のモデルでは利用しそうにない(再利用可能性が低い)
利用できると強力ですが、理解不十分で使うには恐ろしいServiceクラスの話でした
その他
軽量DDDでもやったほうがいい理由、けどやらない理由
レイヤードアーキテクチャの視点
ValueObjectという考え方
集約で境界を正しく表現する意味