138 lines
14 KiB
Markdown
138 lines
14 KiB
Markdown
# arch-05. Правила разработки модулей отдельными агентами
|
||
|
||
> Термины — в [`arch-00-glossary.md`](arch-00-glossary.md). Безопасность VM и production-деплоя — в [`arch-06-service-hosting-security.md`](arch-06-service-hosting-security.md). Настоящий документ описывает процесс разработки и не переопределяет архитектуру. Иерархия приоритета — в [`README.md`](README.md), раздел «Разрешение конфликтов».
|
||
|
||
## Цель
|
||
|
||
Этот документ задает единый процесс разработки, чтобы отдельные агенты создавали совместимые части приложения без расхождения архитектуры.
|
||
|
||
## Общие правила
|
||
- Каждый агент работает только в границах назначенного модуля.
|
||
- Перед разработкой агент читает [`README.md`](README.md), архитектурные документы и профильный документ назначенного модуля.
|
||
- Любое изменение публичного API сопровождается обновлением OpenAPI.
|
||
- Любое изменение структуры данных сопровождается миграцией.
|
||
- Все **бизнес-параметры** — в таблице `app_settings`; несекретная **infra** — в `.env`; production-секреты — только через механизм arch-06.
|
||
- Нельзя hardcode-ить телефоны, лимиты, тексты, mime types, feature flags и параметры Битрикс24.
|
||
- Модули, принимающие пользовательский ввод, должны учитывать rate limits и security/safety проверки.
|
||
|
||
## Размещение и production-деплой
|
||
|
||
- Изменение deployment, VM topology, network exposure, volumes, Linux capabilities, OS/sudo-прав или способа доставки секретов требует impact analysis по arch-06.
|
||
- Агент не добавляет `deploy` в группу `docker` и не расширяет sudo wildcard-командами. Новое право оформляется как конкретная операция над конкретным systemd-unit с review и rollback.
|
||
- Production compose, systemd-units, deploy scripts и secret mappings остаются root-owned и недоступны `deploy` на запись.
|
||
- Release содержит version-controlled manifest owner/group/mode; blanket
|
||
`F644` sync запрещён, executable scripts/hooks/preflight сохраняют `+x`.
|
||
- Изменение image digest повторяет runtime-проверки exact image: UID/GID,
|
||
entrypoint, writable paths, healthcheck binaries и one-shot init jobs.
|
||
- Изменение bind permissions, feature flag/edge allow-list, published ports
|
||
или systemd oneshot helper требует positive/negative production-like test,
|
||
а не только review YAML/shell.
|
||
- Для private/no-egress VM допустим временный bootstrap с SSH из trusted ops CIDR и ограниченным egress для пакетов/образов.
|
||
- Раскатка private/no-egress VM считается незавершённой, пока не выполнен lockdown: public ingress/SSH и общий egress закрыты, private access проверен, а недоступность снаружи зафиксирована.
|
||
- Повторное открытие ingress/egress после lockdown — документированная break-glass операция с обязательным возвратом в lockdown, а не штатный способ деплоя.
|
||
|
||
## Правила базы данных
|
||
|
||
- Перечень таблиц, полей, индексов и миграций **определяет модуль-владелец** (`database`, `api-backend`, `bitrix-sync`, `message-safety`, `bitrix-local-app`), а не arch-*.
|
||
- Архитектура фиксирует **разделение схем** и общие подходы к ведению баз данных, которые должны соблюдаться при проработке модулей.
|
||
- У каждой основной **прикладной** сущности должен быть `record_status`. Базовые статусы: `A` — active, `D` — deleted.
|
||
- `record_status` выражает только административное наличие строки. Доменное завершение (например `Notification.lifecycle_status='closed'`) хранится отдельно и не переводит запись в `D`.
|
||
- Физическое удаление строк прикладных сущностей запрещено. Если нужно удалить сущность, сервис меняет `record_status` с `A` на `D`.
|
||
- При смене статуса на `D` сервис обязан заполнить `status_changed_at` и `status_change_reason`.
|
||
- Все сервисы при чтении бизнес-данных по умолчанию запрашивают только `record_status = 'A'`.
|
||
- Исключения допускаются только для аудита, админки, технического восстановления и миграций.
|
||
- Прикладные сущности, имеют `id`, `created_at`, `updated_at`, `updater_user_id`.
|
||
- Системные таблицы (`app_settings`, `text_resources`, `popular_questions`, `sync_queue`, audit, справочники) могут использовать `user_id = NULL` или отдельное поле `actor_type` — по спецификации `module-01-api-backend` и migrations схемы `han_app`.
|
||
- Для **строковых enum из arch-00** (`Dialog.status`, `Message.safety_status`, `Message.delivery_status`, `sender_type`, `scan_status` и т.п.) справочник sequence **не** обязателен: значения фиксированы контрактом API.
|
||
- Для больших/изменяемых списков (типы документов post-MVP, причины, классификаторы UI) — справочники с ID (sequence) и расшифровкой.
|
||
- Для часто используемых фильтров добавляются индексы.
|
||
- Миграции не должны удалять данные без отдельного согласования.
|
||
- Все юзеры должны иметь ИД, которое указывается в `updater_user_id` которое они меняют.
|
||
|
||
|
||
## API
|
||
|
||
Правила:
|
||
|
||
- endpoint naming должен следовать `arch-02-api-contracts.md`;
|
||
- cross-VM contract test обязан запускать caller и callee как разные network zones: private DNS, verified internal CA, service token, timeout/circuit и запрет plaintext; Docker hostname вынесенного сервиса не считается валидным remote test;
|
||
- response schema не должна раскрывать внутренние поля;
|
||
- ошибки возвращаются в едином формате;
|
||
- для пользовательских данных всегда используется текущий user context из JWT;
|
||
- frontend не передает `client_profile_id` для доступа к своим данным;
|
||
- профиль в MVP не редактируется через `PATCH /me`;
|
||
- сообщения оператора должны приходить в frontend через realtime или polling fallback.
|
||
|
||
## Логирование и OTP
|
||
|
||
Каждый модуль должен:
|
||
|
||
- использовать общий формат JSON-логов из [`arch-07-observability.md`](arch-07-observability.md);
|
||
- добавлять `module`, `event`, `request_id`, `trace_id`;
|
||
- для `api-backend` добавлять **`ux_session_id`** в JSON-логи, если передан заголовок `X-Ux-Session-Id`;
|
||
- не логировать access token, refresh token, raw OTP, документы, полные PII;
|
||
- хранить факт отправки OTP через `provider_message_id`, `sent_at`, `destination_masked`, `otp_hash`, попытки и итог проверки.
|
||
|
||
Raw OTP запрещено хранить в открытом виде: это временный секрет. Доказательство отправки и проверки строится на аудите, delivery id провайдера и hash-проверке.
|
||
|
||
## Тесты
|
||
|
||
Минимум для каждого модуля:
|
||
|
||
- happy path;
|
||
- ошибки авторизации и доступа;
|
||
- rate limits, если модуль принимает пользовательский ввод;
|
||
- soft delete и фильтрация `record_status = 'A'`, если модуль работает с БД;
|
||
- idempotency, если операция может повториться;
|
||
- отсутствие секретов и PII в логах.
|
||
|
||
Дополнительно для модулей с async/worker:
|
||
|
||
- идемпотентность обработки задач очереди;
|
||
- поведение при повторной доставке webhook;
|
||
- таймауты и retry/backoff.
|
||
|
||
Для Notification Center обязательны contract tests каталога без ветвления по виду, бессрочной дедупликации `(source, external_id)`, изоляции producer tokens, TTL с сохранением существующего `date_expired`, первого скачивания любого связанного документа и открытия instruction только в новой вкладке.
|
||
|
||
## Definition of Done
|
||
|
||
Модуль считается готовым, если:
|
||
|
||
- реализованы сценарии из задачи;
|
||
- обновлен `{service}/openapi.yaml`, если менялся HTTP API;
|
||
- обновлены каталог ошибок в `arch-02` и contract tests, если менялась публичная или internal HTTP-семантика;
|
||
- созданы миграции, если менялась БД;
|
||
- обновлены seed `app_settings` и `.env.example`, если добавлялись настройки, service tokens, лимиты или feature flags;
|
||
- secret value не добавлен в `.env.example`; новый секрет включён только в runtime secret catalog и выдан минимальному набору сервисов;
|
||
- добавлены тесты;
|
||
- сервис запускается в root Docker Compose своей VM; CI отдельно валидирует оба projects и отсутствие cross-host `depends_on`/Docker DNS;
|
||
- remote Message Safety contract tests покрывают v2 `202 + Location + Retry-After`, sticky final result, terminal failed `503`, `409` invariant mapping и legacy v1 migration adapter;
|
||
- container проверен по arch-06: non-root, `no-new-privileges`, capabilities, read-only filesystem/writable paths, volumes, networks и resource limits;
|
||
- release manifest проверяет executable modes; bind files читаются реальным
|
||
container UID/GID, named volumes подготовлены ограниченным init-job;
|
||
- healthcheck выполнен внутри exact pinned digest, без несуществующих
|
||
вспомогательных binaries;
|
||
- published Docker ports проверены через live `DOCKER-USER` counters с учётом
|
||
DNAT; обновлённый active oneshot helper применён explicit restart;
|
||
- hooks/timers имеют корректный exit code и пустой stderr при успехе;
|
||
- feature flags, edge allow-lists и service readiness согласованы fail-closed;
|
||
- изменение embedded seed/schema выпущено как совместимая пара нового image
|
||
digest и новой монотонной config version с проверенным rollback;
|
||
- изменение прав `deploy`, systemd, network exposure, capabilities, volumes или secret delivery отражено в arch-06 и deployment runbook;
|
||
- для private/no-egress VM выполнен и зафиксирован bootstrap→lockdown checklist;
|
||
- worker, указанный в Compose/runbook, имеет реально зарегистрированный entrypoint в image; deployment не может заранее выдумывать имя команды;
|
||
- все изменяемые параметры вынесены из кода;
|
||
- логи содержат `request_id`, `trace_id` и **`ux_session_id`** (если передан в запросе);
|
||
- нет секретов, raw OTP и PII в логах;
|
||
- soft delete соблюден;
|
||
- агент указал, какие документы архитектуры были затронуты.
|
||
|
||
## Правила изменения архитектуры
|
||
|
||
Если агент видит, что текущая архитектура мешает задаче, он должен:
|
||
|
||
1. описать проблему;
|
||
2. предложить минимальное изменение;
|
||
3. указать затронутые документы;
|
||
4. не делать широкий рефакторинг без подтверждения.
|