Рассматривали ли вы смешанный модельный подход?
Где вы используете единую таблицу наследования для основных полей уведомлений. Затем выгрузите все уникальные элементы в конкретные таблицы / модели, принадлежащие / связанные с вашими подклассами уведомлений.
Это немного сложнее в настройке, но получается довольно СУХОЙ, когда все классы и таблицы определены. Похоже, довольно эффективный способ хранения вещей. При энергичной загрузке вы не должны вызывать слишком большую нагрузку на базу данных.
Для целей этого примера давайте предположим, что электронные письма не имеют уникальных деталей. Вот как это выглядит.
class Notification < ActiveRecord::Base
# common methods/validations/associations
...
def self.relate_to_details
class_eval <<-EOF
has_one :details, :class_name => "#{self.name}Detail"
accepts_nested_attributes_for :details
default_scope -> { includes(:details) }
EOF
end
end
class SMS < Notification
relate_to_details
# sms specific methods
...
end
class Twitter < Notification
relate_to_details
# twitter specific methods
...
end
class Email < Notification
# email specific methods
...
end
class SMSDetail < ActiveRecord::Base
belongs_to :SMS, :class_name => "SMS"
# sms specific validations
...
end
class TwiterDetail < ActiveRecord::Base
belongs_to :twitter
# twitter specific validations
...
end
Каждая из таблиц подробностей будет содержать идентификатор уведомления и только столбцы, которые соответствуют формам коммуникационных потребностей и не включены в таблицу уведомлений. Хотя это будет означать дополнительный вызов метода для получения информации, специфичной для медиа.
Это приятно знать, но вы думаете, что это необходимо?
Очень мало вещей необходимо с точки зрения дизайна. По мере того, как стоимость процессора и места для хранения снижается, возникают и необходимые концепции проектирования. Я предложил эту схему, потому что она обеспечивает лучшее из ИППП и ИМТ, и устраняет некоторые из их недостатков.
Что касается преимуществ:
Эта схема обеспечивает последовательность ИППП. С таблицами, которые не нужно воссоздавать.
Связанная таблица обходит десятки столбцов, в 75% ваших строк пустых. Вы также получаете легкое создание подкласса. Где вам нужно только создать соответствующую таблицу подробностей, если ваш новый тип не полностью покрыт базовыми полями уведомлений. Он также продолжает перебирать все уведомления.
Из MTI вы получаете экономию хранилища и простоту настройки в соответствии с потребностями класса без необходимости переопределять одни и те же столбцы для каждого нового типа уведомлений. Только уникальные.
Однако эта схема также переносит основной недостаток с ИППП. Таблица заменит 4. Что может привести к замедлению, как только оно станет огромным.
Краткий ответ: нет, такой подход не нужен. Я считаю, что это самый СУХОЙ способ эффективного решения проблемы. В кратчайшие сроки STI - способ сделать это. В долгосрочной перспективе MTI - это путь, но мы говорим о моменте, когда вы нажимаете миллионы уведомлений. Этот подход является хорошей промежуточной точкой, которую легко расширить.
Подробный камень
Я создал драгоценный камень поверх вашего решения: https://github.com/czaks/detailed. Используя его, вы можете упростить свой класс уведомлений до:
class Notification < ActiveRecord::Base
include Detailed
end
Остальное идет прежним путем.
В качестве дополнительного бонуса вы теперь можете получить доступ (читать, писать, связывать) специфичные для подкласса атрибуты напрямую: notification.phone_number
, не прибегая к: notification.details.phone_number
. Вы также можете написать весь код в основных классах и подклассах, оставив модель Details пустой. Вы также сможете выполнять меньше запросов (в приведенном выше примере 4 вместо N + 1) для больших наборов данных, используя Notification.all_with_details
вместо обычного Notification.all
.
Имейте в виду, что в настоящее время этот драгоценный камень не очень хорошо протестирован, хотя он работает в моем сценарии использования.