LoginSignup
2
1

More than 3 years have passed since last update.

【Rails】 RDB の VIEWとモデル継承を使ってMTIを実装する (Scenic 応用編)

Posted at

Railsでモデルを継承する場合、デフォルトではSTI(Single Table Inheritance)を使うことが多いと思います。この時に、同じような振る舞いをするから継承するが、長期的なDBの運用を考えるとテーブルを分けたい場合がしばしばあります。そういった場合のために、MTI(Multi Table Inheritance)というデザインパターンを用いた実装を紹介したいと思います。
また、Scenicを使いサブクラスのテーブルを統合したデータを取得する方法も取り上げます。Scenicについて

STIなどの実装パターンについて
MTIの参考

ゴール

例として以下のような設計になる実装をしてみます。

モデル DB
Account accountsビュー
CorporateAccount corporate_accountsテーブル
PersonalAccount personal_accountsテーブル

実装

account.rb
class Account < ActiveRecord::Base
  self.primary_key = :id
  def read_only?; true; end
end
coporate_account.rb
class CorporateAccount < Account
  self.table_name = 'corporate_accounts'
  def read_only?; false; end
end
personal_account.rb
class PersonalAccount < Account
  self.table_name = 'personal_accounts'
  def read_only?; false; end
end

Migrationは、CorporateAccountPersonalAccount のみ通常どおりテーブルを定義しておきます。
Accout はScenicで以下のようなviewを作成します。

SELECT corporate_accounts.id,
       corporate_accounts.name,
       corporate_accounts.created_at,
       corporate_accounts.updated_at,
       'CorporateAccount'::text AS type
FROM corporate_accounts

UNION ALL

SELECT personal_accounts.id,
       personal_accounts.name,
       personal_accounts.created_at,
       personal_accounts.updated_at,
       'PersonalAccount'::text AS type
FROM personal_accounts

実行

この設計で、CorporateAccountPersonalAccountを通常どおりCRUDします。
Account.allで統合したレコードを取得することができ、それぞれのActiveRecordオブジェクトはサブクラスとしてインスタンス化されるため、それぞれの振る舞いをそのまま使うことができます。Decoratorで表示を分けることも可能です。

おわりに

今回の例では想定しづらいですが、MTIの目的としてレコード数が多くなったときのパフォーマンス改善も見込めますが、統合レコードを取得するケースは劣化するため個別テーブルで取得するケースを優先したい場合には有効な手段になると思います。
統合レコードの取得の劣化は、PostgreSQLのMaterialized Viewを使う方法もあり、より冗長性の高い手法になります。

2
1
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
2
1