В чем заключаются различия компиляции и исполнения кода на сервере и клиенте в обычных формах 1С?

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

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

Базовая архитектура обычных форм в толстом клиенте

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

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

Компиляция общих модулей и флаги исполнения

Хотя формы работают на клиенте, бизнес-логику часто выносят в Общие модули. Здесь и кроется основной механизм управления компиляцией. Выясним, как влияют настройки общего модуля на его доступность:

  1. Клиент (толстый клиент): Если установлена эта галочка, код модуля будет скомпилирован на стороне пользователя. В нем будут доступны все интерактивные методы (например, Предупреждение()) и работа с интерфейсом.
  2. Сервер: Код компилируется на стороне сервера (в процессе rphost). Здесь недоступны никакие интерактивные действия, но есть прямой доступ к базе данных и глобальному контексту сервера.
  3. Вызов сервера: Это критически важный флаг. Если он не установлен у серверного модуля, вы просто не сможете вызвать его процедуры из клиентского кода обычной формы. Платформа выдаст ошибку, что метод не обнаружен.

Разберем пример вызова серверного кода из обычной формы. Предположим, у нас есть общий модуль РаботаССервером с установленными флагами «Сервер» и «Вызов сервера»:


// В модуле обычной формы
Процедура КнопкаВыполнитьНажатие(Кнопка)
    Результат = РаботаССервером.ПолучитьДанныеССервера();
    Сообщить(Результат);
КонецПроцедуры

В данном случае происходит явный переход контекста: клиентское приложение приостанавливает работу, упаковывает параметры вызова, передает их на сервер, получает ответ и продолжает выполнение.

Особенности режима «Толстый клиент — управляемое приложение»

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

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

Для корректной работы в таких условиях необходимо активно использовать директивы препроцессора. Рассмотрим правильный подход к написанию кода, который должен понимать, где он находится:


#Если Сервер Тогда
    // Код, который выполнится только на сервере
    УстановитьПривилегированныйРежим(Истина);
#КонецЕсли

#Если Клиент Тогда
    // Код для интерактивных действий
    Сообщить("Действие выполнено!");
#КонецЕсли

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

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

Что такое мутабельность? В терминах 1С — это способность объекта изменяться. Когда мы вызываем серверный метод, платформа должна передать параметры через сетевой протокол. Для этого объекты проходят процесс сериализации (превращения в поток данных). Однако некоторые объекты слишком сложны или тесно связаны с контекстом памяти конкретного приложения, чтобы их можно было легко «переслать».

К мутабельным значениям относятся:

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


// ОШИБОЧНЫЙ ПУТЬ
Процедура ОшибкаПередачи()
    ТЗ = Новый ТаблицаЗначений;
    // ... заполнение ТЗ
    СерверныйМодуль.ОбработатьТаблицу(ТЗ); // Здесь возникнет ошибка
КонецПроцедуры

Как мы можем это исправить? Разберем правильный алгоритм: нужно преобразовать сложные данные в простые (неизменяемые или легко сериализуемые) типы, такие как Массив и Структура.


// ПРАВИЛЬНЫЙ ПУТЬ
Процедура ИсправленнаяПередача()
    ТЗ = Новый ТаблицаЗначений;
    // ... заполнение ТЗ
    
    // Преобразуем таблицу в массив структур
    МассивДанных = Новый Массив;
    Для Каждого СтрокаТЗ Из ТЗ Цикл
        Стр = Новый Структура("Код, Наименование", СтрокаТЗ.Код, СтрокаТЗ.Наименование);
        МассивДанных.Добавить(Стр);
    КонецЦикла;
    
    СерверныйМодуль.ОбработатьДанные(МассивДанных); // Это сработает!
КонецПроцедуры

Неявные вызовы сервера и контекст исполнения

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

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

Итоги и рекомендации

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

  1. Четко разделяйте клиент и сервер. Даже если толстый клиент позволяет «все», старайтесь выносить тяжелые вычисления и работу с базой данных в серверные общие модули. Это подготовит ваш код к возможному переходу на управляемые формы.
  2. Контролируйте типы данных. При вызове серверных методов передавайте только ссылки, примитивные типы (числа, строки, даты), массивы или структуры.
  3. Используйте директивы препроцессора. При работе в режиме «Толстый клиент — управляемое приложение» всегда проверяйте контекст с помощью #Если Сервер Тогда, чтобы избежать ошибок при запуске в тонком клиенте.
  4. Помните про глобальный контекст. Переменные, объявленные в модуле приложения как Экспорт, доступны на клиенте, но не «видны» на сервере. Для обмена данными между контекстами используйте ПараметрыСеанса.

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

← На главную