Чёрный пояс 1С. Общий модуль или модуль объекта: где живёт бизнес-логика и три беды через год
Что мы на самом деле сравниваем
Общий модуль и модуль объекта — это не «два места, где можно держать процедуру». Это две модели владения кодом:
- Общий модуль — функциональный контейнер. Процедура живёт отдельно от данных, принимает их параметрами, возвращает результат. Контекст — нулевой; всё, что нужно, вы передали явно. Идеален для чистых вычислений, разделяемых утилит, протоколов работы с внешними системами.
- Модуль объекта — поведенческий контейнер. Логика живёт рядом с данными, контекст «я документ Реализация» доступен бесплатно. Идеален для всего, что не имеет смысла без типа объекта (документ РеализацияТоваровУслуг, заказ клиента и подобные): проведение, проверка корректности, заполнение по шаблону.
Когда коллега говорит «вынеси в общий модуль, чтобы переиспользовать» — он имеет в виду первый сценарий. Когда другой говорит «оставь в модуле объекта» — он защищает второй. Разногласие возникает, когда конкретный кусок логики попадает в серую зону: вроде бы про документ, но и похожий есть у другого документа.
Три беды «всё в общий модуль» через год
| Симптом | Что под капотом | Цена |
|---|---|---|
| Модуль вырос до 4–6 тысяч строк | «Бог-модуль»: ни одна правка не безопасна, потому что неизвестно, кто ещё вызывает | PR-ревью занимает день, регрессионные баги в неочевидных местах |
| Граф вызовов невозможно прочитать | Любая процедура вызывается отовсюду, неявные зависимости между подсистемами | Любой рефакторинг под угрозой; новичок в команде боится трогать модуль |
| Бизнес-логика оторвана от данных | Поведение, скажем, документа РеализацияТоваровУслуг ищется не в его модуле, а в каком-нибудь служебном общем модуле | Новый разработчик три дня ищет, где «по правде» проводится документ |
Где проходит граница
Кандидат на общий модуль
Процедура заслуживает места в общем модуле, если выполняет одно из условий:
- Истинно кросс-функциональна: работает с разными типами объектов или совсем без объектов (форматирование, парсинг, обращение к внешнему API).
- Чистое вычисление: на одном входе всегда тот же выход, без побочных эффектов и без зависимости от состояния объекта.
- Используется минимум из трёх разных мест, и эти места — не один и тот же объект под разными углами.
Кандидат на модуль объекта
В модуле объекта логике место, если справедливо хотя бы одно:
- Без контекста объекта логика не имеет смысла. Например, проверка корректности реквизитов документа знает структуру именно этого документа.
- Логика срабатывает в стандартных обработчиках платформы — ПередЗаписью, ПриЗаписи, ПередПроведением, ОбработкаПроведения. Выносить такое в общий модуль — значит создавать обходной мостик ради формальности.
- Логика влияет на состояние объекта (поля, табличные части), а не просто читает его.
Безопасный приём: тонкий модуль объекта + чистая утилита
// В модуле объекта документа РеализацияТоваровУслуг — тонкий обработчик
// в роли «диспетчера»: знает структуру документа, делегирует расчёт
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Если Не ЗначениеЗаполнено(Контрагент) Тогда
Отказ = Истина;
Возврат;
КонецЕсли;
// Чистая утилита из общего модуля — не знает про документ Реализация
СуммаНДС = РасчётНалогов.ВычислитьНДС(СуммаДокумента, СтавкаНДС);
НДСДокумента = СуммаНДС;
КонецПроцедуры
Обратите внимание: знание «у документа есть реквизит Контрагент, реквизит СтавкаНДС» живёт в модуле объекта. Знание «как считать НДС от суммы» — в общем модуле, и оно одинаково для трёх десятков разных документов. Никто не пересекается, граф вызовов читается, рефакторить можно по частям.
Старший товарищ, у которого больше шрамов: если вы стоите перед выбором и неясно — отложите процедуру в модуль объекта. Перенести её потом в общий модуль, когда станет ясно, что она там нужна, — пять минут работы и одна правка в одном месте вызова. Обратное движение — из перегруженного общего модуля в десяток модулей объектов — две недели и двадцать осторожных PR.
Чек-лист принятия решения
- Эта процедура работает с конкретным типом объекта или нет? Если с конкретным — модуль объекта.
- Она вызывается из обработчика платформы (ПриЗаписи, ПередПроведением и т.п.)? Если да — модуль объекта.
- Она читает состояние или его меняет? Если меняет — модуль объекта.
- Она вызывается из трёх и более разных мест, не связанных одним типом объекта? Если да — общий модуль.
- В её сигнатуре есть параметры «достаточно полные», чтобы не нужно было лезть «обратно» в исходный объект? Если да — кандидат в общий модуль.
- Если процедуру вынести в общий модуль, потребует ли это передавать «весь объект» как параметр? Если да — оставьте её в модуле объекта.
- Когда сомневаетесь — модуль объекта. Перенос «вверх» дешевле обратного.
Типичные ошибки, которые видны через год
- «Вынес в общий модуль на всякий случай». Тот же случай не наступил, процедура осталась одна, контекст пропал, читать её приходится с боковой стороны. Через год кто-то добавит к ней второй вызов «по аналогии», и начнётся накопление.
- «Передаю весь объект в общий модуль». Если в сигнатуре `ОбработатьДокумент(ДокументСсылка)` и внутри процедура читает десять полей — это не общий модуль, это модуль объекта, выгнанный наружу через окно. Решение — вернуть в модуль объекта, или явно разделить параметры (контрагент, дата, сумма, табличная часть).
- «У нас один общий модуль на всё». «ОбщегоНазначения», «Утилиты», «Хелперы» — по 5000 строк каждый. Никто не знает, где что, всё ищется поиском по тексту. Грамотный путь — несколько узких общих модулей с понятными ролями («Налоги», «Конвертация», «Печать»).
- «Если не модуль объекта — точно общий». Забывают про модуль менеджера и про модуль формы. Многие операции по типу объекта (поиск, выборка) логично положить в модуль менеджера, не в общий и не в модуль объекта. Когда выбираете, держите в голове все четыре, не два.
- «Не записали правило в команде». Разные разработчики принимают решение по личному вкусу, через два года в проекте десять разных моделей раскладки. Минимум документ на одной странице с правилом «по умолчанию — модуль объекта, общий модуль только если выполнено A, B или C».
Перейти в каталог решений →