Можно ли вызывать процедуры из модуля с флагом «Вызов сервера» внутри другого серверного модуля?

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

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

Техническая возможность вызова

Проанализируем техническую сторону вопроса. Платформа 1С не накладывает жесткого запрета на подобные вызовы. Если процедура или функция в модуле с флагом «Вызов сервера» помечена ключевым словом Экспорт, она будет доступна для вызова из любого серверного контекста. Рассмотрим пример кода, который часто вызывает споры:


Процедура ВыполнитьРасчеты(НоменклатураСсылка) Экспорт
    // Вызов из обычного серверного модуля
    РезРасчета = _РасчетНоменклатурыВызовСервера.ВыполнитьРасчет(НоменклатураСсылка);
    // Дальнейшая логика обработки...
КонецПроцедуры

Если в модуле _РасчетНоменклатурыВызовСервера процедура ВыполнитьРасчет объявлена как экспортная, система успешно выполнит этот код. Как отмечают участники обсуждения, ограничений на уровне компиляции здесь нет. Однако важно понимать, почему опытные разработчики называют такой подход «неправильным».

Назначение флага «Вызов сервера»

Выясним причину, по которой существуют рекомендации разделять модули. Флаг «Вызов сервера» (Server Call) предназначен исключительно для одной цели: предоставить программный интерфейс клиентскому приложению для обращения к серверным процедурам.

Рассмотрим стандартную иерархию вызовов:

  1. Тонкий клиент вызывает метод модуля с флагом Вызов сервера.
  2. Модуль «Вызов сервера» выступает в роли «диспетчера» или «шлюза». Его задача — принять данные от клиента, возможно, выполнить минимальную проверку и передать управление в «настоящий» серверный модуль.
  3. Общий серверный модуль (без флага «Вызов сервера») выполняет тяжелые вычисления, работу с базой данных и возвращает результат.

Когда мы вызываем модуль «Вызов сервера» из другого серверного модуля, мы нарушаем эту цепочку, заставляя серверный код обращаться к интерфейсному слою, предназначенному для клиента. Это создает избыточную связанность кода.

Проблемы производительности и метаданных

Проанализируем ситуацию с точки зрения ресурсов системы. Каждый экспортный метод в модуле с флагом «Вызов сервера» регистрируется платформой в специальном реестре методов, доступных для удаленного вызова (маршаллинга). Это увеличивает объем метаданных, которые платформа обрабатывает при инициализации сеанса и передаче контекста между слоями.

Если мы начинаем «складировать» в таких модулях всю серверную логику только потому, что нам удобно вызывать её и с клиента, и с сервера, мы неоправданно раздуваем интерфейсную часть системы. В больших конфигурациях это может привести к замедлению работы интерфейса и увеличению потребления памяти клиентским приложением.

Рекомендуемый стандарт рефакторинга

Если вы обнаружили, что вам необходимо вызвать метод модуля «Вызов сервера» из другого серверного модуля, давайте разберем правильный алгоритм действий по шагам:

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

Посмотрим на пример правильной организации кода. Было:


// Модуль: _РасчетНоменклатурыВызовСервера (Сервер, Вызов сервера)
Функция ПолучитьЦену(Товар) Экспорт
    // Сложный запрос к БД
    Возврат РезультатЗапроса;
КонецФункции

Стало (правильный вариант):


// Модуль: НоменклатураСервер (Только флаг Сервер)
Функция ПолучитьЦену(Товар) Экспорт
    // Сложный запрос к БД здесь
    Возврат РезультатЗапроса;
КонецФункции

// Модуль: _РасчетНоменклатурыВызовСервера (Сервер, Вызов сервера)
Функция ПолучитьЦену(Товар) Экспорт
    // Тонкая обертка для клиента
    Возврат НоменклатураСервер.ПолучитьЦену(Товар);
КонецФункции

В этом случае любой другой серверный модуль будет обращаться напрямую к НоменклатураСервер.ПолучитьЦену(), что соответствует стандартам разработки 1С:Совместимо.

Когда это допустимо?

Как отметили участники обсуждения на форуме, иногда «голоса в голове» или жесткие дедлайны заставляют писать код быстрее. В небольших доработках или внешних обработках вызов ВызовСервера из серверного контекста не приведет к немедленному падению системы. Технически это будет работать, так как контекст исполнения уже находится на сервере, и платформа просто перейдет к выполнению метода в том же сеансе без сетевых задержек.

Однако при проектировании тиражных решений или крупных корпоративных систем следует строго придерживаться разделения ответственности. Модули с флагом «Вызов сервера» должны оставаться максимально «тонкими» и не содержать бизнес-логики, которую хотелось бы повторно использовать внутри сервера.

Заключение

Подведем итог нашему анализу:

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

← На главную