BETA

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

投稿日:2020-08-26
最終更新:2020-08-26

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

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

ゴール

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

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

実装

class Account < ActiveRecord::Base  
  self.primary_key = :id  
  def read_only?; true; end  
end  
class CorporateAccount < Account  
  self.table_name = 'corporate_accounts'  
  def read_only?; false; end  
end  
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を使う方法もあり、より冗長性の高い手法になります。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@samemuraの技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう