①:無駄に高度な認証 編
だいたいのwebアプリケーションにおいて、ユーザーに認証、権限というものを付与する機会があるだろう。オーソドックスな実装としてはUserと多対多で紐づくRole
というモデルを作り、そこにUserを加えたりあるいは削除したりするという実装などがあるかもしれない。が、こういうモデル設計はよくアプリケーションが本格的に作られて、実際どういう機能が必要かわかってくる前に作られることが多い。その結果、不必要な機能まで実装してしまって無駄に複雑になったりする。今回はそういうアンチパターンの話。
ソリューション:単純にフラグ変数で良い
要するにモデルにしてしまうからややこしいんだ、という話。Userにadmin, editorなどの役割フラグBooleanカラムを作ってしまう。ActiveRecordが自動でadmin?
などのメソッドを作ってくれるのでさらに使いやすくなる。どんないいコードより、書かれてないコードの方が良い。
roleが多くなってきたら初めてRole
モデルの再採用を検討してもよい。が、has_and_belongs_to_many
なんてなどしないで普通にUser has_many roleでよい(つまり非正規化)。
教訓:
- 必要となるまでは書くな
- いきなり高度なこと(モデルにしたり、中間テーブルで正規化したり)をするな
各Userについて複数もたれるroleを単純にhas_manyにしてしまうのはレコード量が爆発しそうな気がするけど、最初からそんな先のことを考えるな、と言いたいんだと思う。Railsはアジャイルという考えかたに寄り添っているフレームワークなので、最初から完璧なものを作ろうとしない方がいい、ということなのだろう。いきなりdeviseとか使い出すの、やっぱりおかしいのかもしれない。
②:モデル多すぎ問題編
ソリューション: 単なるフィールドに非正規化する
以下のようなモデル達を考える。
class Article < ActiveRecord::Base
belongs_to :state
belongs_to :category
validates :state_id, :presence => true
validates :category_id, :presence => true
end
class State < ActiveRecord::Base
has_many :articles
end
class Category < ActiveRecord::Base
has_many :articles
end
はて、このStateモデルやCategoryモデルは必要だろうか。これでいいのではないか。
class Article < ActiveRecord::Base
STATES = %w(draft review published archived)
CATEGORIES = %w(tips faqs misc)
validates :state, :inclusion => {:in => STATES}
validates :category, :inclusion => {:in => CATEGORIES}
STATES.each do |state|
define_method "#{state}?" do
self.state == state
end
end
CATEGORIES.each do |category|
define_method "#{category}?" do
self.category == category
end
end
end
もちろんモデル化することによってたとえば管理者が手軽にStateを足せるという話はある、かもしれないが、結局プログラマーはその新しいstateをどう扱うかということについて新しいコードを書かないといけない。だったら無駄にモデルを増やすより、Textフィールドにしてしまった方が単純でよい。
結論:その関連、本当にモデルである必要がありますか。
ソリューション:RailsのSerialize機能を使おう
Railsにはserializeという機能がある。カラムとしてはtextで持っておきながら、配列やHash,JSONなどの情報を文字列として持っておける(YAMLで格納されている)。そしてRailsから利用するときにはそのことを意識することなくそのままアクセスできる。
これで永続化しておくべきな、しかし些細な情報にいちいちモデルを作らなくても良くなる。
ただし、YAMLtextとして所持しているので検索性は最悪なのに注意すること。このカラムでレコードをfindするようなことはやめた方がよい。
結論:簡単な関連はserialize機能で簡単に管理できないか考えてみよう。
実はSQLアンチパターンでも触れられている。
パターン4の汎用的な属性テーブルの使用のところ。解決策の一つとしてLOBという名前が付いている。
所感
単なるwebフレームワークではなく「アジャイル開発用」であるという側面をもったフレームワークだということを意識すると、それぞれの機能の使い方とかが見えてきたりするなあ、と思ったりした。最初はなるべく簡単な形に実装する。必要になるまで作らない。そういう精神が根底にあることを認識しておくことも大事かもしれない。巨大アプリケーションを作りたいなら(別にRailsでも不可能ではないと思うけど)あんまり良くない規約なんかもいっぱいあると思う(ActiveRecordパターン自体が最適でないケースも沢山あると思う)のでちゃんと思想を知った上で機能やそもそもの採用の取捨選択ができるようになれればいいな、と思った。