Railsでドメインロジックをモデルに書くのは、果たして良い設計なのだろうか?

Railsの”モデル”は責務を持ちすぎでは無いだろうか?

RailsのActive Recordはエンティティ(テーブル)とロジックを1対1に縛って、1枚岩の”モデル”と呼ぶオブジェクトとして扱っている。単純な場合は1対1で、多少の複雑さであればActive Recordは非常に便利なAPIを提供してくれて、開発効率をグンと上げてくれる。

しかし、ある程度の複雑度が出てくるとドメインロジックはエンティティと1対1に対応しなくなるし、それを扱う側(Controllerなど)によってロジックが変わることもある。

単純にFat modelに従えば、どちらか一方のモデルのメソッドとしてロジックを隠蔽する。そのため、複数のモデルの操作をしているにも関わらず、様々なデータ操作の責務を1つのモデルが抱えてしまう。これをControllerやViewで扱う時には、操作するオブジェクトが直感的で無く扱いづらい。

(例えば、複数のエンティティのデータを複合した集計なのに、一方のモデルのメソッドから取ってくる場合、1つのフォームから複数のモデルに対してアクセスする場合。)

かといってControllerに書いたら、DRYではない。

 

この1枚岩になっているエンティティとドメインロジックを、切り離してそれぞれ別のオブジェクトにした方が良いのではないだろうか?

この問題についてずっと考えてきた個人的な結論としては、ModelとControllerの中間層にオブジェクトが介在して、このオブジェクトがビジネスロジックの責務を持つのが良いのではないか?と考えてきた。

ふとSQLアンチパターンを呼んだところ、まさにActive Recordパターンのこの問題について言及している章があった。(24章:マジックビーンズ)SQLアンチパターンで提示されている解決策としても、エンティティ=アクティブレコード, ドメインロジック=ドメインモデルとして分離することを推奨していた。

 

ところで、1枚岩のActive Recordの機能をバラして使おうとする(Active Modelなど)とActive RecordやRailsの恩恵が犠牲になってしまう。(文字通りレールから逸れている)
ここをうまく切り抜けるにはどうしよう…というところ。うまくRailsのAPIを吸収して、ドメインモデルを分離したい。
この辺りに取り組んだ事例などが知りたい…

追記(12/11)

a_suenamiさんから、アドバイス記事をいただきました!

Rails でドメインロジックの実装方法まとめ

他にも参考になる記事があったので紹介します。

肥大化したActiveRecordモデルをリファクタリングする7つの方法(翻訳)