18
19

More than 5 years have passed since last update.

Railsアンチパターン<ドメインモデリング編>①無駄に高度な認証,②モデル多すぎ問題

Posted at

①:無駄に高度な認証 編

だいたいの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パターン自体が最適でないケースも沢山あると思う)のでちゃんと思想を知った上で機能やそもそもの採用の取捨選択ができるようになれればいいな、と思った。

18
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
19