Как объединить расширения в 1С без потери данных и стоит ли собирать их в один монолит?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе развития и сопровождения информационных баз на платформе 1С:Предприятие 8.3 разработчики часто сталкиваются с ситуацией, когда количество расширений конфигурации начинает стремительно расти. Со временем этот «зоопарк» расширений становится трудно контролировать: заимствованные объекты пересекаются, обновления типовой конфигурации требуют анализа десятков модулей, а администрирование системы усложняется. У многих возникает естественное желание — объединить все имеющиеся доработки в одно большое монолитное расширение.

В этой статье мы подробно разберем, как устроен механизм хранения данных расширений на уровне СУБД, проанализируем архитектурные риски создания монолитного расширения, выясним правила очередности применения расширений платформой и разработаем безопасный пошаговый алгоритм миграции данных при реструктуризации.

Как расширения хранят данные на уровне SQL: разбираем физическую структуру

Прежде чем приступать к любым манипуляциям с метаданными, нам необходимо четко понимать, как платформа 1С организует хранение информации расширений в таблицах СУБД (Microsoft SQL Server, PostgreSQL и др.). Это знание убережет нас от случайной и безвозвратной потери накопленных пользователями данных.

Платформа разделяет хранение данных основной конфигурации и расширений по следующим правилам:

  1. Собственные (новые) объекты расширения: Если вы создаете внутри расширения абсолютно новый объект (например, справочник ДополнительныеСвойстваНоменклатуры или регистр сведений ИсторияИзмененийЦен), платформа создает для него полноценную отдельную таблицу на уровне СУБД. Имя этой таблицы содержит уникальный внутренний идентификатор расширения.
  2. Заимствованные объекты с новыми реквизитами: Если вы заимствуете типовой справочник (например, Номенклатура) или документ (например, РеализацияТоваровУслуг) и добавляете в него новый реквизит в рамках расширения, платформа не перестраивает основную физическую таблицу этого объекта в СУБД. Вместо этого создается вспомогательная таблица расширения (так называемая таблица расширения реквизитов) вида _ReferenceXX_ExtYY или _DocumentXX_ExtYY, где ExtYY жестко привязано к внутреннему идентификатору конкретного расширения. В этой таблице хранятся значения новых реквизитов, сопоставленные с основной таблицей по уникальному идентификатору записи (_IDRRef).

Критически важный вывод: Поскольку таблицы СУБД для расширений жестко привязаны к их уникальным внутренним идентификаторам в конкретной базе данных, вы не можете просто скопировать метаданные в новое расширение и удалить старые. При удалении старого расширения (или при первой же реструктуризации и операции «Тестирование и исправление») платформа удалит связанные с ним таблицы в СУБД со всеми накопленными данными! Новое расширение создаст для точно таких же реквизитов новые, абсолютно пустые таблицы.

Архитектурный спор: Одно монолитное расширение или «зоопарк» мелких?

Идея объединить все доработки в один монолит кажется привлекательной только на первый взгляд. Проанализируем риски и последствия такого шага, которые выработаны многолетней практикой сообщества разработчиков 1С.

Главный риск монолита — падение всей системы из-за одной мелочи. Представьте, что в вашем едином расширении объединены критически важные алгоритмы расчета себестоимости, интеграция со сторонними сервисами и незначительные доработки интерфейса (например, изменение цвета шрифта в динамическом списке справочника контрагентов). При очередном обновлении типовой конфигурации вендор изменяет или удаляет реквизит, связанный с оформлением этого списка. Результат окажется катастрофическим: при запуске системы все расширение целиком откажется подключаться. Из-за одной неработающей «рюшечки» пользователи полностью лишатся критически важного функционала расчета себестоимости, работы складов и проведения документов.

Минусы избыточного количества мелких расширений («зоопарка»):

Рекомендованный гибридный подход:
Оптимальным решением является группировка расширений по функциональным блокам (подсистемам). Например:

При таком подходе критически важные бизнес-процессы изолированы от интерфейсных изменений, и работоспособность ключевых узлов системы при обновлениях не пострадает.

Очередность применения и порядок работы расширений

Разберем, как платформа определяет приоритеты выполнения доработок при наличии нескольких расширений в системе.

Порядок применения расширений строго регламентирован платформой:

  1. По назначению расширения: В первую очередь платформа применяет расширения с типом назначения Исправление (используется в основном для патчей), затем Адаптация, и в последнюю очередь — Дополнение.
  2. По порядку в списке: Внутри одного и того же назначения очередность определяется расположением расширений в списке конфигуратора (сверху вниз). Расширение, находящееся ниже в списке, накладывается поверх расширений, расположенных выше.
  3. Порядок выполнения перехваченных методов:
    • Аннотация &Перед (&Before) выполняется в обратном порядке (снизу вверх по списку) — код самого нижнего (последнего) расширения выполнится первым.
    • Аннотация &После (&After) выполняется в прямом порядке (сверху вниз по списку).
    • Аннотация &Вместо (&Around): если метод перехвачен этой аннотацией в нескольких расширениях, управление получит только то расширение, которое имеет наивысший приоритет. Начиная с версии платформы 8.3.15, разработчики могут использовать метод ПродолжитьВызов() для передачи управления по цепочке другим расширениям и основной конфигурации, что значительно снизило конфликтность совместной разработки.

