# Создатель (`Creator`)

`Creator` рекомендует поручать создание объекта тому, кто тесно связан с ним по данным, композиции или жизненному циклу.

## Опора на ООП

`Creator` опирается на [инкапсуляцию и низкую связанность](https://v8std.ru/patterns/principles/): объект создается там, где уже есть все данные для корректной инициализации

## Что показывает пример на 1С

- `Documents.SalesOrder` сам создает `PostingContext`.
- Документ уже содержит организацию, склад и строки товаров, поэтому именно он может корректно подготовить контекст проведения.
- Клиент не собирает этот объект вручную из разрозненных полей.

## Пример

#### DataProcessors.Application
```bsl
SalesOrder = Documents.SalesOrder.GetRef("...").GetObject();
PostingContext = SalesOrder.CreatePostingContext();
```

#### Documents.SalesOrder
```bsl
Function CreatePostingContext() Export

    PostingContext = DataProcessors.PostingContext.Create();
    PostingContext.Organization = Organization;
    PostingContext.Warehouse = Warehouse;
    PostingContext.Items = Items.Unload();

    Return PostingContext;

EndFunction
```

#### DataProcessors.PostingContext
```bsl
Var Organization Export;
Var Warehouse Export;
Var Items Export;
```

## Где полезен в 1С

- при создании контекстов проведения, печати, обмена и расчета;
- когда один объект агрегирует данные, нужные для инициализации другого;
- когда хочется убрать внешнюю ручную сборку служебных структур.

## Когда принцип применяют неправильно

- когда создатель ничего не знает о создаваемом объекте и просто "назначен" случайно;
- когда создание требует зависимостей из другой подсистемы;
- когда объект начинает производить слишком много чужих сущностей и превращается в фабрику всего подряд.

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

Принципы ООП

Creator


# Высокая связность (`High Cohesion`)

`High Cohesion` означает, что у модуля, объекта или сервиса должна быть узкая и понятная зона ответственности.

## Опора на ООП

`High Cohesion` опирается на [инкапсуляцию](https://v8std.ru/patterns/principles/): каждая единица кода отвечает за близкие по смыслу задачи, а не за все подряд

## Что показывает пример на 1С

- `SalesOrderTotals` занимается только расчетом итогов.
- В нем нет проверки прав, печати, отправки уведомлений и клиентской логики формы.
- Благодаря этому модуль проще читать, тестировать и переиспользовать.

## Пример

#### DataProcessors.Application
```bsl
TotalAmount = CommonModules.SalesOrderTotals.Calculate(SalesOrder);
```

#### CommonModules.SalesOrderTotals
```bsl
Function Calculate(SalesOrder) Export

    TotalAmount = 0;

    For Each ItemRow In SalesOrder.Items Do
        TotalAmount = TotalAmount + ItemRow.Amount;
    EndDo;

    Return TotalAmount;

EndFunction
```

## Где полезен в 1С

- при проектировании общих модулей и сервисов;
- когда модуль уже начал совмещать расчет, запись, уведомления и UI;
- при разбиении "комбайнов" на отдельные понятные компоненты.

## Когда принцип применяют неправильно

- когда ради высокой связности код дробят до бессмысленно мелких кусочков;
- когда единая операция искусственно разрывается между множеством модулей;
- когда за "чистотой" теряется целостность use case.

High Cohesion означает, что у модуля, объекта или сервиса должна быть узкая и понятная зона ответственности.

Принципы ООП

High Cohesion


`GRASP` — это набор принципов, которые помогают распределять ответственность между объектами и модулями.

В отличие от `GOF`, это не каталог готовых шаблонов, а набор вопросов: кто должен знать, кто должен создавать, кто должен координировать и как не связать систему в узел.

## Принципы GRASP

| Принцип | Описание | На что смотреть |
| --- | --- | --- |
| [Информационный эксперт](https://v8std.ru/patterns/grasp/information-expert/) (`Information Expert`) | Ответственность получает тот, у кого уже есть нужные данные для ее выполнения. | Не тянуть логику в случайный общий модуль, если нужный контекст уже рядом. |
| [Создатель](https://v8std.ru/patterns/grasp/creator/) (`Creator`) | Объект создает тот, кто тесно связан с создаваемым объектом или владеет его данными. | Кто агрегирует, хранит или инициализирует создаваемую сущность. |
| [Контроллер](https://v8std.ru/patterns/grasp/controller/) (`Controller`) | Внешние запросы принимает объект-координатор сценария, а не низкоуровневые участники. | Кто управляет use case целиком и не смешивает orchestration с деталями. |
| [Низкая связанность](https://v8std.ru/patterns/grasp/low-coupling/) (`Low Coupling`) | Зависимостей между частями системы должно быть как можно меньше. | Насколько дорого менять один модуль из-за устройства другого. |
| [Высокая связность](https://v8std.ru/patterns/grasp/high-cohesion/) (`High Cohesion`) | Каждый модуль должен иметь узкую и понятную зону ответственности. | Не превращается ли модуль в склад разнородной логики. |
| [Полиморфизм](https://v8std.ru/patterns/grasp/polymorphism/) (`Polymorphism`) | Поведение по вариантам лучше раскладывать по реализациям, а не по длинным веткам условий. | Можно ли заменить `Если... ИначеЕсли...` на общий контракт и набор реализаций. |
| [Чистая выдумка](https://v8std.ru/patterns/grasp/pure-fabrication/) (`Pure Fabrication`) | Иногда стоит ввести отдельный служебный объект, если это уменьшает связанность и упрощает модель. | Где вспомогательный сервис полезнее, чем перегрузка доменных объектов чужой логикой. |
| [Посредничество](https://v8std.ru/patterns/grasp/indirection/) (`Indirection`) | Между подсистемами вводят промежуточный слой, чтобы ослабить прямую зависимость. | Нужен ли адаптер, фасад, шина или сервис-обертка для развязки модулей. |
| [Защищенные вариации](https://v8std.ru/patterns/grasp/protected-variations/) (`Protected Variations`) | Точки возможных изменений закрывают стабильным интерфейсом. | Где стоит отделить контракт от реализации, чтобы изменения не разносились по всей системе. |

GRASP — это набор принципов, которые помогают распределять ответственность между объектами и модулями.

Controller

Creator

High Cohesion

Indirection

Information Expert

Low Coupling

Polymorphism

Protected Variations

Pure Fabrication

GRASP


# Посредничество (`Indirection`)

`Indirection` предлагает ввести промежуточный слой между подсистемами, чтобы ослабить прямую зависимость.

## Опора на ООП

`Indirection` опирается на [интерфейсы, инкапсуляцию и низкую связанность](https://v8std.ru/patterns/principles/): вместо прямой связи два участника работают через промежуточный объект или слой

## Что показывает пример на 1С

- прикладной код работает с `NotificationGateway`.
- шлюз уже сам решает, какой отправитель использовать: email, Telegram или другой канал.
- доменный код не зависит напрямую от конкретного канала доставки.

## Пример

#### DataProcessors.Application
```bsl
CommonModules.NotificationGateway.NotifyManager(SalesOrder);
```

#### CommonModules.NotificationGateway
```bsl
Procedure NotifyManager(SalesOrder) Export

    Sender = CommonModules.TelegramSender;
    Sender.Send(SalesOrder.Manager, SalesOrder.Number);

EndProcedure
```

#### CommonModules.TelegramSender
```bsl
Procedure Send(User, MessageText) Export

    // Telegram integration.

EndProcedure
```

## Близость к Adapter, Proxy и Decorator

`Indirection` близок к [Adapter](https://v8std.ru/patterns/gof/adapter/), [Proxy](https://v8std.ru/patterns/gof/proxy/) и [Decorator](https://v8std.ru/patterns/gof/decorator/), потому что во всех четырех случаях между клиентом и реальной реализацией появляется промежуточный слой

Снаружи это действительно похоже:

- клиент не идет напрямую к конечному объекту;
- появляется дополнительный объект или модуль между слоями;
- часть зависимости скрывается за более удобной точкой входа.

Но у `Indirection` намерение шире и архитектурнее.

- `Indirection` нужен, чтобы развязать две части системы через промежуточный слой.
- `Adapter` нужен, чтобы перевести один контракт в другой.
- `Proxy` нужен, чтобы контролировать доступ, создание или способ обращения к объекту.
- `Decorator` нужен, чтобы нарастить поведение поверх существующего компонента.

Для 1С это удобно различать так:

- `NotificationGateway` между прикладным кодом и каналами доставки - это `Indirection`;
- обертка над старым общим модулем с неудобным API - это `Adapter`;
- модуль, который проверяет права или лениво создает тяжелый сервис - это `Proxy`;
- слой логирования, ретраев или метрик вокруг отправителя - это `Decorator`.

## Где полезен в 1С

- между прикладной логикой и внешними интеграциями;
- при переходе на новую подсистему без массовой правки клиентов;
- когда нужна точка развязки между двумя нестабильными частями системы.

## Когда принцип применяют неправильно

- если промежуточный слой ничего не упрощает;
- если из-за лишней прослойки архитектура становится мутнее;
- если `Indirection` превращается в бесконечную матрешку сервисов.

Indirection предлагает ввести промежуточный слой между подсистемами, чтобы ослабить прямую зависимость.

Adapter

Decorator

Proxy

Принципы ООП

Indirection


# Информационный эксперт (`Information Expert`)

`Information Expert` говорит: ответственность стоит отдавать тому объекту, у которого уже есть нужные данные для ее выполнения.

## Опора на ООП

`Information Expert` опирается на [инкапсуляцию и высокую связность](https://v8std.ru/patterns/principles/): логика живет рядом с данными, для которых она естественна

## Что показывает пример на 1С

- `Documents.SalesOrder` сам считает итог по своей табличной части.
- Клиентский код не вытаскивает строки документа наружу и не пересчитывает сумму во внешнем модуле.
- Ответственность остается у объекта, который и так владеет нужными данными.

## Пример

#### DataProcessors.Application
```bsl
SalesOrder = Documents.SalesOrder.GetRef("...").GetObject();
TotalAmount = SalesOrder.GetTotalAmount();
```

#### Documents.SalesOrder
```bsl
Function GetTotalAmount() Export

    TotalAmount = 0;

    For Each ItemRow In Items Do
        TotalAmount = TotalAmount + ItemRow.Amount;
    EndDo;

    Return TotalAmount;

EndFunction
```

## Где полезен в 1С

- для расчетов по данным документа, формы или регистра;
- для проверок, которые напрямую опираются на внутреннее состояние объекта;
- когда внешний модуль начинает тянуть слишком много чужих данных только ради одной операции.

## Когда принцип применяют неправильно

- когда в объект заталкивают логику, требующую много внешних зависимостей;
- когда данные распределены по нескольким подсистемам и одного "эксперта" просто нет;
- когда ради формального следования принципу объект превращается в перегруженный модуль.

Information Expert говорит: ответственность стоит отдавать тому объекту, у которого уже есть нужные данные для ее выполнения.

Принципы ООП

Information Expert


# Низкая связанность (`Low Coupling`)

`Low Coupling` требует уменьшать количество и жесткость зависимостей между частями системы.

## Опора на ООП

`Low Coupling` опирается на [интерфейсы, композицию и инкапсуляцию](https://v8std.ru/patterns/principles/): модуль зависит от контракта, а не от устройства конкретной реализации

## Что показывает пример на 1С

- `PaymentService` зависит от абстрактного шлюза оплаты.
- Конкретный `BankGateway` подставляется снаружи.
- Сервис не вшивает внутрь себя детали конкретного банка или API.

## Пример

#### DataProcessors.Application
```bsl
PaymentService = DataProcessors.PaymentService.Create();
PaymentService.SetGateway(CommonModules.BankGateway);

PaymentService.Pay(InvoiceRef, 1000);
```

#### DataProcessors.PaymentService
```bsl
Var Gateway;

#Region Public

Procedure SetGateway(Gateway_) Export

    Gateway = Gateway_;

EndProcedure

Procedure Pay(InvoiceRef, Amount) Export

    Gateway.Pay(InvoiceRef, Amount);

EndProcedure

#EndRegion
```

#### CommonModules.BankGateway
```bsl
Procedure Pay(InvoiceRef, Amount) Export

    // Bank-specific integration.

EndProcedure
```

## Где полезен в 1С

- при интеграции с банками, API, внешними сервисами и обменами;
- когда реализация инфраструктуры может меняться;
- когда модуль хочется тестировать и заменять без каскадных правок.

## Когда принцип применяют неправильно

- когда интерфейсы вводятся "на всякий случай" без реальной точки изменения;
- когда низкая связанность покупается ценой чрезмерной фрагментации;
- когда ради отвязки скрывается важный смысл предметной области.

Low Coupling требует уменьшать количество и жесткость зависимостей между частями системы.

Принципы ООП

Low Coupling


# Полиморфизм (`Polymorphism`)

`Polymorphism` в `GRASP` говорит: если поведение зависит от варианта, лучше распределить его по реализациям, а не держать в длинной цепочке условий.

Важно: это не то же самое, что [полиморфизм как принцип ООП](https://v8std.ru/patterns/principles/). В `GRASP` это уже архитектурное правило распределения ответственности, которое построено на полиморфизме как механизме языка и объектной модели

## Опора на ООП

`Polymorphism` напрямую опирается на [полиморфизм и интерфейсы](https://v8std.ru/patterns/principles/): разные реализации отвечают на один и тот же запрос по-своему

## Что показывает пример на 1С

- `DiscountCalculator` работает с политикой скидки через общий контракт.
- `RetailDiscountPolicy` и `WholesaleDiscountPolicy` реализуют разные правила.
- Контекст не содержит длинного `Если ... ИначеЕсли` по типу клиента.

## Пример

#### DataProcessors.Application
```bsl
Calculator = DataProcessors.DiscountCalculator.Create();

If Counterparty.IsWholesale Then
    Calculator.SetPolicy(DataProcessors.WholesaleDiscountPolicy.Create());
Else
    Calculator.SetPolicy(DataProcessors.RetailDiscountPolicy.Create());
EndIf;

DiscountAmount = Calculator.Calculate(SalesOrder);
```

#### DataProcessors.DiscountCalculator
```bsl
Var Policy;

#Region Public

Procedure SetPolicy(Policy_) Export

    Policy = Policy_;

EndProcedure

Function Calculate(SalesOrder) Export

    Return Policy.Calculate(SalesOrder);

EndFunction

#EndRegion
```

#### DataProcessors.RetailDiscountPolicy
```bsl
Function Calculate(SalesOrder) Export

    Return SalesOrder.TotalAmount * 0.03;

EndFunction
```

#### DataProcessors.WholesaleDiscountPolicy
```bsl
Function Calculate(SalesOrder) Export

    If SalesOrder.TotalAmount >= 100000 Then
        Return SalesOrder.TotalAmount * 0.10;
    EndIf;

    Return SalesOrder.TotalAmount * 0.05;

EndFunction
```

## Где полезен в 1С

- для разных правил расчета, обмена, печати и маршрутизации;
- когда ветвления по типам уже начинают дублироваться;
- как принцип, на котором потом строятся `Strategy`, `State` и похожие паттерны.

## Когда принцип применяют неправильно

- если вариантов один-два и они почти не меняются;
- если полиморфизм вводится без ясного общего контракта;
- если внешняя система все равно требует большого условного диспетчера.

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

Принципы ООП

Polymorphism


# Защищенные вариации (`Protected Variations`)

`Protected Variations` рекомендует изолировать точки изменений стабильным контрактом, чтобы изменения не расходились по всей системе.

## Опора на ООП

`Protected Variations` опирается на [интерфейсы, инкапсуляцию и полиморфизм](https://v8std.ru/patterns/principles/): внешние клиенты знают стабильный контракт, а изменяющиеся реализации прячутся за ним

## Что показывает пример на 1С

- `CurrencyRateService` зависит от контракта поставщика курсов.
- конкретный `ExternalRateProvider` можно заменить без переписывания сервиса.
- нестабильная часть системы изолирована в одном месте.

## Пример

#### DataProcessors.Application
```bsl
RateService = DataProcessors.CurrencyRateService.Create();
RateService.SetProvider(CommonModules.ExternalRateProvider);

Rate = RateService.GetRate("USD", CurrentDate());
```

#### DataProcessors.CurrencyRateService
```bsl
Var Provider;

#Region Public

Procedure SetProvider(Provider_) Export

    Provider = Provider_;

EndProcedure

Function GetRate(CurrencyCode, RateDate) Export

    Return Provider.GetRate(CurrencyCode, RateDate);

EndFunction

#EndRegion
```

#### CommonModules.ExternalRateProvider
```bsl
Function GetRate(CurrencyCode, RateDate) Export

    Return 100;

EndFunction
```

## Частый вариант в 1С

`Protected Variations` в 1С часто выражается не только через интерфейс или модуль-обертку, но и через справочники и регистры настроек, в которых варианты поведения задаются параметрами.

Идея такая:

- возможные способы работы алгоритма заранее описываются как допустимые варианты;
- прикладной код не переписывают под каждый новый сценарий, а читает параметры выбранного варианта;
- точка изменения переносится из кода в управляемую настройку.

Хороший пример - учетная политика.

В ней бизнес заранее фиксирует допустимые варианты расчета и отражения операций:

- способ оценки запасов;
- порядок признания расходов;
- варианты распределения затрат;
- правила налогового учета.

Тогда прикладной алгоритм работает не с россыпью `Если ... ИначеЕсли ...` по всей системе, а с параметризуемым элементом настройки:

```bsl
Policy = AccountingPolicyService.GetPolicy(Organization, Period);

If Policy.InventoryWriteOffMethod = Enums.InventoryWriteOffMethods.FIFO Then
    Return CalculateFifoCost(DocumentItems);
EndIf;

If Policy.InventoryWriteOffMethod = Enums.InventoryWriteOffMethods.AverageCost Then
    Return CalculateAverageCost(DocumentItems);
EndIf;
```

Здесь защищенной вариацией становится не только контракт сервиса `GetPolicy(...)`, но и сам набор допустимых настроек: система знает, какие варианты разрешены, и новые изменения старается вносить через расширение параметров, а не через хаотичную перепись всех клиентов.

## Где полезен в 1С

- для внешних API, платежных систем, обменов и провайдеров данных;
- для учетной политики, справочников настроек и параметризуемых алгоритмов;
- в зонах, где ожидаются смена реализации или разные поставщики;
- как архитектурная страховка от каскадных изменений.

## Когда принцип применяют неправильно

- если защищать просто нечего и точка изменения не просматривается;
- если ради гипотетической гибкости плодятся лишние абстракции;
- если стабильный контракт сам оказывается нестабильным и часто меняется.

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

Принципы ООП

Protected Variations


# Чистая выдумка (`Pure Fabrication`)

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

## Опора на ООП

`Pure Fabrication` опирается на [низкую связанность и высокую связность](https://v8std.ru/patterns/principles/): иногда лучше создать специальный сервис, чем заставлять доменный объект делать неестественную для него работу

## Что показывает пример на 1С

- сериализация документа для обмена вынесена в `ExchangePayloadSerializer`.
- логика подготовки JSON не попадает в объект документа.
- доменная модель и интеграционная логика остаются разделены.

## Пример

#### DataProcessors.Application
```bsl
Payload = CommonModules.ExchangePayloadSerializer.Serialize(SalesOrder);
```

#### CommonModules.ExchangePayloadSerializer
```bsl
Function Serialize(SalesOrder) Export

    Payload = New Structure;
    Payload.Insert("Number", SalesOrder.Number);
    Payload.Insert("Date", SalesOrder.Date);
    Payload.Insert("Partner", SalesOrder.Partner);

    Return WriteJSON(Payload);

EndFunction
```

## Где полезен в 1С

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

## Когда принцип применяют неправильно

- если под видом `Pure Fabrication` создают случайные "утилиты на все случаи";
- если служебный сервис становится новой свалкой логики;
- если вынесенная обязанность на самом деле естественно принадлежала доменному объекту.

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

Принципы ООП

Pure Fabrication


Этот раздел собирает классические паттерны проектирования, которые можно аккуратно приземлять на 1С-разработку.

Сейчас здесь лежит базовый каркас:

- базовые принципы объектного подхода;
- `SOLID` и инженерные принципы проектирования;
- краткие определения паттернов и отдельные практические страницы;
- разбивка на `GOF` и `GRASP`;
- опорные формулировки, от которых можно дальше переходить к 1С-примерам и ограничениям платформы.

Дальше в этот раздел можно постепенно добавлять:

- примеры реализации на встроенном языке и в конфигурации;
- антипримеры, когда паттерн только усложняет решение;
- привязку к стандартам разработки и типовым сценариям сопровождения.

## Что читать

| Раздел | Что внутри | Зачем нужен |
| --- | --- | --- |
| [Принципы ООП](https://v8std.ru/patterns/principles/) | Полиморфизм, инкапсуляция и наследование | Чтобы сначала выровнять базовый язык проектирования |
| [SOLID](https://v8std.ru/patterns/solid/) | Пять принципов устойчивой объектной архитектуры | Чтобы проверять, не расползается ли архитектура по обязанностям, контрактам и зависимостям |
| [GOF](https://v8std.ru/patterns/gof/) | 23 классических паттерна и практические разборы по части из них | Чтобы говорить о типовых решениях на одном языке и сразу видеть 1С-приземление |
| [GRASP](https://v8std.ru/patterns/grasp/) | Принципы распределения ответственности | Чтобы понимать, куда класть логику и как не раздувать связанность |
| [Инженерные принципы](https://v8std.ru/patterns/engineering/) | DRY, KISS, YAGNI и соседние правила | Чтобы не переусложнять код и архитектуру без причины |

## Как использовать раздел

1. Сначала пройти базовые принципы и выровнять термины.
2. Потом понять намерение паттерна, а не копировать форму.
3. Затем проверить, решает ли он реальную проблему в 1С-коде или архитектуре конфигурации.
4. Только после этого подбирать конкретную реализацию под ограничения платформы, модулей, форм и клиент-серверного разделения.

Инженерные принципы

GOF

GRASP

Принципы ООП

SOLID

Паттерны проектирования


Базовые идеи объектного подхода. Они дают язык, на котором потом проще обсуждать архитектурные решения.

Для 1С это особенно важно: часть архитектурных понятий закреплена не отдельными конструкциями языка, а стандартами разработки. Например:

- [#std551: Разработка конфигураций с повторным использованием общего кода и объектов метаданных](https://v8std.ru/std/551/) и [#std455: Структура модуля](https://v8std.ru/std/455/) формализуют понятия программного интерфейса, служебного программного интерфейса и внутренней реализации
- [#std453: Описание процедур и функций](https://v8std.ru/std/453/) требует документировать методы программного интерфейса
- [#std644: Обеспечение совместимости библиотек](https://v8std.ru/std/644/) рассматривает API как контракт совместимости
- [#std678: Безопасность прикладного программного интерфейса сервера](https://v8std.ru/std/678/) показывает, что серверный интерфейс — это еще и граница безопасности

## Интерфейсы

Интерфейс — это контракт, который описывает, **какие операции доступны снаружи**, но не раскрывает, **как именно** они реализованы внутри.

Практический смысл:

- вызывающий код зависит от набора возможностей, а не от устройства реализации;
- разные реализации можно подменять без переписывания всего клиентского кода;
- границы между подсистемами становятся более явными и проверяемыми.

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

- документированный набор экспортных методов;
- согласованный контракт между модулями;
- общий набор методов у разных объектов платформы.

В 1С понятие интерфейса дополнительно формализуют стандарты:

- [#std551](https://v8std.ru/std/551/) разделяет код на `Программный интерфейс`, `Служебный программный интерфейс` и служебную реализацию
- [#std455](https://v8std.ru/std/455/) закрепляет это разделение в структуре модуля и шаблонах областей
- [#std453](https://v8std.ru/std/453/) требует документировать методы программного интерфейса
- и [#std544: Ограничения на использование экспортных процедур и функций](https://v8std.ru/std/544/) объясняют, где интерфейс не должен жить: в формах и командах
- [#std644](https://v8std.ru/std/644/) требует не ломать API библиотек и не смешивать контракт с деталями реализации

```bsl
Процедура СохранитьОбъект(Объект)
    // Для этой процедуры интерфейс объекта - обладание методом Записать().
    Объект.Записать();
КонецПроцедуры
```

Для вызывающего кода интерфейс здесь очень простой: переданный объект должен поддерживать метод `Записать()`.

Это важно для 1С по двум причинам:

- контракт часто не зафиксирован синтаксисом языка и держится на договоренности;
- поэтому интерфейс нужно явно документировать, проверять и не размывать случайными зависимостями.

На что смотреть в 1С:

- понятен ли публичный API общего модуля или объекта;
- не зависит ли вызывающий код от лишних внутренних деталей;
- можно ли заменить реализацию, не переписывая всех потребителей.

## Клиент как роль в паттернах

В описаниях ООП и паттернов слово `Client` обычно означает не сетевого клиента и не место выполнения кода, а **код-потребитель интерфейса**.

То есть `Client` в паттернах:

- создает объект сам или получает его через фабрику, параметр или внедрение зависимости;
- вызывает методы через контракт, а не через внутренние детали реализации;
- связывает участников паттерна в рабочий сценарий.

Именно поэтому в книгах по паттернам часто пишут `client code`: это просто код, который пользуется абстракцией.

Например:

- в [`Strategy`](https://v8std.ru/patterns/gof/strategy/) клиент выбирает конкретную стратегию и передает ее контексту
- в [`Factory Method`](https://v8std.ru/patterns/gof/factory-method/) клиент просит фабрику создать объект, не зная конкретный класс
- в [`Facade`](https://v8std.ru/patterns/gof/facade/) клиент работает с фасадом вместо прямой координации нескольких подсистем

Это не то же самое, что клиент в клиент-серверной архитектуре.

В клиент-серверной архитектуре клиент — это сторона взаимодействия, которая обращается к серверу по сети или выполняется в клиентском контексте. В 1С сюда относятся, например, клиентское приложение, форма или код с `&НаКлиенте`.

В паттернах `Client` может быть любым потребителем API:

- другим объектом;
- общим модулем;
- подсистемой;
- отдельной процедурой в том же процессе.

То есть один и тот же модуль в 1С может быть серверным по месту выполнения, но при этом оставаться `Client` по роли относительно другого объекта или интерфейса.

## Полиморфизм

Полиморфизм позволяет работать с разными реализациями через общий контракт.

Обычно полезно различать два вида полиморфизма:

- `ad-hoc` полиморфизм;
- классический полиморфизм по подтипу или контракту.

Практический смысл:

- вызывающий код знает, **что** нужно сделать, но не зависит от того, **как именно** это сделано;
- разные варианты поведения можно подменять без переписывания всего сценария;
- длинные ветки `Если ... ИначеЕсли ...` часто можно заменить на набор согласованных реализаций.

Когда это полезно:

- есть несколько способов выполнить одну и ту же операцию;
- алгоритм зависит от типа объекта, режима работы или бизнес-сценария;
- нужно уменьшить связанность между вызывающим кодом и конкретной реализацией.

### `Ad-hoc` полиморфизм

`Ad-hoc` полиморфизм означает, что одна и та же операция ведет себя по-разному в зависимости от типов аргументов.

В 1С это особенно заметно на встроенных операциях и универсальных функциях языка.

```bsl
ЧисловойРезультат = 2 + 3;
ТекстовыйРезультат = "Заказ №" + "15";
НоваяДата = '20240101' + 60;
```

Здесь один и тот же оператор `+`:

- складывает числа;
- конкатенирует строки;
- сдвигает дату на число секунд.

Это и есть `ad-hoc` полиморфизм: синтаксис один, а конкретное поведение выбирается по типам значений.

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

```bsl
Функция ПолучитьКраткоеПредставление(Значение) Экспорт
    ТипЗначения = ТипЗнч(Значение);

    Если ТипЗначения = Тип("Строка") Тогда
        Возврат СокрЛП(Значение);
    ИначеЕсли ТипЗначения = Тип("Число") Тогда
        Возврат Формат(Значение, "ЧГ=0");
    ИначеЕсли ТипЗначения = Тип("Дата") Тогда
        Возврат Формат(Значение, "ДФ=dd.MM.yyyy");
    Иначе
        Возврат Строка(Значение);
    КонецЕсли;
КонецФункции
```

Здесь интерфейс вызова один и тот же, но результат зависит от того, что именно передали в параметр `Значение`. Это тоже `ad-hoc` полиморфизм, только реализованный вручную в коде прикладного решения.

### Классический полиморфизм

Классический полиморфизм означает, что код работает с разными объектами через один и тот же ожидаемый набор операций.

В прикладной разработке на 1С это чаще проявляется не через собственные пользовательские классы, а через:

- общий набор методов у разных объектов платформы;
- договоренность о публичном API;
- единый контракт, который соблюдают разные реализации.

Пример на 1С:

```bsl
Процедура СохранитьОбъект(Объект)
    Объект.Записать();
КонецПроцедуры

Заказ = Документы.ЗаказКлиента.СоздатьДокумент();
Реализация = Документы.РеализацияТоваровУслуг.СоздатьДокумент();

СохранитьОбъект(Заказ);
СохранитьОбъект(Реализация);
```

Для процедуры `СохранитьОбъект` не так важно, какой именно это документ. Ей важен контракт: объект должен уметь выполнять `Записать()`.

Именно это и есть классический полиморфизм: вызывающий код опирается на общее поведение, а не на конкретный тип реализации.

В 1С такой контракт часто делают явным организационно: методы, на которые должны опираться другие подсистемы, выносят в раздел `Программный интерфейс` по [#std455](https://v8std.ru/std/455/) и [#std551](https://v8std.ru/std/551/), а их назначение документируют по [#std453](https://v8std.ru/std/453/)

На что смотреть в 1С:

- не разрастается ли код условиями по типу объекта или режиму выполнения;
- можно ли вынести различающееся поведение за стабильный интерфейс;
- не превращается ли общий модуль в диспетчер из десятков веток.

## Инкапсуляция

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

Практический смысл:

- внешний код меньше знает о внутреннем состоянии и шагах обработки;
- изменение внутренней реализации меньше затрагивает соседние части системы;
- проще держать инварианты и проверять корректность данных в одном месте.

Когда это полезно:

- у сущности есть важные правила изменения состояния;
- внутренние детали легко испортить прямым доступом из разных мест;
- хочется сократить число неявных зависимостей между модулями.

В прикладной архитектуре 1С эту идею поддерживают и стандарты: выносит программный интерфейс из модулей форм, а [#std678](https://v8std.ru/std/678/) требует не держать бизнес-логику в форме и не раскрывать на клиент лишние серверные детали

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

Для регистров это закрепляет [#std477: Самодостаточность регистров](https://v8std.ru/std/477/): логика и отчеты по регистру не должны зависеть от полей регистратора, а должны работать на данных самого регистра

Для документов ту же идею фиксирует [#std603: Требования к проведению документов](https://v8std.ru/std/603/): движения нужно формировать максимально на данных самого документа, а изменяемые внешние данные, влияющие на движения, следует сохранять в документе. Это и есть практическое “замыкание данных в документе”, чтобы результат проведения не зависел от того, как позже изменились НСИ или другие внешние объекты

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

На что смотреть в 1С:

- не читает ли внешний код слишком много внутренних полей напрямую;
- не дублируются ли проверки и подготовка данных в нескольких местах;
- можно ли собрать связанную логику в один понятный API вместо россыпи процедур.

## Наследование

Наследование позволяет строить новую реализацию на основе существующей, переиспользуя общее поведение и уточняя различия.

Обычно полезно различать несколько близких, но не одинаковых сценариев:

- наследование от базового класса;
- многоуровневое наследование;
- множественное наследование;
- реализацию интерфейса.

Практический смысл:

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

Когда это полезно:

- есть устойчивое отношение `общее -> частное`;
- несколько реализаций действительно разделяют существенную общую логику;
- модель становится проще, а не сложнее, если ввести базовый уровень абстракции.

### Наследование от базового класса

Это самый привычный вариант: есть базовый тип с общим поведением, а конкретные типы уточняют его под свою предметную область.

В 1С такой механизм редко оформляется как пользовательское наследование классов, но архитектурно он хорошо читается в платформенных типах, которые порождаются метаданными.

Например, платформа строит конкретные типы объектов метаданных:

- `СправочникОбъект.Номенклатура`;
- `СправочникОбъект.Контрагенты`;
- `ДокументОбъект.ЗаказКлиента`.

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

В этом смысле конкретный справочник можно рассматривать как порожденный из более общего “базового” конструктора платформы для объектов метаданных.

Пример на 1С:

```bsl
Номенклатура = Справочники.Номенклатура.СоздатьЭлемент();
Контрагент = Справочники.Контрагенты.СоздатьЭлемент();

Номенклатура.Наименование = "Товар";
Контрагент.Наименование = "Покупатель";

Номенклатура.Записать();
Контрагент.Записать();
```

Для прикладного кода это выглядит как работа с разными конкретными сущностями, которые унаследовали общий набор действий своего семейства.

### Многоуровневое наследование

Многоуровневое наследование означает, что тип наследуется не напрямую только от одного базового класса, а через цепочку уровней: например, `БазовыйТип -> ПромежуточныйТип -> КонкретныйТип`.

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

- поведение размазывается по нескольким уровням и его трудно собирать в голове;
- изменение базового уровня неожиданно ломает дальних потомков;
- переопределения и `super`-вызовы быстро делают код хрупким;
- иерархия начинает жить своей жизнью и хуже отражает предметную модель.

Поэтому даже там, где многоуровневое наследование технически доступно, обычно предпочитают держать иерархии короткими или заменять их:

- композицией;
- стратегиями;
- явными интерфейсами и делегированием.

В прикладной 1С такой прием применять нельзя: платформа не дает строить собственные пользовательские цепочки наследования классов уровня `базовый -> промежуточный -> конечный`.

То есть разработчик 1С не может осознанно спроектировать многоуровневую объектную иерархию так, как это делают в Java, C# или C++.

Если в 1С возникает желание выстроить несколько уровней “общего поведения”, это обычно признак того, что задачу лучше решить через:

- общий модуль с понятным API;
- композицию нескольких объектов или сервисов;
- полиморфизм по согласованному контракту.

### Множественное наследование

Множественное наследование означает, что один тип наследует поведение сразу от нескольких базовых типов.

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

- становится трудно понять, откуда пришел конкретный метод;
- возникают конфликты одинаковых имен и правил переопределения;
- иерархия становится хрупкой и плохо читаемой.

В 1С такого пользовательского механизма архитектурно нет, и это скорее плюс: платформа не подталкивает к сложным иерархиям, которые потом тяжело сопровождать.

Поэтому в прикладной архитектуре 1С обычно выбирают не множественное наследование, а:

- композицию;
- явный программный интерфейс модуля;
- разделение ответственности по подсистемам и областям API.

### Реализация интерфейса

Во многих языках это отдельный механизм: тип явно пишет, какой интерфейс он реализует.

В 1С такого синтаксиса обычно нет, но архитектурно похожий прием используется постоянно: реализация не объявляется ключевым словом, а организуется через согласованный контракт.

Обычно это выражается так:

- модуль или объект предоставляет ожидаемый набор экспортных методов;
- эти методы выносятся в область `ПрограммныйИнтерфейс`;
- при необходимости методы для внутренних потребителей выносятся в `СлужебныйПрограммныйИнтерфейс`;
- назначение интерфейса документируется по [#std453](https://v8std.ru/std/453/), а сама структура закрепляется по [#std455](https://v8std.ru/std/455/) и [#std551](https://v8std.ru/std/551/)

То есть в 1С реализация интерфейса чаще всего не объявляется явно, а описывается организационно: через специальную область и соблюдение контрактов между модулями и подсистемами.

На что смотреть в 1С:

- не пытаемся ли мы смоделировать сложную иерархию там, где достаточно композиции;
- не подменяем ли отсутствие явных интерфейсов хаотичным набором экспортных процедур;
- оформлен ли контракт в `ПрограммномИнтерфейсе` так, чтобы им реально могли безопасно пользоваться другие подсистемы;
- не используется ли наследование там, где достаточно композиции или стратегии;
- не появляется ли хрупкая иерархия, где изменение базового поведения ломает все частные случаи;
- не маскирует ли наследование обычное копирование кода под “архитектуру”.

## Как читать этот раздел дальше

1. Сначала понять базовый смысл принципа.
2. Потом проверить, какую реальную проблему он решает в прикладной конфигурации.
3. Только после этого переходить к более конкретным паттернам `GOF` и принципам `GRASP`.

Facade

Factory Method

Strategy

Описание процедур и функций #std453

Структура модуля #std455

Самодостаточность регистров #std477

Ограничения на использование экспортных процедур и функций #std544

Разработка конфигураций с повторным использованием общего кода и объектов метаданных #std551

Требования к проведению документов #std603

Обеспечение совместимости библиотек #std644

Безопасность прикладного программного интерфейса сервера #std678

Принципы ООП


# Dependency Inversion Principle (`DIP`)

`DIP` требует, чтобы высокоуровневая логика зависела от абстракций, а не от конкретных деталей инфраструктуры.

## Что означает в 1С

Бизнес-сервису лучше знать про контракт поставщика курсов, уведомлений или оплаты, чем напрямую зависеть от конкретного HTTP-клиента, банка или внешней системы.

В 1С это часто проявляется в двух очень практических местах.

- При работе с внешними компонентами прикладной код стараются завязывать не на конкретный COM-объект или бинарную компоненту, а на общий модуль или подсистему-обертку.
- В механизмах печати роль стабильной границы часто играет `ТабличныйДокумент`: прикладной код зависит от факта, что на выходе есть готовое табличное представление, а не от конкретного устройства печати, формата файла или способа показа пользователю.

То есть `DIP` в 1С не всегда выглядит как "классический интерфейс" в коде. Иногда роль абстракции выполняет:

- общий модуль с контрактом поверх внешней компоненты;
- подсистема, которая централизует подключение внешнего кода;
- промежуточное представление вроде `ТабличныйДокумент`.

Это хорошо видно в:
-  и  по сути закрепляют разделение между получением данных и выводом печатной формы.

## Пример на 1С

#### DataProcessors.PaymentService
```bsl
Var Gateway;

Procedure SetGateway(Gateway_) Export

    Gateway = Gateway_;

EndProcedure

Procedure Pay(InvoiceRef, Amount) Export

    Gateway.Pay(InvoiceRef, Amount);

EndProcedure
```

#### CommonModules.BankGateway
```bsl
Procedure Pay(InvoiceRef, Amount) Export

    // Specific bank integration.

EndProcedure
```

## Частый пример с печатью

В печатных механизмах `DIP` часто выглядит так: прикладной код подготавливает `ТабличныйДокумент`, а уже другой слой решает, показать его, распечатать, сохранить или отправить дальше.

В этом смысле `ТабличныйДокумент` выступает как инвертирующая граница между бизнес-логикой подготовки печати и конкретным способом вывода.

```bsl
Функция СформироватьПечатнуюФорму(ДокументСсылка) Экспорт

    ТабДок = Новый ТабличныйДокумент;
    // Заполнение областей и вывод данных.
    Возврат ТабДок;

КонецФункции

Процедура Печать(ДокументСсылка) Экспорт

    ТабДок = СформироватьПечатнуюФорму(ДокументСсылка);
    ТабДок.Показать();

КонецПроцедуры
```

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

## Где полезен

- в интеграциях с банками, API и внешними сервисами;
- при работе с внешними компонентами через обертки и подсистемы;
- в механизмах печати, где `ТабличныйДокумент` служит стабильным контрактом;
- в прикладных сервисах, которые должны переживать смену провайдера;
- в тестируемой бизнес-логике.

## Когда применяют неправильно

- когда абстракции вводятся без реальной точки изменения;
- когда высокий уровень по-прежнему знает слишком много о деталях;
- когда простую зависимость усложняют ради формального следования принципу.

DIP требует, чтобы высокоуровневая логика зависела от абстракций, а не от конкретных деталей инфраструктуры.

Dependency Inversion Principle


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

В 1С они полезны не как догма, а как набор проверочных вопросов:

- не перегружен ли модуль лишними обязанностями;
- можно ли расширять поведение без постоянной правки старого кода;
- не ломает ли замена реализации ожидания клиента;
- не стал ли контракт слишком широким;
- не зависит ли бизнес-логика напрямую от инфраструктуры.

## Принципы SOLID

| Принцип | Что означает | На что смотреть |
| --- | --- | --- |
| [Single Responsibility Principle](https://v8std.ru/patterns/solid/single-responsibility/) (`SRP`) | У модуля должна быть одна причина для изменения. | Не смешаны ли в одном месте расчет, проведение, печать и интеграции. |
| [Open/Closed Principle](https://v8std.ru/patterns/solid/open-closed/) (`OCP`) | Систему лучше расширять, чем постоянно переписывать. | Можно ли добавить новый вариант без правки стабильного кода. |
| [Liskov Substitution Principle](https://v8std.ru/patterns/solid/liskov-substitution/) (`LSP`) | Подстановка реализации не должна ломать ожидания клиента. | Можно ли безопасно заменить одну реализацию другой. |
| [Interface Segregation Principle](https://v8std.ru/patterns/solid/interface-segregation/) (`ISP`) | Контракты должны быть узкими и целевыми. | Не заставляем ли клиентов зависеть от лишних методов. |
| [Dependency Inversion Principle](https://v8std.ru/patterns/solid/dependency-inversion/) (`DIP`) | Высокоуровневая логика должна зависеть от абстракций, а не от деталей. | Не знает ли бизнес-модуль слишком много о конкретной интеграции или хранилище. |

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

Dependency Inversion Principle

Interface Segregation Principle

Liskov Substitution Principle

Open/Closed Principle

Single Responsibility Principle

SOLID


# Interface Segregation Principle (`ISP`)

`ISP` говорит: лучше несколько узких контрактов, чем один широкий интерфейс, от которого всем достается лишнее.

## Что означает в 1С

Для 1С это обычно означает, что не нужно заставлять клиента зависеть от общего модуля или объекта, где рядом лежат:

- чтение;
- запись;
- печать;
- отправка;
- администрирование.

Если клиенту нужна только одна часть, контракт лучше сузить.

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

Например:

- методы для внешних потребителей выносят в `Программный интерфейс`;
- более узкие точки входа для других подсистем могут оформляться областью `ДляВызоваИзДругихПодсистем`;
- методы для внутренних технических потребителей оставляют в `СлужебныйПрограммныйИнтерфейс`.

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

Это поддерживают и стандарты: [#std455: Структура модуля](https://v8std.ru/std/455/), [#std551: Разработка конфигураций с повторным использованием общего кода и объектов метаданных](https://v8std.ru/std/551/) и [#std644: Обеспечение совместимости библиотек](https://v8std.ru/std/644/)

## Пример на 1С

#### CommonModules.InvoicePrintProvider
```bsl
Function GetPrintData(InvoiceRef) Export

    Return New Structure;

EndFunction
```

#### CommonModules.InvoiceExportProvider
```bsl
Function Export(InvoiceRef) Export

    Return "Exported";

EndFunction
```

#### CommonModules.InvoiceServices
```bsl
#Region Program interface

Function GetPrintData(InvoiceRef) Export

    Return New Structure;

EndFunction

#EndRegion

#Region ДляВызоваИзДругихПодсистем

Function ExportForExchange(InvoiceRef) Export

    Return "Exported";

EndFunction

#EndRegion

#Region Service interface

Procedure ResetCache() Export

EndProcedure

#EndRegion
```

Клиент печати зависит только от `GetPrintData()`, а клиент экспорта только от `Export()`. Им не нужен общий "супер-интерфейс", в котором перемешаны обе обязанности.

## Где полезен

- в общих модулях прикладных сервисов;
- в интеграционных шлюзах;
- в коде форм, которым нужна только узкая часть большого API.

## Когда применяют неправильно

- когда дробление уходит слишком далеко и интерфейсов становится больше, чем смысла;
- когда разделение не отражает реальных сценариев использования;
- когда клиентам почти всегда нужен полный набор операций.

ISP говорит: лучше несколько узких контрактов, чем один широкий интерфейс, от которого всем достается лишнее.

Структура модуля #std455

Interface Segregation Principle


# Liskov Substitution Principle (`LSP`)

`LSP` требует, чтобы одну реализацию можно было заменить другой без поломки ожиданий клиента.

## Что означает в 1С

Если модуль или объект объявлен как реализация общего контракта, клиент должен иметь возможность работать с ним одинаково:

- без неожиданных исключений;
- без изменения смысла результата;
- без скрытых ограничений, о которых контракт не предупреждает.

## Пример на 1С

#### DataProcessors.ReportSourceConsumer
```bsl
Function Load(Source) Export

    Return Source.GetRows();

EndFunction
```

#### CommonModules.QueryReportSource
```bsl
Function GetRows() Export

    Return New ValueTable;

EndFunction
```

#### CommonModules.InteractiveOnlySource
```bsl
Function GetRows() Export

    Raise "Can be used only from form";

EndFunction
```

`InteractiveOnlySource` формально похож на подходящую реализацию, но фактически нарушает `LSP`: клиент ожидает, что `GetRows()` можно вызвать по контракту, а получает ограничение, о котором контракт ничего не сказал.

## Типичный пример в 1С

В 1С нарушение `LSP` часто проявляется не только в возвращаемых значениях, но и в способе сигнализации об ошибках.

Например, для сценария проверки заполнения обычно ожидается, что обработчик:

- установит `Отказ = Истина`;
- добавит понятные сообщения пользователю;
- завершит проверку без исключения.

Именно на такой стиль работы завязаны и платформа, и рекомендации по, и по [#std463: Проверки, выполняемые в и вне транзакции записи объекта](https://v8std.ru/std/463/)

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

Условно:

```bsl
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)

    Если Не ЗначениеЗаполнено(Контрагент) Тогда
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "Не заполнен контрагент.";
        Сообщение.Сообщить();
        Отказ = Истина;
        Возврат;
    КонецЕсли;

КонецПроцедуры
```

И проблемный вариант:

```bsl
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)

    Если Не ЗначениеЗаполнено(Контрагент) Тогда
        ВызватьИсключение "Не заполнен контрагент";
    КонецЕсли;

КонецПроцедуры
```

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

## Где полезен

- в семействах взаимозаменяемых сервисов;
- в расширяемых точках конфигурации;
- там, где один и тот же клиент работает с несколькими реализациями.

## Когда применяют неправильно

- когда общий контракт слишком расплывчат;
- когда разные реализации на самом деле решают разные задачи;
- когда ограничения реализации не выражены в контракте.

LSP требует, чтобы одну реализацию можно было заменить другой без поломки ожиданий клиента.

Обработчик события ОбработкаПроверкиЗаполнения #std463

Liskov Substitution Principle


# Open/Closed Principle (`OCP`)

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

## Что означает в 1С

Если новый вариант поведения появляется регулярно, лучше подготовить точку расширения, чем каждый раз дописывать еще одну ветку `Если ... ИначеЕсли`.

## Пример на 1С

#### DataProcessors.ExportService
```bsl
Var Provider;

Procedure SetProvider(Provider_) Export

    Provider = Provider_;

EndProcedure

Function Export(DocumentRef) Export

    Return Provider.Export(DocumentRef);

EndFunction
```

#### CommonModules.PdfExportProvider
```bsl
Function Export(DocumentRef) Export

    Return "PDF";

EndFunction
```

#### CommonModules.ExcelExportProvider
```bsl
Function Export(DocumentRef) Export

    Return "XLSX";

EndFunction
```

## Где полезен

- для вариантов печати, обмена, расчета и интеграции;
- когда список реализаций растет;
- в устойчивых прикладных сервисах, куда часто добавляют новые режимы.

## Когда применяют неправильно

- когда точку расширения строят заранее без реальной потребности;
- когда ради гипотетической гибкости вводят лишние абстракции;
- когда проще и понятнее оставить обычное условие.

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

Open/Closed Principle


# Single Responsibility Principle (`SRP`)

`SRP` говорит: у модуля, объекта или сервиса должна быть одна причина для изменения.

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

Важно правильно понимать слово "ответственность". В `SRP` это не "кто отвечает за код" и не "какой разработчик владеет модулем". Ответственность здесь - это код, который нужен для одной причины изменения, одного сценария или одного use case.

Если один и тот же модуль меняют:

- из-за изменения расчета;
- из-за изменения печатной формы;
- из-за нового сценария обмена;
- из-за нового UI-поведения;

то у него уже несколько осей изменений, а значит `SRP` нарушен.

## Что означает в 1С

Для 1С это почти всегда вопрос о том, не превратился ли один общий модуль или объект документа в комбайн, где одновременно живут:

- расчет;
- проведение;
- печать;
- обмен;
- уведомления;
- работа формы.

Если эти части меняются по разным причинам, их лучше развести.

То есть полезно спрашивать не "про что этот модуль вообще", а "по какой причине мы будем его менять". Если ответов несколько, модуль, скорее всего, собран слишком широко.

## Пример на 1С

#### CommonModules.SalesOrderTotals
```bsl
Function Calculate(DocumentObject) Export

    TotalAmount = 0;

    For Each ItemRow In DocumentObject.Items Do
        TotalAmount = TotalAmount + ItemRow.Amount;
    EndDo;

    Return TotalAmount;

EndFunction
```

#### CommonModules.SalesOrderPrinting
```bsl
Function GetPrintData(DocumentObject) Export

    PrintData = New Structure;
    PrintData.Insert("Number", DocumentObject.Number);
    PrintData.Insert("TotalAmount", DocumentObject.TotalAmount);
    Return PrintData;

EndFunction
```

## Где полезен

- при проектировании общих модулей;
- в объектных модулях документов и справочников;
- при разбиении слишком больших форм и сервисов.

## Когда применяют неправильно

- когда ради чистоты модуль режут на десятки бессмысленно мелких файлов;
- когда одна целостная операция искусственно распадается на фрагменты;
- когда вместо простой структуры появляется лишняя бюрократия.

SRP говорит: у модуля, объекта или сервиса должна быть одна причина для изменения.

Single Responsibility Principle


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

---

## 1. Лексика и базовые правила языка (символы, ключевые слова, формат кода)

### 1.1 Регистр и язык ключевых слов

Язык **регистронезависим**:

- `Если`, `ЕСЛИ`, `если` — эквивалентны.

Язык **двуязычный**:

- почти все ключевые слова имеют русское и английское написание;
- можно смешивать языки в одном модуле.

### 1.2 Идентификаторы

Имена переменных, процедур и функций:

- начинаются с буквы или `_`;
- содержат буквы, цифры и `_`;
- не должны совпадать с ключевыми словами языка;
- не должны совпадать с именами свойств/методов, доступных в текущем контексте.

Примеры корректных идентификаторов:
```bsl
Сумма
_ВнутреннийФлаг
СформироватьОтчет2024
```

### 1.3 Комментарии

Комментарий начинается с `//` и заканчивается концом строки.

```bsl
// Это комментарий
Сумма = 10; // Комментарий после оператора
```

После `//` на той же строке **нельзя** продолжать писать код.

### 1.4 Разделитель операторов

Операторы отделяются **точкой с запятой** `;`.

Конец строки **не завершает** оператор. Оператор может переноситься.

```bsl
Сумма = 1 +
        2 +
        3;
```

Можно писать несколько операторов в одной строке:
```bsl
A = 1; B = 2; C = A + B;
```

### 1.5 Метки и переходы

Метка — это специальный идентификатор, начинающийся с `~` и заканчивающийся `:`.

```bsl
~Начало: A = 1;
```

Метки используются оператором `Перейти` (аналог `Goto`). Это редкий инструмент, применяйте только при реальной необходимости.

### 1.6 Специальные символы (что означает каждый)

- `//` — комментарий до конца строки
- `|` — перенос строки в многострочной строке (строка-продолжение)
- `~` — начало метки
- `:` — конец метки
- `;` — разделитель операторов
- `()` — параметры вызова и управление приоритетом
- `[]` — доступ к свойству по имени / доступ к элементу коллекции
- `,` — разделитель параметров
- `""` — строковый литерал
- `''` — литерал даты
- `.` — доступ к свойствам/методам
- `+ - * / %` — арифметика и/или конкатенация
- `> >= ` — сравнение

### 1.7 Ключевые слова (зарезервированные)

Эти слова нельзя использовать как имена переменных/процедур/функций.

Управление потоком:

- `Если/If`, `Тогда/Then`, `ИначеЕсли/ElsIf`, `Иначе/Else`, `КонецЕсли/EndIf`
- `Для/For`, `Каждого/Each`, `Из/In`, `По/To`
- `Пока/While`, `Цикл/Do`, `КонецЦикла/EndDo`
- `Продолжить/Continue`, `Прервать/Break`, `Возврат/Return`
- `Перейти/Goto`

Процедуры/функции и переменные:

- `Процедура/Procedure`, `КонецПроцедуры/EndProcedure`
- `Функция/Function`, `КонецФункции/EndFunction`
- `Перем/Var`

Логика:

- `И/And`, `Или/Or`, `Не/Not`

Исключения:

- `Попытка/Try`, `Исключение/Except`, `КонецПопытки/EndTry`, `ВызватьИсключение/Raise`

Прочее:

- `Новый/New`
- `Выполнить/Execute`
- `Ждать/Await`

---

## 2. Типы данных и значения

### 2.1 Примитивные типы

**Число**

- десятичное;
- до 38 значащих цифр;

**Строка**

- Unicode;
- произвольной длины;
- поддерживает многострочные литералы.

**Дата**

- дата и время;
- точность до 1 секунды;
- арифметика с секундами.

**Булево**

- `Истина` / `Ложь`;
- результат логических выражений.

**`Неопределено`**

- отсутствие значения;
- используется по умолчанию.

**`Null`**

- отсутствие данных БД;
- применяется при соединениях и запросах.

### 2.2 Тип

Используется для:

- проверки типов;
- сравнения типов;
- динамической логики.

---

## 3. Выражения и вычисления (все операции с примерами)

Выражение — это формула, которая вычисляет значение. Выражения состоят из литералов, переменных, вызовов функций и операций.

### 3.1 Арифметические операции

> Ниже перечислены **все арифметические операции**, доступные в языке.

**Сложение `+`**
```bsl
Сумма = 2 + 3; // 5
```
Определено для:

- `Число + Число`
- `Дата + Число` (число — секунды)
-
```bsl
НоваяДата = '20240101' + 60; // +60 секунд
```

**Вычитание `-`**
```bsl
Разница = 10 - 4; // 6
```
Определено для:

- `Число - Число`
- `Дата - Число` (число — секунды)
- `Дата - Дата` (результат — секунды)

```bsl
Дата1 = '20240101';
Дата2 = '20240101';
Секунды = Дата1 - Дата2; // 0
```

**Умножение `*`**
```bsl
Итог = 6 * 7; // 42
```

**Деление `/`**
```bsl
Частное = 10 / 4; // 2.5
```

**Остаток от деления `%`**
```bsl
Остаток = 10 % 3; // 1
```

**Унарный минус `-`**
```bsl
X = -5;
Y = -(2 + 3); // -5
```

> Примечание: семантика операции определяется типом **первого операнда**. Если тип второго операнда не подходит, платформа пытается привести тип автоматически; если нельзя — возникает ошибка выполнения.

### 3.2 Операция конкатенации строк

Конкатенация использует тот же оператор `+`.

```bsl
ФИО = Фамилия + " " + Имя + " " + Отчество;
```
Если правый операнд не строка, он будет преобразован к строке по правилам преобразования типов.

### 3.3 Операции сравнения

Операции сравнения возвращают **Булево**: `Истина` или `Ложь`.

**Больше `>`**
```bsl
Результат = (10 > 5); // Истина
```

**Больше или равно `>=`**
```bsl
Результат = (10 >= 10); // Истина
```

**Меньше `<`**
```bsl
Результат = (3 < 2); // Ложь
```

**Меньше или равно `<=`**
```bsl
Результат = (3 <= 3); // Истина
```

**Равно `=`**
```bsl
Результат = ("A" = "A"); // Истина
```

**Не равно `<>`**
```bsl
Результат = ("A" <> "B"); // Истина
```

Операции сравнения определены для:
- `Число`, `Строка`, `Дата` (для `> >= < <=`)
- любых типов (для `=` и `<>`)

### 3.4 Булевы операции (логика)

**`И` / `And`**
```bsl
Результат = (Цена > 0) И (Количество > 0);
```

**`Или` / `Or`**
```bsl
Результат = (ЭтоАдмин) Или (ЭтоМенеджер);
```

**`Не` / `Not`**
```bsl
Результат = Не ЭтоАдмин;
```

**Короткое замыкание**
Вычисляются только необходимые части выражения.

```bsl
Если (Цена > 0) И ПроверкаСуммы() Тогда
    // Если Цена <= 0, функция ПроверкаСуммы() не вызовется
КонецЕсли;
```

### 3.5 Приоритет операций

Операторы с одинаковым приоритетом вычисляются слева направо.

1. Скобки: `()`
2. `Не`
3. `И`
4. `Или`
5. Сравнение: `= > <>`
6. `+ -`
7. `* / %`
8. Унарные: `- +`
9. Доступ/вызов: `.` и `()`

Практическое правило:

- если есть сомнения — ставьте скобки.

---

## 4. Операторы управления (все базовые конструкции с примерами)

### 4.1 Условие: `Если` / `ИначеЕсли` / `Иначе`

```bsl
Если Сумма > 1000 Тогда
    Сообщить("Скидка применена");
ИначеЕсли Сумма > 0 Тогда
    Сообщить("Без скидки");
Иначе
    Сообщить("Сумма не задана");
КонецЕсли;
```

### 4.2 Цикл `Для ... По ...`

Числовой цикл.

```bsl
Для i = 1 По 5 Цикл
    Сообщить(i);
КонецЦикла;
```

Шаг цикла задать невозможно, шак всегда равен `1`;

### 4.3 Цикл `Для Каждого ... Из ...`

Обход коллекций.

```bsl
Для Каждого Элемент Из Коллекция Цикл
    Сообщить(Элемент);
КонецЦикла;
```

### 4.4 Цикл `Пока ...`

Цикл с предусловием.

```bsl
Пока Счетчик < 3 Цикл
    Счетчик = Счетчик + 1;
КонецЦикла;
```

### 4.5 Управление циклом

**`Продолжить`** — перейти к следующей итерации:
```bsl
Для Индекс = 1 По 5 Цикл
    Если Индекс = 3 Тогда
        Продолжить;
    КонецЕсли;
    Сообщить(Индекс); // выведет 1,2,4,5
КонецЦикла;
```

**`Прервать`** — выйти из цикла:
```bsl
Для Индекс = 1 По 5 Цикл
    Если Индекс = 3 Тогда
        Прервать;
    КонецЕсли;
КонецЦикла;
```

### 4.6 Возврат из функции

`Возврат` завершает функцию и возвращает значение.

```bsl
Функция Макс(А, Б)
    Если А > Б Тогда
        Возврат А;
    Иначе
        Возврат Б;
    КонецЕсли;
КонецФункции
```

### 4.7 Переход `Перейти` (`Goto`)

Используйте только если действительно нужен линейный сценарий с редкими ветвлениями.

```bsl
A = 0;
Перейти ~Конец;
A = 1; // не выполнится
~Конец:
Сообщить(A); // 0
```

### 4.8 Исключения: `Попытка` / `Исключение` / `ВызватьИсключение`

```bsl
Попытка
    // потенциально опасный код
    Рез = 10 / 0;
Исключение
    Сообщить("Произошла ошибка");
КонецПопытки;
```

Явный выброс исключения:
```bsl
Если Сумма <= 0 Тогда
    ВызватьИсключение "Сумма должна быть больше 0";
КонецЕсли;
```

### 4.9 `Новый` (создание объекта)

`Новый` создает объект указанного типа.

```bsl
М = Новый Массив;
```

> Конкретные имена типов объектов зависят от доступного контекста (платформа и конфигурация).

### 4.10 `Выполнить` (`Execute`)

Выполняет строку как код.

```bsl
ТекстКода = "A = 1;";
Выполнить(ТекстКода);
```

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

### 4.11 `Асинх` (`Async`)

Ключевое слово языка для определения процедур и функций на клиенте как асинхронных.

Асинхронное исполнение возможно только у асинхронных функций.
Ждать асинхронные функции можно, а асинхронные процедуры нельзя.
Из асинхронных процедур можно вызывать асинхронные функции.
Из простых процедур нельзя вызывать асинхронные функции.

При добавлении `Асинх` к процедуре или функции все ее параметры начинают передаваться по значению, так, как будто перед ними стояло бы ключевое слово `Знач`.

### 4.12 `Ждать` (`Await`)

Ключевое слово языка для ожидания (использование зависит от поддерживаемых механизмов в окружении).

---

## 5. Процедуры и функции

### 5.1 Процедуры

- выполняют действие;
- не возвращают значение;
- могут быть экспортируемыми.

### 5.2 Функции

- возвращают значение через `Возврат`;
- могут использоваться в выражениях;
- допускают досрочный возврат.

### 5.3 Параметры

- по значению (при использовании `Знач` и в асинхронных методах);
- по ссылке (по умолчанию);
- значения по умолчанию.

---

## 6. Программный модуль

### 6.1 Разделы модуля

1. Переменные (`Перем`)
2. Процедуры и функции
3. Основной код (инициализация)

---

## 7. Контекст выполнения

### 7.1 Глобальный контекст

- функции платформы;
- системные перечисления;
- глобальные общие модули.

### 7.2 Локальный контекст

Зависит от:

- типа модуля;
- объекта конфигурации;
- режима запуска.

---

## 8. Клиент-серверная модель

### 8.1 Среды выполнения

- клиентское приложение;
- сервер приложения.

### 8.2 Директивы компиляции

- `&НаКлиенте`
- `&НаСервере`
- `&НаСервереБезКонтекста`
- `&НаКлиентеНаСервере`

Директива:

- определяет место выполнения;
- влияет на доступные данные;
- влияет на сетевые вызовы.

---

## 9. Препроцессор

### 9.1 Назначение

- исключение кода;
- платформозависимая логика;
- группировка.

### 9.2 Инструкции

- `#Если / #Иначе / #КонецЕсли`
- `#Область / #КонецОбласти`
- `#Вставка / #Удаление`

Препроцессор выполняется **до компиляции**.

---

## 10. Аннотации

- `&Перед`
- `&После`
- `&Вместо`
- `&ИзменениеИКонтроль`

Используются только в расширениях.

---

## 11. Особенности и ограничения языка

- динамическая типизация;
- отсутствие классов в классическом смысле;
- сильная зависимость от контекста;
- ориентация на бизнес-логику;
- высокая роль архитектурных решений.

---

## Итог

Встроенный язык:

- прост синтаксически;
- сложен контекстуально;
- требует архитектурного мышления.
- ориентирован на бизнес-логику.

Его нужно изучать **не как язык общего назначения**, а как инструмент платформы.

Главное для начинающего:

- понимать, **где выполняется код**;
- понимать, **в каком контексте** он работает;
- писать простой и читаемый код.

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

Описание возможностей и особенностей встроенного языка


MCP (Model Context Protocol) — протокол, через который среда разработки или ИИ-агент
получает доступ к внешним инструментам и источникам данных.

Для `v8std.ru` MCP-сервер дает агенту структурированный доступ к стандартам,
диагностикам и связям между ними. Вместо того чтобы искать страницы через браузер,
агент вызывает методы MCP: ищет стандарт, получает страницу по идентификатору,
переходит к связанным диагностикам или объясняет предупреждение анализатора.

Основной сценарий — совместная работа с локальными инструментами анализа кода,
например с [`bsl-analyzer`](https://github.com/itrous/bsl-analyzer)
или [BSL Language Server](https://github.com/1c-syntax/bsl-language-server):

1. локальный анализатор проверяет исходный код проекта и находит диагностику;
2. агент запрашивает в MCP описание диагностики и связанные стандарты;
3. агент предлагает исправление с учетом причины нарушения, а не только текста предупреждения.

MCP-сервер `v8std` не анализирует исходный код пользователя и не принимает файлы
конфигурации. Он работает как read-only банк знаний по публичным материалам сайта.

## Подключение готового сервиса

Публичный MCP-сервер доступен по адресу:

```text
https://ai.v8std.ru/mcp
```

Подключение к Codex:

```bash
codex mcp add v8std --url https://ai.v8std.ru/mcp
```

Подключение к Claude Code:

```bash
claude mcp add --transport http v8std https://ai.v8std.ru/mcp
```

Проверочные endpoint'ы:

```text
https://ai.v8std.ru/healthz
https://ai.v8std.ru/version
```

Готовый сервис использует опубликованный индекс сайта и подходит для обычной
работы в IDE: поиск стандартов, объяснение диагностики, подбор связанных страниц.

## Безопасность и приватность

Публичный `https://ai.v8std.ru/mcp` работает только с публичным индексом сайта и
не выполняет пользовательский код. При этом все параметры MCP-вызовов передаются
на сервер в HTTP body, поэтому публичный сервис не предназначен для приватных
фрагментов исходного кода в `v8std.explain_snippet`.

Для закрытого кода используйте локальный MCP-контейнер и передавайте на публичный
сервис только поисковые фразы, идентификаторы стандартов и коды диагностик.
Продакшен-контур должен держать MCP-процесс на `127.0.0.1`, проксировать его
через nginx и ограничивать `/mcp`: `client_max_body_size 256k`, rate limit и
логи без тел запросов.

## Локальный запуск

Локальный вариант нужен, если требуется полностью автономный запуск по файлам
репозитория или нужно проверить изменения сайта до публикации.

```bash
git clone https://github.com/zeegin/v8std.git
cd v8std

docker compose -f docker-compose/docker-compose.yml up -d v8std-mcp
```

MCP будет доступен только с этой машины:

```text
http://127.0.0.1:8765/mcp
```

Подключение к Codex:

```bash
codex mcp add v8std-local --url http://127.0.0.1:8765/mcp
```

Подключение к Claude Code:

```bash
claude mcp add --transport http v8std-local http://127.0.0.1:8765/mcp
```

Локальный контейнер монтирует репозиторий в `/docs` и читает файлы
`/docs/docs/ai/pages.jsonl` и `/docs/docs/ai/search-vectors.jsonl`.
При `V8STD_MCP_GENERATE_INDEX=auto` индекс и векторы генерируются при старте
контейнера, если файлы отсутствуют.

Полезные команды:

```bash
curl http://127.0.0.1:8765/healthz
docker logs v8std-mcp
docker compose -f docker-compose/docker-compose.yml restart v8std-mcp
```

## Инструменты MCP

### `v8std.search`

Ищет страницы гибридным ранжированием: точные алиасы и коды диагностик,
BM25 по заголовкам и тексту, статический векторный индекс и доменные правила.

Подходит для сценариев:

- найти стандарт по `std437`, `#std437`, `стандарт 437`;
- найти диагностику по `acc:1245`, `апк 1245`, `AssignAliasFieldsInQuery`;
- найти материалы по теме: `запросы`, `регламентные задания`, `права`;
- найти смысловой запрос: `форма не должна содержать бизнес-логику и доступ к данным`;
- разобрать кодовый фрагмент: `Новый Запрос("ВЫБРАТЬ РАЗРЕШЕННЫЕ ...")`.

Основные параметры:

| Параметр | Назначение |
| --- | --- |
| `query` | Строка поиска |
| `types` | Необязательный список типов: `standard`, `diagnostic`, `pattern`, `service` |
| `mode` | `hybrid`, `exact`, `bm25` или `semantic` |
| `limit` | Количество результатов, не больше 50 |

### `v8std.page`

Возвращает страницу по идентификатору, алиасу, пути или URL.

Подходит для сценариев:

- получить полный текст стандарта перед исправлением кода;
- открыть Markdown-версию страницы по HTML-ссылке;
- получить описание диагностики по точному коду.

Основные параметры:

| Параметр | Назначение |
| --- | --- |
| `id_or_alias_or_url` | Идентификатор, алиас, путь, HTML URL или Markdown URL |
| `body_limit` | Ограничение размера возвращаемого Markdown |

### `v8std.related`

Возвращает связанные стандарты, диагностики и проверки EDT.

Подходит для сценариев:

- от стандарта перейти к диагностике анализатора;
- от диагностики перейти к стандарту, объясняющему причину;
- собрать контекст для code review или автоисправления.

Основные параметры:

| Параметр | Назначение |
| --- | --- |
| `id_or_alias_or_url` | Исходная страница |
| `relations` | Необязательный список связей: `standard`, `diagnostic`, `edt_check`, `related` |
| `limit` | Количество связей |

### `v8std.explain_snippet`

Разбирает короткий фрагмент BSL/SDBL и возвращает распознанные токены,
сигналы, вероятные диагностики, стандарты и confidence.

Подходит для сценариев:

- передать `Запрос = Новый Запрос("ВЫБРАТЬ РАЗРЕШЕННЫЕ ...")` и получить `std415`;
- передать `ОткрытьФормуМодально("...")`, `Предупреждение("...")`, `Вопрос("...")` и получить
  `bslls:UsingModalWindows`;
- отделить кодовые токены от обычного текстового поиска.

`explain_snippet` ориентирован на фрагменты кода. Для обычной фразы вроде
`модальные окна` используйте `v8std.search`.

Основные параметры:

| Параметр | Назначение |
| --- | --- |
| `snippet` | Фрагмент кода до 4000 символов |
| `language` | `auto`, `bsl` или `sdbl` |
| `limit` | Количество вероятных страниц |

### `v8std.explain_diagnostics`

Объясняет список диагностик и группирует связанные стандарты.

Подходит для сценариев:

- после запуска [`bsl-analyzer`](https://github.com/itrous/bsl-analyzer)
  объяснить пачку предупреждений;
- сгруппировать отчет BSL Language Server по стандартам;
- выбрать исправления, которые устраняют причины нарушений.

Основные параметры:

| Параметр | Назначение |
| --- | --- |
| `codes` | Список кодов: `acc 1245`, `bslls:AssignAliasFieldsInQuery`, `v8cs:...`; максимум 500 |

## Файлы для ИИ-инструментов

Сайт генерирует несколько статических файлов, которые можно использовать без MCP.

| Файл | Назначение |
| --- | --- |
| [`/llms.txt`](https://v8std.ru/llms.txt) | Компактная карта сайта: разделы, страницы, ссылки на корпус и индекс |
| [`/llms-full.txt`](https://v8std.ru/llms-full.txt) | Полный очищенный Markdown-корпус страниц без front matter и служебной разметки темы |
| [`/ai/pages.jsonl`](https://v8std.ru/ai/pages.jsonl) | Машинный JSONL-индекс страниц, алиасов, связей, источников и очищенного Markdown |
| [`/ai/search-vectors.jsonl`](https://v8std.ru/ai/search-vectors.jsonl) | Статический векторный индекс для semantic-слоя MCP |
| `/*.md` | Очищенная Markdown-версия публичной страницы по исходному пути, например [`/std/437.md`](https://v8std.ru/std/437.md) |

`/ai/pages.jsonl` является основным источником данных для MCP. Каждая строка —
отдельная страница со следующими полями:

| Поле | Назначение |
| --- | --- |
| `id` | Стабильный идентификатор: `std437`, `acc:1245`, `bslls:AssignAliasFieldsInQuery` |
| `type` | Тип страницы: `standard`, `diagnostic`, `pattern`, `service` |
| `title` | Заголовок страницы |
| `description` | Краткое описание |
| `url` | Канонический HTML URL |
| `markdown_url` | URL очищенной Markdown-версии |
| `source_path` | Исходный путь страницы в репозитории |
| `aliases` | Поисковые алиасы и варианты записи идентификатора |
| `related` | Связанные стандарты, диагностики и проверки |
| `source_urls` | Внешние источники страницы |
| `body_markdown` | Очищенное содержимое страницы |

`/ai/search-vectors.jsonl` строится из `pages.jsonl`. MCP читает этот файл как
готовый read-only индекс; если файл временно недоступен, сервер продолжает
работать через точные правила и BM25, а `/healthz` показывает
`semantic_enabled: false`.

Через MCP resources доступны:

```text
v8std://llms.txt
v8std://llms-full.txt
v8std://ai/pages.jsonl
```

Страницы с front matter `llms.ignore: true` исключаются из `/llms.txt`,
`/llms-full.txt` и `/ai/pages.jsonl`, но остаются обычными страницами сайта.

## Детали для разработчиков

Код локального MCP находится в репозитории:

| Файл | Назначение |
| --- | --- |
| `scripts/v8std_mcp_index.py` | Загрузка индекса, нормализация алиасов, поиск, связи |
| `scripts/v8std_mcp_server.py` | HTTP MCP-сервер и регистрация инструментов |
| `scripts/generate_search_vectors.py` | Генерация статического векторного индекса |
| `scripts/search_benchmark.py` | Регрессионный benchmark поиска |
| `scripts/run_v8std_mcp.sh` | Docker-entrypoint для автономного локального запуска |
| `retrieval-rules.yml` | Доменные алиасы, синонимы и code-aware mappings |
| `requirements-mcp.txt` | Зависимости MCP-сервера |
| `tests/test_v8std_mcp_index.py` | Unit-тесты поиска и нормализации |

Переменные локального Docker-запуска:

| Переменная | Значение по умолчанию | Назначение |
| --- | --- | --- |
| `V8STD_MCP_PAGES` | `/docs/docs/ai/pages.jsonl` | Локальный JSONL-индекс |
| `V8STD_MCP_VECTORS` | `/docs/docs/ai/search-vectors.jsonl` | Локальный векторный индекс |
| `V8STD_MCP_GENERATE_INDEX` | `auto` | `auto` генерирует индекс только при отсутствии файла, `always` всегда обновляет |
| `V8STD_MCP_HOST` | `0.0.0.0` | Адрес, на котором слушает контейнер |
| `V8STD_MCP_PORT` | `8765` | Порт MCP-сервера |
| `V8STD_MCP_PATH` | `/mcp` | HTTP path MCP endpoint'а |

В `docker-compose` порт опубликован как `127.0.0.1:8765:8765`, поэтому контейнер
слушает `0.0.0.0` только внутри Docker-сети и не открывает MCP наружу.

Локальная проверка MCP без Docker:

```bash
python3.12 -m venv .venv
source .venv/bin/activate

pip install -r requirements.txt -r requirements-mcp.txt
python scripts/generate_ai_artifacts.py
python scripts/generate_search_vectors.py
python scripts/v8std_mcp_server.py \
  --pages docs/ai/pages.jsonl \
  --vectors docs/ai/search-vectors.jsonl \
  --host 127.0.0.1 \
  --port 8765
```

Проверка качества поиска:

```bash
python -m unittest tests.test_v8std_mcp_index
python scripts/search_benchmark.py
```

MCP-сервер intentionally read-only: он не изменяет код пользователя, не запускает
анализаторы и не принимает файлы исходников. Для анализа проекта подключайте локальный
[`bsl-analyzer`](https://github.com/itrous/bsl-analyzer) или другой анализатор
отдельным инструментом, а `v8std` используйте как источник стандартов,
связей и объяснений.

MCP для ИИ-инструментов


1590


3221


#### #metod3266

#### 1.

Возможности обычного приложения, которые не реализованы в управляемом приложении

#### 2.

Управляемая форма

| Возможность | Реализовано | Рекомендация |
| --- | --- | --- |
| Редактирование непосредственно в динамических списках | Нет | Использовать для редактирования форму объекта. |
| Команда перехода к списку из формы объекта или записи | Нет | Проектировать интерфейс без этой возможности. Нужные команды размещать прямо в форме объекта. |
| События табличного поля `ПриВыводеСтроки`, `ПриПолученииДанных` | Нет | Использовать условное оформление формы. |
| Событие формы `ОбновлениеОтображения` | Нет | Использовать обработчики событий изменения данных. |
| Механизм оповещения об изменении данных формы | Нет | Использовать обработчики событий изменения данных. |
| Ввод на основании в журналах документов | Нет | Не использовать. При необходимости реализовывать программно. |
| Команды изменения времени в формах | Нет | Не включать в прикладное решение. |
| Команды активизации и выполнения задач в формах списков задач и бизнес-процессов | Нет | Использовать аналогичные команды в формах или реализовывать программно. |
| ActiveX в форме | Нет | Использовать другие возможности. |
| Интерактивные обработчики бизнес-процессов и задач | Нет | Реализовать программную методику с анализом бизнес-процесса, точки маршрута и задачи. |
| Механизм проверки отображения новой строки в списках | Нет | Ориентировать пользователей на оповещения о записи данных. |
| Картинка в поле надписи | Нет | Использовать другие возможности. |
| Картинка в поле ввода | Нет | Не использовать данную возможность. |
| Настройка вертикального положения в поле ввода, поле надписи и т. д. | Нет | Не использовать данную возможность. |
| Режим бегущей строки в поле надписи | Нет | Не использовать данную возможность. |
| Прозрачный фон поля ввода, поля надписи, поля флажка и т. д. | Нет | Не использовать данную возможность. |
| Цвет фона и рамки поля переключателя | Нет | Не использовать данную возможность. |
| Установка картинки заголовка формы | Нет | Не использовать данную возможность. |
| Фиксация строк при прокрутке таблицы | Нет | Не использовать данную возможность. |
| Фоновая картинка формы | Нет | Не использовать данную возможность. |
| Группировка колонок при выводе таблицы в табличный документ | Нет | Не использовать данную возможность. |

#### 3.

Динамический список

| Возможность | Реализовано | Рекомендация |
| --- | --- | --- |
| Хранение отборов и история отборов в динамическом списке | Добавленные отборы запоминаются, но с выключенным признаком использования | В отдельных случаях реализовывать программно. |
| Отбор по текущей колонке в динамическом списке | За счет функциональности поиска | Рекомендовать пользователям использовать поиск. Поиск по умолчанию предлагает значение текущей колонки. |
| Команда отключения отбора в динамических списках | Нет | Для оперативного отбора использовать поиск, для остальных случаев — режим настройки списка. |
| Команда сортировки в динамических списках | Нет | Использовать сортировку в шапке таблицы и режим настройки списка. |
| Установка сортировки при открытии в динамических списках | Нет | Не использовать данную возможность. |
| Пользовательские настройки в динамическом списке (режим иерархического списка при открытии) | Нет | Не использовать. Для поиска по всему списку рекомендовать режим поиска. |
| Установка отбора по значению критерия отбора | Нет | Использовать другие отборы или форму критерия отбора. |

#### 4.

Командный интерфейс

| Возможность | Реализовано | Рекомендация |
| --- | --- | --- |
| Использование альтернативных сочетаний клавиш для системных команд | Нет | Не использовать данную возможность. |

#### 5.

Прочее

| Возможность | Реализовано | Рекомендация |
| --- | --- | --- |
| Сводная таблица и сводная диаграмма | Нет | Использовать возможности системы компоновки данных. |
| Построитель отчета анализа данных | Нет | Реализовывать программно. |
| Макет типа ActiveDocument на клиенте | Нет | Работать с макетами типа ActiveDocument на сервере. |
| Рабочая дата | Нет | Не использовать данную возможность. |
| Использование стилей в управляемом приложении | Имеется возможность создавать элементы стиля | Использовать единообразное оформление прикладных решений. |
| Автоматическое смещение окон при открытии | Нет | Не использовать данную возможность. |
| Конструктор запроса для клиента управляемого приложения | Нет | Не использовать данную возможность. |
| Конструктор схемы компоновки данных для клиента управляемого приложения | Нет | Использовать варианты отчета. |
| Использование объекта `СравнениеФайлов` в тонком клиенте и на сервере | Нет | Не использовать данную возможность. |

#### См. также

- [#std680: Поддержка толстого клиента, управляемое приложение, клиент-сервер](https://v8std.ru/std/680/)

#### Источник

https://its.1c.ru/db/metod8dev/content/3266/hdoc

Поддержка толстого клиента, управляемое приложение, клиент-сервер #std680

Функциональность обычного приложения, отсутствующая в управляемом приложении


#### #metod4105

#### 1.

Цель: обеспечить предсказуемую и быструю работу при обычных и низкоскоростных каналах связи.

#### 2.

Оптимизируйте два показателя:

- количество серверных вызовов;
- объем передаваемых данных (трафик).

Даже при небольшом трафике большое число вызовов может заметно замедлять работу.

#### 3.

Для анализа и контроля используйте три инструмента:

- `Показатели производительности` в режиме `1С:Предприятие`;
- `Замер производительности` в конфигураторе;
- режим `Имитация задержек при вызовах сервера`.

`Показатели производительности` удобны для регулярного контроля в ходе разработки.
`Замер производительности` полезен для анализа конкретных строк модулей.
`Имитация задержек` помогает оценить комфортность пользовательских сценариев.

#### 4.

Учитывайте, что итоговые показатели формируются совместно платформой и конфигурацией.

Платформа минимизирует нагрузку за счет следующих принципов:

- передает на клиент только нужные данные;
- по возможности передает данные «по требованию»;
- передает данные порциями;
- использует многоуровневое кеширование.

Эти принципы иногда противоречат друг другу,
поэтому в реальных сценариях платформа применяет компромиссы.

#### 4.1.

Упрощенно можно выделить уровни кеширования:

- между сеансами;
- в пределах сеанса;
- в пределах формы.

Из-за кеширования один и тот же сценарий может давать разные результаты
при первом и повторном выполнении.

#### 5.

Проводите проверки на реалистичных сценариях и объемах данных.

Например, табличную часть тестируйте на типичном и на редком «тяжелом» объеме строк.

#### 5.1.

Используйте три уровня испытаний:

1. первое действие после изменения конфигурации;
2. первое действие в сеансе (когда в прошлых сеансах оно уже выполнялось);
3. повторное действие в текущем сеансе.

Обычно наиболее важны уровни `2` и `3`.
При ограниченном времени анализа приоритет чаще у уровня `3`.

#### 5.2.

Для уровня `3` повторяйте действие с новыми прикладными данными,
а не с полностью идентичными.

Так оценка ближе к реальной работе пользователей.

#### 6.

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

Не путайте:

- `Низкая скорость соединения` — меняет поведение платформы для медленных каналов;
- `Имитация задержек` — воспроизводит внешние условия работы канала.

#### 7.

Интерпретируйте результаты аккуратно:

- часть вызовов инициирует код конфигурации;
- часть вызовов выполняет сама платформа вне кода модулей.

Полезные приемы анализа:

- сравнить сценарий с включенным фрагментом конфигурации и без него;
- сравнить показатели со «стендовой» конфигурацией на настройках по умолчанию;
- зафиксировать нормативы по критичным сценариям и отслеживать их в динамике.

#### 8.

Ориентиры по числу вызовов (уровень `3`; значения уровня `2` указаны в скобках,
если отличаются):

| Операция | Обычная скорость | Низкая скорость | Примечание |
| --- | --- | --- | --- |
| Переход на подсистему в панели разделов | `0` | `0` |  |
| Открытие списка справочника | `1` | `1` |  |
| Открытие формы выбора справочника | `1` | `0 (1)` | Повторное открытие формы выбора с теми же параметрами |
| Выбор из формы выбора справочника и закрытие формы выбора | `0` | `0` |  |
| Открытие формы элемента справочника | `1` | `1` |  |
| Закрытие формы элемента справочника | `0` | `0` |  |
| Запись и закрытие формы элемента справочника после редактирования | `2` | `1` | Тест со закрытым списком справочника |
| Запись, проведение и закрытие формы документа после редактирования | `2` | `1` | Тест со закрытым списком документов |
| Установка отбора в таблице, связанной с динамическим списком | `1` | `1` |  |
| Получение списка выбора при вводе по строке | `1` | `0 (1)` | Повторный ввод с теми же условиями поиска |
| Формирование отчета | `1` | `1` |  |

#### 9.

При выработке решений по оптимизации задавайте два вопроса:

- можно ли убрать этот вызов;
- можно ли сократить объем передаваемых данных.

Типовые приемы:

- объединять несколько вызовов в один;
- исключать лишние вызовы;
- использовать повторно используемые возвращаемые значения там, где это оправдано.

Если цели оптимизации конфликтуют, выбирайте компромисс на основе замеров
в максимально близком к эксплуатации стенде.

#### См. также

- [#std487: Минимизация количества серверных вызовов и трафика](https://v8std.ru/std/487/)
- [#std724: Использование модулей с повторным использованием возвращаемых значений](https://v8std.ru/std/724/)
- [#std459: Использование значений, влияющих на поведение клиентского приложения](https://v8std.ru/std/459/)

#### Источник

https://its.1c.ru/db/metod81/content/4105/hdoc

Использование значений, влияющих на поведение клиентского приложения #std459

Минимизация количества серверных вызовов и трафика #std487

Использование модулей с повторным использованием возвращаемых значений #std724

Оптимизация клиент-серверного взаимодействия прикладных решений


## Форматы запросов

| Что нужно найти | Примеры запросов |
| --- | --- |
| Стандарт | `#std437`, `std 437`, `стандарт 437` |
| Диагностику BSLLS | `bslls:AssignAliasFieldsInQuery`, `AssignAliasFieldsInQuery` |
| Диагностику АПК | `acc:1245`, `апк 1245`, `ACC 1245` |
| Диагностику EDT / v8-code-style | `v8cs:common-module-name-client-server`, `edt common-module-name-client-server` |
| Тему | `транзакции`, `запросы`, `общие модули`, `права` |

## Индексы для LLM

| Файл | Назначение |
| --- | --- |
| [/llms.txt](https://v8std.ru/llms.txt) | Компактная карта сайта со стандартами, диагностиками и ссылками на машинный индекс |
| [/llms-full.txt](https://v8std.ru/llms-full.txt) | Очищенный полный Markdown-корпус страниц без front matter и служебной разметки темы |
| [/ai/pages.jsonl](https://v8std.ru/ai/pages.jsonl) | JSONL-индекс страниц, алиасов, связей и очищенного Markdown |
| [/std/437.md](https://v8std.ru/std/437.md) | Пример очищенной Markdown-версии страницы; для публичных страниц используется исходный путь `.md` |

Страницы с front matter `llms.ignore: true` исключаются из всех публичных LLM-артефактов: `/llms.txt`, `/llms-full.txt` и `/ai/pages.jsonl`. Это не влияет на обычные страницы сайта и поиск Zensical.

Страницы с front matter llms.ignore: true исключаются из всех публичных LLM-артефактов: /llms.txt, /llms-full.txt и /ai/pages.jsonl. Это не влияет на обычные страницы сайта и поиск...

Поиск по сайту


### Нашли ошибку?

[Пожаловаться или предложить идею](https://github.com/zeegin/v8std/issues/new)

### Хотите внести исправление?

Можно просто нажать на иконку  `Редактировать` и прислать Pull Request.

Основной конфиг проекта: [`zensical.toml`](https://github.com/zeegin/v8std/blob/main/zensical.toml).

### Как развернуть локально?

Для локального запуска без Docker требуется `Python 3.12`. Zensical устанавливается из PyPI в зафиксированной версии.

#### mac
```bash
git clone https://github.com/zeegin/v8std.git
cd v8std

python3.12 -m venv .venv
source .venv/bin/activate

./scripts/install_zensical.sh
./scripts/zensical_docs.sh serve
```

#### windows

```bash
git clone https://github.com/zeegin/v8std.git
cd v8std

python3.12 -m venv .venv
source .venv/bin/activate

./scripts/install_zensical.sh
./scripts/zensical_docs.sh serve
```

Рекомендуется `WSL 2`: установочные и служебные скрипты репозитория написаны на Bash.

#### linux
```bash
git clone https://github.com/zeegin/v8std.git
cd v8std

python3.12 -m venv .venv
source .venv/bin/activate

./scripts/install_zensical.sh
./scripts/zensical_docs.sh serve
```

#### docker
```bash
git clone https://github.com/zeegin/v8std.git
cd v8std

docker compose -f docker-compose/docker-compose.yml up --build
```

Статическая сборка и проверка через `nginx`:

```bash
docker compose -f docker-compose/docker-compose.ngnix.yml up --build
```

Скрипт [`./scripts/install_zensical.sh`](https://github.com/zeegin/v8std/blob/main/scripts/install_zensical.sh) устанавливает зафиксированную версию Zensical из PyPI и Python-зависимости проекта из [`requirements.txt`](https://github.com/zeegin/v8std/blob/main/requirements.txt).

Скрипт [`./scripts/zensical_docs.sh`](https://github.com/zeegin/v8std/blob/main/scripts/zensical_docs.sh) перед `serve` и `build` обновляет временные social cards и AI-артефакты. Пример production-сборки:

```bash
./scripts/zensical_docs.sh build --strict
```

Документация будет доступна по адресу `http://127.0.0.1:8000`.

### Поиск и LLM-индексы

Форматы запросов описаны на странице [Поиск по сайту](https://v8std.ru/search-help/)

При `serve` и `build` автоматически генерируются статические AI-артефакты:

- [`/llms.txt`](https://v8std.ru/llms.txt) — компактная карта сайта для LLM
- [`/llms-full.txt`](https://v8std.ru/llms-full.txt) — очищенный полный Markdown-корпус
- [`/ai/pages.jsonl`](https://v8std.ru/ai/pages.jsonl) — индекс страниц, алиасов, связей и очищенного Markdown

Чтобы исключить страницу из публичных LLM-артефактов (`/llms.txt`, `/llms-full.txt`, `/ai/pages.jsonl`), добавьте в её front matter:

```yaml
llms:
  ignore: true
```

Это не влияет на обычную сборку сайта, навигацию и поиск Zensical.

Пожаловаться или предложить идею{ .md-button }

Поиск по сайту

Поддержка