SlideShare a Scribd company logo
Skinny Controllers,
Skinny Models
M3 Tech Talk #m3dev
Kazuhiro Sera @seratch
RubyKaigi 2013
•  LT しました(rspec-kickstarter)
•  席に戻ったら Gemfile に追加!
•  今日は Refactoring Fat Models with
Patterns という講演の内容とその実践に
ついて紹介します
•  興味を持った方は、動画が公開されている
ので後で観てみてください
https://vimeo.com/68611168
Skinny Controllers, Skinny Models
@brynary
•  Bryan Helmkamp
•  Code Climate 創業者
•  顧客の Ruby コードを解析して品質・セ
キュリティ改善を支援するサービス
•  OSS なら無料、以下は Gistub の例
•  https://codeclimate.com/github/
seratch/gistub/
Code Climate: Rating
Code Climate: Code Smell
Code Climate: Classes
ブログエントリ
•  7 Patterns to Refactor Fat ActiveRecord
Models
•  http://blog.codeclimate.com/blog/
2012/10/17/7-ways-to-decompose-fat-
activerecord-models/
•  さらに改善されたコード例を GitHub で公開
している(RubyKaigi での発表で使用)
•  https://github.com/codeclimate/
refactoring-fat-models
Rails ActiveRecord
•  Pro: シンプルさ
•  Con: DB のテーブルと 1:1 を強いられる
Rails アプリでの失敗例
•  適切にデザインされたオブジェクト指向の
コードはラビオリに例えられる
•  時折、カルゾーネみたいに一カ所に詰め込
んだ Rails コードを見かける・・
Skinny Models
•  Fat Models とよく言われるが Model も
Skinny であるべきなんでは?
Fat Models との戦い
•  Controller が肥大化するよりはマシ?
•  既存の AR(ActiveRecord) model に責
務を持たせていくとどんどん肥大化
•  最悪のケースは神オブジェクトが誕生
•  @brynary は、定番の実装パターンをうま
く Rails の model のリファクタリングに
適用する方法を提案している
アンチパターン
•  ただし、安易に mixin として module に
切り出すのはアンチパターン
•  mixin は形を変えた継承
•  がらくたを突っ込む引き出し(コードを
module に移して include するだけ)
•  たくさん mixin された class だとどんな
オブジェクトか把握するのが大変になる
7 つの実装パターン
•  Value Objects
•  Service Objects
•  Form Objects
•  Query Objects
•  View Objects
•  Policy Objects
•  Decorators
Value Objects
•  おなじみ Value Object
•  immutable な値を保持するクラス
•  保持する値によって比較可能
•  何らかのロジックを持つ attribute(また
はその小さな集合)が適している
•  具体例:PhoneNumber、Money、
Ratingなど
Service Objects
•  複数の model を利用する場合
•  外部のサービス、API と連携する場合
•  複雑なロジックを実装する場合
•  既存の model の主たる責務でない場合
•  Strategy パターンを適用したい場合
•  普通 ActiveModel ではない
Form Objects
•  一つのフォームで複数の AR model を更
新したい場合に有効
•  ActiveModel にする
•  Form の save の内部で複数の AR model
の save を呼び出す
•  標準の accepts_nested_attributes_for
は @brynary 的には非推奨
•  複雑になったら永続化処理を Service に
Query Objects
•  バッチ用 or 複雑な条件の SQL を AR の
scope やクラスメソッドにせず Query
Object として分離
•  初期化時に AR::Relation オブジェクトを
渡すようにすると合成可能になる
•  TrialQuery.new(Account.where(…)).fin
d_each do ¦e¦ … end
View Objects
•  View のためのロジックを AR model に
書かない(エラーメッセージ生成など)
•  AR model を保持するクラスを定義し
render にはこちらを渡すようにする
•  helper に AR model を渡す実装よりも、
リファクタリングが促進される
•  多様な UI に対応する場合にも有効
Policy Objects
•  AR model を参照はするが主たるドメイ
ンではないロジック(分析のための判定
など)を分離する
•  例:メール配信対象?アクティブユーザ?
•  AR model を保持するクラスとして定義
•  他との違い:Policy は read な処理だけ
Service は write も含む、Query は SQL
実行と result set を yield に渡すだけ
Decorators
•  Decorator パターンを適用して callback
地獄を回避する
•  例:save メソッドを数珠つなぎにする
(AR の Order#save で orders テーブル
に insert -> DHW に通知 -> 受付完了
メールを送信)
実践してみた
•  実際にパターンを使って Gistub のリファ
クタリングをしてみた
パターンを実践する前
パターンを実践した後
やったこと
•  使ったパターンは Service Objects だけ
•  単に Controller から Service に切り出し
ただけだと、今度はその新しい Service
のスコアが悪かった(当たり前)
•  複雑度の高すぎるメソッドを名前を付け
られる単位に分割
•  コードの重複を共通化
改善の過程
今日から出来ること
•  盲目的に AR のクラスメソッドにしない
•  AR でないモデルもどんどんつくる
•  Controller に AR model 呼び出しをズラ
ズラ書くより Service にする
•  複雑なフォームは Form を導入する
•  AR に表示用ロジックを持たず View
Object でラップして表示処理に渡す
まとめ
•  既に実践されているものも多いが、パター
ンに名前がついて言語化されることは大事
•  最初はシンプルでよい(パターンのやりす
ぎは over engineering)
•  アプリが育っていく中で規模に応じたリ
ファクタリングは必要になっていく
•  ただし、十分なコードカバレッジは必須
アプリ規模とアーキテクチャ
Q&A
M3 Tech Talk #m3dev
Kazuhiro Sera @seratch
Any Question?

More Related Content

Skinny Controllers, Skinny Models