При разработке внешних обработок в среде 1С:Предприятие 8.3 на управляемых формах разработчики часто сталкиваются с дилеммой: куда поместить основной программный код, который должен исполняться на сервере? Существует две основные стратегии: использование модуля формы с директивами &НаСервере или вынос логики в модуль объекта. В этой статье мы подробно разберем, какие рекомендации дает вендор, как архитектурный выбор влияет на производительность системы и почему в некоторых случаях модуль формы может стать «ловушкой» для разработчика.
Для начала проанализируем ситуацию с точки зрения доступности контекста. В управляемом приложении модуль формы неразрывно связан с интерфейсом. Когда мы объявляем процедуру с директивой &НаСервере, платформа при вызове этой процедуры передает на сервер весь контекст формы. Это означает, что все значения реквизитов формы (включая многотысячные строки табличных частей) упаковываются в пакет данных, передаются по сети, распаковываются на сервере, а после выполнения кода — передаются обратно.
Модуль объекта внешней обработки ведет себя иначе. Чтобы выполнить метод модуля объекта, нам необходимо создать этот объект на сервере. Рассмотрим основные случаи, когда выбор того или иного модуля становится критичным.
Рассмотрим подробнее причины, по которым профессиональная разработка требует выноса серверной логики в модуль объекта:
ВнешниеОбработки.Создать(). В этом случае доступны только экспортные методы модуля объекта.Многие разработчики, пытаясь совместить удобство модуля формы и мощь модуля объекта, используют метод РеквизитФормыВЗначение("Объект"). Проанализируем ситуацию, описанную в дискуссии: использование этого метода может быть крайне «дорогим» удовольствием.
Выясним причину: при вызове РеквизитФормыВЗначение платформа создает полную копию данных в памяти сервера в виде прикладного объекта. Если вы вызываете этот метод внутри цикла (например, при обработке строк таблицы), производительность упадет в геометрической прогрессии.
Посмотрим на пример неправильного кода:
&НаСервере
Процедура ОбработатьДанныеНаСервере()
// ТАК ДЕЛАТЬ НЕЛЬЗЯ В ЦИКЛЕ
Для Каждого Строка Из Объект.Товары Цикл
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
ОбработкаОбъект.РассчитатьЦену(Строка);
ЗначениеВРеквизитФормы(ОбработкаОбъект, "Объект");
КонецЦикла;
КонецПроцедуры
Вместо этого рекомендуется один раз конвертировать данные в объект, выполнить все действия и один раз вернуть результат в форму, либо вообще передавать только необходимые данные в экспортные методы модуля объекта без полной конвертации.
Автор темы затронул важный вопрос: нужно ли страховаться в модуле объекта инструкциями #Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда?
Разберем ситуацию: внешняя обработка может быть запущена в разных типах клиентов. В Толстом клиенте (управляемое приложение) модуль объекта компилируется на клиенте. Если внутри модуля есть обращения к серверным общим модулям (которые не имеют флага «Вызов сервера»), вы получите ошибку компиляции.
Поэтому хорошим тоном считается обрамление кода в модуле объекта препроцессором, аналогично тому, как это делает вендор в модулях менеджеров типовых конфигураций. Это гарантирует, что код будет успешно скомпилирован вне зависимости от того, в каком режиме запущена обработка.
Иногда возникают ситуации, когда логика все же «застряла» в модуле формы, и ее нужно вызвать из другого серверного контекста. Рассмотрим пример из типовых решений, где форма передается как параметр. Это позволяет использовать экспортные методы формы даже в &НаСервереБезКонтекста.
Проанализируем пример кода, предложенный участниками обсуждения:
&НаКлиенте
Процедура ВыполнитьДействие(Команда)
// Передаем форму как параметр в серверную процедуру
ВыполнитьНаСервереБезКонтекста(ЭтаФорма);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ВыполнитьНаСервереБезКонтекста(ФормаПараметр)
// Мы можем вызвать экспортный серверный метод этой формы
ФормаПараметр.МойЭкспортныйМетодНаСервере();
КонецПроцедуры
&НаСервере
Процедура МойЭкспортныйМетодНаСервере() Экспорт
Сообщить("Метод формы успешно выполнен на сервере!");
КонецПроцедуры
Важное примечание: данный прием работает только внутри серверного контекста одного сеанса. Передать форму с клиента на сервер напрямую через НаСервереБезКонтекста нельзя (возникнет ошибка отображения типов УправляемаяФорма), но можно манипулировать формой, если она уже находится в серверном контексте (например, при передаче из одной серверной процедуры в другую).
Подведем итог и сформулируем правила «хорошего тона» при разработке внешних обработок:
Экспортными. Если логика использует серверные модули — используйте #Если Сервер Тогда.&НаСервере в форме используйте для предварительного заполнения данных при открытии (ПриСозданииНаСервере) или для быстрых внеконтекстных вызовов &НаСервереБезКонтекста, когда нужно получить справочную информацию из базы.РеквизитФормыВЗначение. Попробуйте передать нужную таблицу или структуру данных в экспортный метод модуля объекта как параметр.Таким образом, хотя платформа позволяет «нагородить» тысячи строк серверного кода в модуле формы, это является архитектурной ошибкой. Вынос кода в модуль объекта делает вашу внешнюю обработку профессиональным инструментом, готовым к интеграции, фоновому выполнению и стабильной работе в любых режимах запуска.