В процессе развития и сопровождения информационных баз на платформе 1С:Предприятие 8.3 разработчики часто сталкиваются с ситуацией, когда количество расширений конфигурации начинает стремительно расти. Со временем этот «зоопарк» расширений становится трудно контролировать: заимствованные объекты пересекаются, обновления типовой конфигурации требуют анализа десятков модулей, а администрирование системы усложняется. У многих возникает естественное желание — объединить все имеющиеся доработки в одно большое монолитное расширение.
В этой статье мы подробно разберем, как устроен механизм хранения данных расширений на уровне СУБД, проанализируем архитектурные риски создания монолитного расширения, выясним правила очередности применения расширений платформой и разработаем безопасный пошаговый алгоритм миграции данных при реструктуризации.
Прежде чем приступать к любым манипуляциям с метаданными, нам необходимо четко понимать, как платформа 1С организует хранение информации расширений в таблицах СУБД (Microsoft SQL Server, PostgreSQL и др.). Это знание убережет нас от случайной и безвозвратной потери накопленных пользователями данных.
Платформа разделяет хранение данных основной конфигурации и расширений по следующим правилам:
ДополнительныеСвойстваНоменклатуры или регистр сведений ИсторияИзмененийЦен), платформа создает для него полноценную отдельную таблицу на уровне СУБД. Имя этой таблицы содержит уникальный внутренний идентификатор расширения.Номенклатура) или документ (например, РеализацияТоваровУслуг) и добавляете в него новый реквизит в рамках расширения, платформа не перестраивает основную физическую таблицу этого объекта в СУБД. Вместо этого создается вспомогательная таблица расширения (так называемая таблица расширения реквизитов) вида _ReferenceXX_ExtYY или _DocumentXX_ExtYY, где ExtYY жестко привязано к внутреннему идентификатору конкретного расширения. В этой таблице хранятся значения новых реквизитов, сопоставленные с основной таблицей по уникальному идентификатору записи (_IDRRef).Критически важный вывод: Поскольку таблицы СУБД для расширений жестко привязаны к их уникальным внутренним идентификаторам в конкретной базе данных, вы не можете просто скопировать метаданные в новое расширение и удалить старые. При удалении старого расширения (или при первой же реструктуризации и операции «Тестирование и исправление») платформа удалит связанные с ним таблицы в СУБД со всеми накопленными данными! Новое расширение создаст для точно таких же реквизитов новые, абсолютно пустые таблицы.
Идея объединить все доработки в один монолит кажется привлекательной только на первый взгляд. Проанализируем риски и последствия такого шага, которые выработаны многолетней практикой сообщества разработчиков 1С.
Главный риск монолита — падение всей системы из-за одной мелочи. Представьте, что в вашем едином расширении объединены критически важные алгоритмы расчета себестоимости, интеграция со сторонними сервисами и незначительные доработки интерфейса (например, изменение цвета шрифта в динамическом списке справочника контрагентов). При очередном обновлении типовой конфигурации вендор изменяет или удаляет реквизит, связанный с оформлением этого списка. Результат окажется катастрофическим: при запуске системы все расширение целиком откажется подключаться. Из-за одной неработающей «рюшечки» пользователи полностью лишатся критически важного функционала расчета себестоимости, работы складов и проведения документов.
Минусы избыточного количества мелких расширений («зоопарка»):
&Вместо в разных расширениях для одного и того же метода.Рекомендованный гибридный подход:
Оптимальным решением является группировка расширений по функциональным блокам (подсистемам). Например:
Расширение_ИнтеграцияWMS — содержит всю логику обмена данными со складом.Расширение_ПечатныеФормы — агрегирует все кастомные макеты и печатные формы документов.Расширение_Интерфейс — отвечает за мелкие доработки экранных форм, отчетов и списков.При таком подходе критически важные бизнес-процессы изолированы от интерфейсных изменений, и работоспособность ключевых узлов системы при обновлениях не пострадает.
Разберем, как платформа определяет приоритеты выполнения доработок при наличии нескольких расширений в системе.
Порядок применения расширений строго регламентирован платформой:
Исправление (используется в основном для патчей), затем Адаптация, и в последнюю очередь — Дополнение.&Перед (&Before) выполняется в обратном порядке (снизу вверх по списку) — код самого нижнего (последнего) расширения выполнится первым.&После (&After) выполняется в прямом порядке (сверху вниз по списку).&Вместо (&Around): если метод перехвачен этой аннотацией в нескольких расширениях, управление получит только то расширение, которое имеет наивысший приоритет. Начиная с версии платформы 8.3.15, разработчики могут использовать метод ПродолжитьВызов() для передачи управления по цепочке другим расширениям и основной конфигурации, что значительно снизило конфликтность совместной разработки.Если вы все же приняли решение объединить несколько расширений в одно (или перегруппировать их по функциональным блокам), рассмотрим по шагам, как выполнить эту процедуру безболезненно и без потери данных.
Шаг 1. Подготовка нового расширения
Создаем новое укрупненное расширение. Переносим в него программный код, формы и заимствованные объекты. При переносе реквизитов, содержащих данные, необходимо создать их под новыми именами или с уникальными префиксами (например, вместо старого реквизита Расш1_ДополнительныйАдрес создаем в новом расширении Общ_ДополнительныйАдрес). Это исключит конфликты имен на этапе, когда в системе будут одновременно активны и старые, и новые расширения.
Шаг 2. Развертывание новой структуры в базе данных
Подключаем новое расширение в базу данных, не отключая старые расширения. В результате в СУБД будут созданы новые физические таблицы для хранения переносимых реквизитов, в то время как старые таблицы со старыми данными по-прежнему будут активны и доступны системе.
Шаг 3. Программный перенос данных (миграция)
Для переноса накопленных данных пишем специальную обработку. Крайне важно осуществлять перенос порциями в транзакциях и использовать свойство ОбменДанными.Загрузка = Истина, чтобы избежать замедления работы из-за проведения лишних проверок и вызова бизнес-логики. Проанализируем пример кода для переноса данных заимствованного справочника Номенклатура:
// Пример обработки для миграции данных между реквизитами разных расширений
Процедура ВыполнитьМиграциюДанных() Экспорт
Запрос = Новый Запрос;
// Выбираем ссылки, у которых заполнены старые реквизиты расширения
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Расш1_ДополнительныйАдрес КАК СтароеЗначение
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Расш1_ДополнительныйАдрес <> """"";
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
КоличествоОбработанных = 0;
НачатьТранзакцию();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Получаем объект для записи данных в новый реквизит
ОбъектНоменклатура = ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект();
// Переносим данные в реквизит нового расширения
ОбъектНоменклатура.Общ_ДополнительныйАдрес = ВыборкаДетальныеЗаписи.СтароеЗначение;
// Отключаем лишние обработчики событий для ускорения записи
ОбъектНоменклатура.ОбменДанными.Загрузка = Истина;
Попытка
ОбъектНоменклатура.Записать();
КоличествоОбработанных = КоличествоОбработанных + 1;
// Фиксируем транзакцию каждые 1000 элементов для предотвращения переполнения транзакционного лога
Если КоличествоОбработанных % 1000 = 0 Тогда
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации("МиграцияРасширений", УровеньЖурналаРегистрации.Ошибка,,,
"Ошибка при записи объекта: " + ВыборкаДетальныеЗаписи.Ссылка + " - " + ОписаниеОшибки());
НачатьТранзакцию();
КонецПопытки;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ЗафиксироватьТранзакцию();
КонецЕсли;
ПоказатьПредупреждение(, "Миграция успешно завершена! Обработано объектов: " + КоличествоОбработанных);
КонецПроцедуры
Шаг 4. Контроль и проверка данных
Перед отключением старых расширений обязательно убедимся, что перенос выполнен без ошибок. Напишем контрольный запрос, проверяющий, что у всех записей, где было заполнено старое поле, теперь корректно заполнено новое поле, и значения в них совпадают.
Шаг 5. Отключение «зоопарка» расширений
После успешной проверки отключаем и удаляем старые расширения из списка базы данных. На этом этапе таблицы СУБД, принадлежавшие старым расширениям, будут удалены платформой, а новые таблицы укрупненного расширения продолжат функционировать в штатном режиме.
Развитие механизмов расширений в платформе 1С значительно упростило жизнь разработчикам. Обратим внимание на следующие полезные возможности:
&Перед или &Вместо. Создавайте собственные подписки на события внутри расширений и связывайте их с заимствованными объектами конфигурации.