0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

初めてLTをしてきた in Sendai.rb

Last updated at Posted at 2025-08-07

経緯

先月、子育てで仙台に引っ越してから初めてのLT発表をしました。RailsのDelegated Typesについて、実際の使用経験をもとに3つのポイントを整理して発表させていただきました。

初参加でしたが、Sendai.rbの皆さんには温かく迎えていただき、とても良い体験でした。

スライド

スライドの補足

想定する要件

  • 営業支援サービスを作る
  • toB, toC両方を対象にしたい
  • 顧客として個人、法人を対象にする
    • 顧客共通(Customer): 住所(address)
    • 個人(Person): 名(first_name), 姓(last_name)
    • 法人(Corporation): 会社名(company_name), 法人番号(tax_identification_number)
  • 顧客を一括管理、一覧できるようにしてほしい

実装パターンの比較検討

よくある実装として3つのパターンを挙げました

1. STI(Single Table Inheritance)

  • 一つのテーブルに全ての属性を詰め込む
  • pros: 一括操作が可能
  • cons: NULL値の多発、テーブル肥大化

2. テーブル分離(CCI: Concrete Class Inheritance)

  • 法人用、個人用に完全にテーブルを分ける
  • pros: 正規化されている
  • cons: 一括操作が困難、カラム重複

3. Delegated Types

  • 共通項目用のテーブルと固有項目用のテーブルをpolymorphic関連で接続
  • pros: 一括操作可能、正規化、型安全
  • cons: 外部キー制約の設定が困難

Delegated Types理解の2つの文脈

発表で強調したのは、Delegated Typesを理解するには2つの文脈が重要だということです。

文脈1: Polymorphic関連の改良版

従来のpolymorphic関連では以下の問題がありました:

  • eager loadingができない
  • customable_typeのvalidationを自分で書く必要がある
  • 型安全なアクセスができない

Delegated Typesはこれらを解決し、より使いやすい形でpolymorphic関連を提供しています。

文脈2: CTI(Class Table Inheritance)を「継承より委譲」で実装

CTIのテーブル設計とほぼ同じですが、重要な違いがあります:

  • CTI: 親テーブルと子テーブルでIDを共有
  • Delegated Types: 親テーブル側にtype, idカラムを持つ(polymorphic関連)

コード上でも大きな違いがあります:

  • STI/CTI: PersonやCorporationインスタンスを主に扱う
  • Delegated Types: Customerインスタンスを主に扱う

この違いにより、Delegated Typesでは拡張が簡単になります。例えば「見込み客(Prospect)」のような概念が追加されても、継承ツリーを考える必要がありません。

includeよりWrapper(PORO)パターン

ドキュメントでよく見るincludeパターンは、以下の問題があります:

  • include先のクラスが肥大化する
  • メソッド名の衝突リスク

代わりに、Wrapperパターンを推奨しています:

class Customer < ApplicationRecord
  SOURCES = {
    person: Customable::Person,
    corporation: Customable::Corporation
  }

  private

  def source
    @source ||= SOURCES[customable_type.underscore.to_sym].new(customable)
  end
end

このパターンの利点:

  • FatModelを避けられる
  • 名前空間を圧迫しない
  • publicメソッドのみに依存するため安定する

抽象化を貫く重要性

最も重要なのは、親クラスやViewに詳細を漏らさないことです。

❌ 悪い例:

<% if customer.person? %>
  <%= customer.person.first_name %><% else %>
  <%= customer.corporation.company_name %>御中
<% end %>

✅ 良い例:

<!-- 統一インターフェース -->
<%= customer.formal_name %>

<!-- 動的パーシャル選択 -->
<%= render "customers/customables/#{customer.customable_type}", customer: customer %>

中途半端な抽象化は技術的負債に直結するため、強い意志で抽象化を貫くことが重要です。

まとめ

Delegated Typesを効果的に使うための3つのポイント:

  1. 2つの文脈で理解する: Polymorphic関連の改良とCTIの委譲実装
  2. includeよりWrap: 依存を安定させ、FatModelを避ける
  3. 抽象化を貫く: 詳細を漏らさない強い意志
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?