В проекте, над которым я работаю, у нас есть модель Address. В нем есть все обычные вещи, которые вы могли бы ожидать:
- Несколько
address_lineполей - Поле
country - Поле
postcode - Некоторые проверки, чтобы гарантировать, что данные, хранящиеся в этих полях, разумны.
class Address < ApplicationRecordbelongs_to :addressable, polymorphic: truevalidates :address_line_1, :postcode, presence: true validates :address_line_1, length: { minimum: 5 } validates :address_line_1, :address_line_2, :address_line_3, :city, length: { maximum: 255 } validates :postcode, ... ...end
Чего еще можно хотеть?
Address относится к другим моделям в нашей области через полиморфную ассоциацию. Для реализации этого у нас есть проблема PhysicalAddressable:
module PhysicalAddressableextend ActiveSupport::Concernincluded do has_one :address, as: :addressable, dependent: :destroy, inverse_of: :addressabledelegate :address_line_1, to: :address delegate :address_line_2, to: :address delegate :address_line_3, to: :address delegate :city, to: :address delegate :postcode, to: :addressdef self.search_by_postcode(postcode) ... end...endend
Включение этой проблемы в модель позволяет ей иметь адресную запись и взаимодействовать с ней.
Хорошо! Все довольно стандартные вещи Railsy, как вы говорите 👍
На этой неделе я внес некоторые изменения в PhysicalAddressable. Эти изменения были совместимы со всеми адресуемыми моделями, кроме одной: DistributionCentre.
В оставшейся части этого поста я расскажу о своем мыслительном процессе для решения этой проблемы и, в конечном итоге, удаления ассоциации адресов с DistributionCentre .
Контекст
Чтобы помочь лучше смоделировать общую структуру, которая существует между различными типами Order, мы недавно представили новую модель для нашего домена: EpisodeOfCare. Там, где раньше у нас было несколько адресных моделей заказов, теперь каждая из этих моделей будет принадлежать эпизоду ухода, а эпизод ухода будет обрабатывать связь с адресом.

Что мне в конечном итоге нужно сделать, так это отключить Address от каждой из адресуемых моделей и вместо этого связать Address с EpisodeOfCare.
Один шаг к этой цели требует изменения PhysicalAddressable таким образом, чтобы каждая из адресуемых моделей передавала вызовы #address на соответствующий эпизод ухода.
delegate :address, to: :episode_of_care
Это сработало, как и планировалось, для всех адресуемых моделей, кроме одной…
Проблема с DistributionCentre
В отличие от других адресных моделей, DistributionCentre не относится к эпизоду ухода. И что же мне делать?
Параметры:
- Предоставьте средства для прямой связи
DistributionCentreсAddress(в обходPhysicalAddressable) - Удалите связь между
DistributionCentreиAddress, вместо этого сохраняя значения адресов непосредственно в центре распределения.
Второй вариант кажется проще; посмотрим, насколько это жизнеспособно.
Нужна ли DistributionCentre собственная адресная запись?
В контексте DistributionCentre нам нужно:
- A (big A) Address — полноценный адресный объект.
- Адрес (маленькое а) — значение адреса.
Чтобы ответить на этот вопрос, я хочу знать следующее:
- Сколько у нас логики, зависящей от адреса распределительного центра?
- Как часто мы делаем запросы к базе данных для
DistributionCentre, которые зависят от адреса?
\Приглушенные звуки скрипа\
Что ж, получается, что ответ на оба вопроса: никто/никогда/мы не делаем.
Фактически:
- Большинство распределительных центров даже не имеют адреса.
- Адреса распределительных центров не взаимодействуют с пользователем (без CRUDing!).
Таким образом, мы не получаем особой выгоды от использования ассоциации записи адреса для DistributionCentre.
- Проверки не особенно полезны, так как мы только создаем адреса центров распространения в начальном задании и никогда их не обновляем.
- Мы никогда не запрашиваем — и даже не заботимся о содержании этих записей на уровне приложения. Таким образом, хранение их в отдельной таблице также не добавляет большой ценности.
В контексте распределительных центров: адрес является необязательным и статическим элементом информации. Он будет время от времени считываться, никогда не запрашиваться и никогда не изменяться.
Зная это, а также поскольку существующая ассоциация теперь мешает необходимому рефакторингу, у нас есть вполне убедительный аргумент в пользу удаления связи между Address и DistributionCentre.
Отключение Address и DistributionCentre
Вот краткое объяснение того, как я это сделал:
Шаг 1.
Мне нужно добавить несколько адресных полей в таблицу центра распределения. Rails предоставляет удобный генератор миграции, который может сделать это в одну строку.
rails generate migration AddAddressFieldsToDistributionCentres address_line_1:string address_line_2:string address_line_3:string city:string postcode:string country:string
Шаг 2.
Далее мне нужно заполнить новые поля адреса центра распределения, скопировав данные из старых адресных записей. Это было сделано с помощью задачи после развертывания:
Address.where(addressable_type: 'DistributionCentre').find_each do |address| distribution_centre = address.addressable address_values = address.slice(*ADDRESS_FIELDS) distribution_centre.update!(address_values) endADDRESS_FIELDS = %i[ address_line_1 address_line_2 address_line_3 city postcode country ].freeze
Я также обновил наш файл seed, чтобы отразить новую схему базы данных.
Шаг 3.
Все записи центров распределения теперь содержат необходимые адресные данные. Затем я проверил, что публичный интерфейс DistributionCentre не сломается после удаления ассоциации адресов (я провел тесты). Это показало, что нам нужен метод для предоставления адресной строки, разделенной запятыми.
class DistributionCentre < ApplicationRecord...def address_string [ address_line_1, address_line_2, address_line_3, city, postcode, country ].compact.reject(&:empty?).join(', ') end...end
Шаг 4.
Наконец, я избавился от ассоциации, удалив проблему PhysicalAddressable из DistributionCentre. Старые записи были удалены с помощью другой задачи после развертывания:
Address.where(addressable_type: 'DistributionCentre').destroy_all
Дело сделано.
Заключительные мысли
Мне приходит в голову, что я мог бы заменить адрес всего одним строковым полем в центре распределения. Сохранение полей отдельно упрощает запрос к ним, если это когда-либо понадобится… но это не то, что нам сейчас нужно делать. Ну, это не имеет особого значения в данный момент.
Наконец, мне кажется интересным, что у большинства распределительных центров нет адреса — это может указывать на то, что в нашем моделировании предметной области в этой области есть возможности для улучшения. В настоящее время это не вызывает каких-либо очевидных проблем, но это может быть чем-то, что следует учитывать в будущем.