Клиент и сервер

В управляемом приложении 1С код разделён на два мира — клиент и сервер, и понимать, где выполняется каждая строка, так же важно, как понимать, что она делает.

Клиент-серверная архитектура — модель, в которой интерфейс (клиент) работает на устройстве пользователя, а данные и бизнес-логика (сервер) — на центральном компьютере; они обмениваются сообщениями по сети. В 1С к какому «миру» относится процедура, определяет директива компиляции.

Это одна из самых частых причин недоумения у новичков: код написан правильно, а платформа ругается «Обращение к процедуре недопустимо» или «Тип не определён на клиенте». Почти всегда дело в том, что строка выполняется не там, где вы думаете. Разберёмся, как устроено разделение и как им управлять.

Зачем вообще разделять клиент и сервер

Данные предприятия лежат в базе на сервере — там же, где безопаснее и быстрее всего с ними работать. А пользователь сидит за своим компьютером, иногда за сотни километров, и видит только форму. Гонять всю базу к нему по сети немыслимо. Поэтому 1С разделяет роли: сервер достаёт и обрабатывает данные, клиент показывает результат и реагирует на действия пользователя. Такое разделение даёт масштабируемость (один сервер обслуживает сотни клиентов), безопасность (клиент не имеет прямого доступа к базе) и скорость (тяжёлое считается там, где лежат данные).

Три главные директивы

Директива компиляции — это строка, начинающаяся с &, перед объявлением процедуры. Она говорит платформе, в каком контексте компилировать и выполнять код. Основных три.

&НаКлиенте

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

&НаКлиенте
Процедура ПриИзмененииКоличества(Элемент)
    Если СтрокаТовары.Количество < 0 Тогда
        Предупреждение("Количество не может быть отрицательным");
        СтрокаТовары.Количество = 0;
    КонецЕсли;
КонецПроцедуры

&НаСервере

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

&НаСервере
Процедура ЗаполнитьЦеныИзПрайса()
    Запрос = Новый Запрос;
    Запрос.Текст =
        "ВЫБРАТЬ Цена ИЗ РегистрСведений.Цены.СрезПоследних КАК Цены
        |ГДЕ Цены.Номенклатура = &Номенклатура";
    Для Каждого Строка Из Объект.Товары Цикл
        Запрос.УстановитьПараметр("Номенклатура", Строка.Номенклатура);
        Выборка = Запрос.Выполнить().Выбрать();
        Если Выборка.Следующий() Тогда
            Строка.Цена = Выборка.Цена;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

&НаСервереБезКонтекста

&НаСервереБезКонтекста — тоже серверная процедура, но она не получает данные формы. Ей передают только явные параметры, и она возвращает результат. За счёт того, что платформе не нужно пересылать на сервер и обратно весь контекст формы, такой вызов заметно легче и быстрее. Это предпочтительный способ, когда серверу нужно лишь что-то посчитать по входным аргументам.

&НаСервереБезКонтекста
Функция ПолучитьЦену(НоменклатураСсылка)
    Запрос = Новый Запрос;
    Запрос.Текст =
        "ВЫБРАТЬ Цена ИЗ РегистрСведений.Цены.СрезПоследних КАК Цены
        |ГДЕ Цены.Номенклатура = &Номенклатура";
    Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
    Выборка = Запрос.Выполнить().Выбрать();
    Возврат ?(Выборка.Следующий(), Выборка.Цена, 0);
КонецФункции

Правило-ориентир: если серверной процедуре достаточно параметров — делайте её &НаСервереБезКонтекста. Контекст подключайте только когда действительно нужно менять данные формы.

Как это работает: передача данных

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

&НаКлиенте
Процедура ЗаполнитьПоКнопке(Команда)
    // 1. Клиент собирает исходные данные
    // 2. Один вызов сервера — тяжёлая работа там
    ЗаполнитьЦеныИзПрайса();
    // 3. Клиент показывает результат пользователю
    Сообщить("Цены заполнены");
КонецПроцедуры

Отсюда — золотое правило производительности: минимизируйте число серверных вызовов. Плохо вызывать сервер в цикле по сто раз; хорошо — собрать данные и сходить на сервер один раз, отдав ему всю пачку. Именно поэтому &НаСервереБезКонтекста так ценят: он позволяет делать точечные, лёгкие обращения без пересылки лишнего.

Ещё важно, что не всякий объект «переживает» переход через сеть. Простые типы (число, строка, дата, булево, ссылка на объект базы) передаются свободно. А вот, например, открытый Запрос или результат выборки — нет: их создают и используют строго на сервере. Попытка «протащить» серверный объект на клиент и вызывает ошибки «тип не определён на клиенте».

Частые ошибки

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

Итоги

  • В управляемом приложении код разделён на клиент (интерфейс) и сервер (данные и логика); директива компиляции задаёт, где выполняется процедура.
  • &НаКлиенте — интерфейс и реакции пользователя, без прямого доступа к базе.
  • &НаСервере — полный доступ к данным и к контексту формы, без интерфейса.
  • &НаСервереБезКонтекста — быстрый серверный вызов по параметрам, без пересылки данных формы.
  • Обмен клиент↔сервер идёт через серверные вызовы; каждый вызов — это «поход» по сети.
  • Главное правило производительности — как можно меньше серверных вызовов; тяжёлые объекты живут только на сервере.
Проверьте себя
1. Вам нужно выполнить запрос к базе данных из процедуры в модуле формы. Какую директиву компиляции выбрать?
A&НаКлиенте
B&НаСервере или &НаСервереБезКонтекста
CДиректива не важна — запрос работает везде
D&НаКлиенте, но обернуть запрос в Попытку
2. Чем &НаСервереБезКонтекста отличается от &НаСервере и почему её часто предпочитают?
AОна выполняется на клиенте, поэтому быстрее
BОна не получает данные формы, работает только по переданным параметрам — вызов легче, без пересылки контекста
CОна может выводить сообщения пользователю
DРазницы нет, это просто синоним