При проектировании интеграционных решений в среде «1С:Предприятие 8» с использованием HTTP-сервисов перед разработчиком неизбежно возникает важный архитектурный вопрос: где именно должна располагаться бизнес-логика обработки запросов? Допустимо ли писать весь код непосредственно в модуле самого HTTP-сервиса, или же правильным подходом является разделение транспортного уровня и бизнес-логики с переносом последней в общие модули (ОМ)?
В этой статье мы подробно проанализируем этот вопрос, рассмотрим ключевые аргументы обеих сторон, разберем архитектурные стандарты разработки на платформе 1С и на наглядных примерах кода покажем, почему вынос бизнес-логики в общие модули является лучшей практикой (Best Practice).
Давайте проанализируем классическую архитектуру современных веб-приложений. В большинстве фреймворков применяется паттерн MVC (Model-View-Controller) или его вариации. В этой концепции модуль HTTP-сервиса выполняет роль контроллера (транспортного уровня). Его зона ответственности крайне ограничена. Он должен только принять сетевой запрос, выполнить его первичный разбор, передать данные дальше и вернуть HTTP-ответ.
В модуле HTTP-сервиса мы должны выполнять исключительно следующие операции:
HTTPСервисОтвет с правильным HTTP-статусом (например: 200 — Успешно, 201 — Создано, 400 — Ошибка клиента, 500 — Ошибка сервера).Всю содержательную работу — поисковые запросы к базе данных, создание или изменение элементов справочников, проведение документов, расчеты — мы должны изолировать в специализированных общих модулях.
Представим ситуацию: мы пишем интеграцию с интернет-магазином для регистрации новых заказов и создания контрагентов. Изначально бизнес-требования предполагают, что создание контрагента происходит только при входящем HTTP-запросе. Казалось бы, зачем создавать лишний общий модуль, если этот код будет вызываться только из одного места?
Однако требования бизнеса меняются очень быстро. Уже через месяц вам может потребоваться:
Здесь мы сталкиваемся с критическим ограничением платформы «1С:Предприятие»: экспортируемые функции модуля HTTP-сервиса невозможно вызвать напрямую из других участков конфигурации встроенным языком. Если вся логика «заперта» внутри модуля HTTP-сервиса, у разработчика останется только один путь — дублирование кода (Copy-Paste), что усложняет сопровождение и приводит к неминуемым ошибкам при обновлении алгоритмов.
Современные стандарты разработки требуют покрытия кода автоматическими тестами. Для этого в экосистеме 1С активно используются фреймворки автоматизации тестирования (например, Vanessa Automation). Давайте выясним, как расположение кода влияет на процесс тестирования.
Если бизнес-логика находится внутри модуля HTTP-сервиса, то для проведения полноценного теста нам потребуется выполнить целый ряд инфраструктурных шагов:
Такой подход сильно усложняет, удорожает и замедляет выполнение тестов, делая интеграцию в процессы CI/CD громоздкой.
Если же вся логика вынесена в функции общего модуля, принимающие на вход простые структуры данных 1С, мы можем протестировать её обычным юнит-тестом внутри 1С за доли секунды. Нам не требуется развертывать веб-сервер и настраивать сеть. Мы просто пишем тест, который передает структуру в функцию общего модуля и проверяет возвращаемое значение или состояние базы данных.
Настройка отладки HTTP-сервисов, особенно на рабочих или серверных базах данных, часто вызывает трудности. Необходимо запускать службу сервера 1С с ключом отладки (-debug), настраивать правила брандмауэра, ловить нужный рабочий процесс сервера.
При использовании схемы с общими модулями разработчик избавляется от этих проблем. Для отладки логики достаточно создать простейшую внешнюю обработку, которая эмулирует входные данные (например, заполняет структуру вручную) и вызывает метод общего модуля в обычном интерактивном сеансе «1С:Предприятие». Стандартная локальная отладка в конфигураторе в этом случае работает быстро и стабильно.
Рассмотрим конкретный пример реализации. Предположим, у нас есть HTTP-сервис для создания документа «Заказ клиента».
Разберем, как выглядит неправильный («толстый») вариант реализации непосредственно в модуле HTTP-сервиса:
// НЕПРАВИЛЬНО: Вся логика зашита в модуле HTTP-сервиса
Функция СоздатьЗаказПокупателяPost(Запрос)
Ответ = Новый HTTPСервисОтвет(200);
ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку();
// Чтение JSON
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТелоЗапроса);
ДанныеЗаказа = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
// Поиск контрагента в БД
Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ДанныеЗаказа.ИНН);
Если Контрагент.Пустая() Тогда
Ответ.КодСостояния = 400;
Ответ.УстановитьТелоИзСтроки("Контрагент с указанным ИНН не найден.");
Возврат Ответ;
КонецЕсли;
// Создание документа
НачатьТранзакцию();
Попытка
НовыйЗаказ = Документы.ЗаказКлиента.СоздатьДокумент();
НовыйЗаказ.Дата = ТекущаяДата();
НовыйЗаказ.Контрагент = Контрагент;
// Заполнение табличной части...
НовыйЗаказ.Записать(РежимЗаписиДокумента.Проведение);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Ответ.КодСостояния = 500;
Ответ.УстановитьТелоИзСтроки(ОписаниеОшибки());
Возврат Ответ;
КонецПопытки;
Ответ.УстановитьТелоИзСтроки("Документ успешно создан: " + Строка(НовыйЗаказ.Ссылка));
Возврат Ответ;
КонецФункции
Теперь разберем, как спроектировать эту же задачу правильно, разделив транспортный уровень и бизнес-логику.
Код модуля HTTP-сервиса (контроллер):
// ПРАВИЛЬНО: Модуль HTTP-сервиса выполняет только роль контроллера
Функция СоздатьЗаказПокупателяPost(Запрос)
Ответ = Новый HTTPСервисОтвет(200);
ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку();
// Десериализация данных
ДанныеЗаказа = СчитатьДанныеИзJSON(ТелоЗапроса);
Если ДанныеЗаказа = Неопределено Тогда
Ответ.КодСостояния = 400;
Ответ.УстановитьТелоИзСтроки("Неверный формат JSON-запроса");
Возврат Ответ;
КонецЕсли;
// Вызов бизнес-логики из общего модуля
РезультатОбработки = ИнтеграцияЗаказыСервер.СоздатьЗаказКлиента(ДанныеЗаказа);
// Формирование HTTP-ответа на основе результата работы общего модуля
Ответ.КодСостояния = РезультатОбработки.КодСтатуса;
Ответ.УстановитьТелоИзСтроки(РезультатОбработки.Сообщение);
Возврат Ответ;
КонецФункции
Функция СчитатьДанныеИзJSON(СтрокаJSON)
Попытка
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(СтрокаJSON);
Данные = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
Возврат Данные;
Исключение
Возврат Неопределено;
КонецПопытки;
КонецФункции
Бизнес-логика в общем модуле ИнтеграцияЗаказыСервер (свойства общего модуля: «Сервер» — Истина, «Вызов сервера» — Ложь, «Внешнее соединение» — Истина):
// Метод общего модуля для создания заказа клиента
Функция СоздатьЗаказКлиента(ДанныеЗаказа) Экспорт
Результат = Новый Структура;
Результат.Вставить("Успешно", Ложь);
Результат.Вставить("КодСтатуса", 500);
Результат.Вставить("Сообщение", "");
Результат.Вставить("Ссылка", Документы.ЗаказКлиента.ПустаяСсылка());
// Валидация ИНН
Если Не ДанныеЗаказа.Свойство("ИНН") Или ПустаяСтрока(ДанныеЗаказа.ИНН) Тогда
Результат.КодСтатуса = 400;
Результат.Сообщение = "Не указан обязательный реквизит ИНН";
Возврат Результат;
КонецЕсли;
// Поиск контрагента
Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ДанныеЗаказа.ИНН);
Если Контрагент.Пустая() Тогда
Результат.КодСтатуса = 400;
Результат.Сообщение = "Контрагент с ИНН " + ДанныеЗаказа.ИНН + " не найден в базе";
Возврат Результат;
КонецЕсли;
// Создание и проведение документа в транзакции
НачатьТранзакцию();
Попытка
НовыйЗаказ = Документы.ЗаказКлиента.СоздатьДокумент();
НовыйЗаказ.Дата = ТекущаяДата();
НовыйЗаказ.Контрагент = Контрагент;
// Дополнительная логика заполнения реквизитов...
НовыйЗаказ.Записать(РежимЗаписиДокумента.Проведение);
ЗафиксироватьТранзакцию();
Результат.Успешно = Истина;
Результат.КодСтатуса = 201; // Created
Результат.Сообщение = "Заказ успешно создан.";
Результат.Ссылка = НовыйЗаказ.Ссылка;
Исключение
ОтменитьТранзакцию();
Результат.КодСтатуса = 500;
Результат.Сообщение = "Ошибка при записи заказа: " + ОписаниеОшибки();
КонецПопытки;
Возврат Результат;
КонецФункции
Важно помнить, что платформа 1С не запускает автоматическую транзакцию при обработке входящего HTTP-запроса (в отличие от SOAP-сервисов, где есть соответствующий флаг «В транзакции»). Любые изменения данных должны быть строго обернуты в явные транзакционные блоки НачатьТранзакцию() / ЗафиксироватьТранзакцию() / ОтменитьТранзакцию(). Реализация транзакций внутри специализированных методов ОМ гарантирует целостность базы данных и позволяет изолировать логику от сетевого контекста.
Кроме того, общие модули позволяют гибко управлять контекстом выполнения (флаги «Сервер», «Внешнее соединение», «Клиент» и т. д.). Это предотвращает появление платформенных ошибок при попытке вызова методов, недоступных в контексте веб-сервиса.
В современных реалиях практически любая типовая конфигурация (ERP, КА, УТ) кастомизируется с помощью расширений (файлы .cfe). Внесение изменений в модули самих HTTP-сервисов через расширения технически сложно и часто приводит к конфликтам при обновлениях платформы или конфигурации.
Гораздо проще и безопаснее перехватывать логику работы интеграции в общем модуле с помощью механизмов расширений (аннотации &Вместо, &Перед, &После). Это позволяет дорабатывать интеграционные сценарии, не затрагивая основной транспортный шлюз конфигурации.
Подводя итоги нашего анализа, можно уверенно утверждать: перенос всей бизнес-логики HTTP-сервисов в общие модули — это не просто дань моде, а обязательный стандарт качественной разработки на платформе «1С:Предприятие 8». Такой подход обеспечивает гибкость, надежность, тестируемость и простоту поддержки ваших интеграционных решений в долгосрочной перспективе.