自分がエンジニアバイトをしているところでは週1回Rails勉強会をしています。今回はそのやったところのまとめを残しておきます。
参考書:現場で使える Ruby on Rails 5速習実践ガイド
Modelの役割を確認
Modelは役割分担されたMVCの中でDBとのやりとりとビジネスロジック(アプリケーション特有の処理)を行っています。どういったデータをDB取得するのかといった処理、オブジェクトが持つデータを変更する処理などはModelで行うのが基本です。
Model内のコードを共通化する
共通機能をモジュールで共通化する
モジュールに共通化した処理を定義しておくことで、それぞれのModelに必要な処理だけをモジュールとしてMix-inすることができます。
またRailsではActiveSupport::Concernが提供されており、これをextendしておくとActiveSupport::Concern が提供しているメソッドを使うことができるようになるため、素のRubyでモジュールを作るより楽になります。
単一テーブル継承(STI)をして、親クラス(基底クラスともいう)に共通処理をもたせる
似たクラスが存在する場合本質的に似ている部分を切り出し、それを基底クラスとして継承することで共通化をすることができます。RailsではModel一つにつき、それに対応したテーブルが存在します。STIでは継承関係にある複数のModelを一つのテーブルとして扱います。これは三つのモデルにそれぞれ存在するカラムを一個のテーブルに集約するということです。なお集約しているテーブルにはtypeというカラムを用意し、STI上で所属するModelのクラス名を文字列として格納します。例えばベースとなる親クラスAがあり、Aを継承するBとCがあります。Bには「name」というカラムが存在し、Cには「email」というカラムが存在します。通常ならテーブルBとCがありますがSTIではテーブルAに継承関係も含めて全てのカラムが存在します。BとCのカラムがテーブルAにあり、nameもしくはemailはどちらかはNULLになります。
STIの問題点としては以下があります。
- Rubyでは継承が一回しかできないため、一度継承を使うと他の類似性は継承できない
- 異なるクラスのレコードを一つに集中させるため、データ量が多くなる
- DBのtypeカラムがModel名に依存するため、Model名が変わると変更しないといけない
- クラスのデータ構造の差異が大きいと、データベースの利用効率が悪くなる。データメンテナンスの際に気にすべきこと が増大するなど維持コストが高くなる。
なのでSTIを検討する場合は以下を満たすかを考慮しましょう。
- Model間の関係が「AはBの一種である」
- 各Model間のカラムに大きな差異がないこと
- DB上一つにまとまっていると便利なとき
ちなみに会社の先輩にSTIを複数人開発のときに使う場合はわかっている人同士でやらないと混乱すると言われたので使いづらそうですね。
ApplicationRecordに共通処理をかく
ApplicationRecordはすべてのActiveRecordモデルクラスの基底クラスになっているため、ここに共通処理を書けば全てで使うことができます。ただし全てで使えるため、全てのModelで共通でない処理を加えると問題が発生することもある。
ApplicationRecordとModelの間に別でModelを挟む
全てに共通する必要はないが一部の複数のModelに共通処理をもたせたい場合、ApplicationRecordとの間に別で特定のModelを継承していないModelを作り、継承させる方法です
ApplicationRecord - A(ここ) - B