Безопасный алгоритм переноса данных при укрупнении расширений

Если вы все же приняли решение объединить несколько расширений в одно (или перегруппировать их по функциональным блокам), рассмотрим по шагам, как выполнить эту процедуру безболезненно и без потери данных.

Шаг 1. Подготовка нового расширения
Создаем новое укрупненное расширение. Переносим в него программный код, формы и заимствованные объекты. При переносе реквизитов, содержащих данные, необходимо создать их под новыми именами или с уникальными префиксами (например, вместо старого реквизита Расш1_ДополнительныйАдрес создаем в новом расширении Общ_ДополнительныйАдрес). Это исключит конфликты имен на этапе, когда в системе будут одновременно активны и старые, и новые расширения.

Шаг 2. Развертывание новой структуры в базе данных
Подключаем новое расширение в базу данных, не отключая старые расширения. В результате в СУБД будут созданы новые физические таблицы для хранения переносимых реквизитов, в то время как старые таблицы со старыми данными по-прежнему будут активны и доступны системе.

Шаг 3. Программный перенос данных (миграция)
Для переноса накопленных данных пишем специальную обработку. Крайне важно осуществлять перенос порциями в транзакциях и использовать свойство ОбменДанными.Загрузка = Истина, чтобы избежать замедления работы из-за проведения лишних проверок и вызова бизнес-логики. Проанализируем пример кода для переноса данных заимствованного справочника Номенклатура:


// Пример обработки для миграции данных между реквизитами разных расширений
Процедура ВыполнитьМиграциюДанных() Экспорт
    
    Запрос = Новый Запрос;
    // Выбираем ссылки, у которых заполнены старые реквизиты расширения
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Номенклатура.Ссылка КАК Ссылка,
        |   Номенклатура.Расш1_ДополнительныйАдрес КАК СтароеЗначение
        |ИЗ
        |   Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |   Номенклатура.Расш1_ДополнительныйАдрес <> """"";
        
    РезультатЗапроса = Запрос.Выполнить();
    ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
    
    КоличествоОбработанных = 0;
    НачатьТранзакцию();
    
    Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
        
        // Получаем объект для записи данных в новый реквизит
        ОбъектНоменклатура = ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект();
        
        // Переносим данные в реквизит нового расширения
        ОбъектНоменклатура.Общ_ДополнительныйАдрес = ВыборкаДетальныеЗаписи.СтароеЗначение;
        
        // Отключаем лишние обработчики событий для ускорения записи
        ОбъектНоменклатура.ОбменДанными.Загрузка = Истина;
        
        Попытка
            ОбъектНоменклатура.Записать();
            КоличествоОбработанных = КоличествоОбработанных + 1;
            
            // Фиксируем транзакцию каждые 1000 элементов для предотвращения переполнения транзакционного лога
            Если КоличествоОбработанных % 1000 = 0 Тогда
                ЗафиксироватьТранзакцию();
                НачатьТранзакцию();
            КонецЕсли;
        Исключение
            ОтменитьТранзакцию();
            ЗаписьЖурналаРегистрации("МиграцияРасширений", УровеньЖурналаРегистрации.Ошибка,,, 
                "Ошибка при записи объекта: " + ВыборкаДетальныеЗаписи.Ссылка + " - " + ОписаниеОшибки());
            НачатьТранзакцию();
        КонецПопытки;
        
    КонецЦикла;
    
    Если ТранзакцияАктивна() Тогда
        ЗафиксироватьТранзакцию();
    КонецЕсли;
    
    ПоказатьПредупреждение(, "Миграция успешно завершена! Обработано объектов: " + КоличествоОбработанных);
    
КонецПроцедуры

Шаг 4. Контроль и проверка данных
Перед отключением старых расширений обязательно убедимся, что перенос выполнен без ошибок. Напишем контрольный запрос, проверяющий, что у всех записей, где было заполнено старое поле, теперь корректно заполнено новое поле, и значения в них совпадают.

Шаг 5. Отключение «зоопарка» расширений
После успешной проверки отключаем и удаляем старые расширения из списка базы данных. На этом этапе таблицы СУБД, принадлежавшие старым расширениям, будут удалены платформой, а новые таблицы укрупненного расширения продолжат функционировать в штатном режиме.

Полезные советы по работе с расширениями в современных версиях платформы

Развитие механизмов расширений в платформе 1С значительно упростило жизнь разработчикам. Обратим внимание на следующие полезные возможности:

← На главную