Тема
Иерархия и зависимости модулей
Статус: актуализировано 15.07.2026 по строкам
requires:/provides:всех 121 ТЗ (modules/*.md). Модули не изолированы — между ними обязательные связи и иерархия, которые ядро обязано знать, проверять и соблюдать. Проверено скриптом: циклов в графе нет, максимальная глубина цепочки от ядра — 6 (коммерция).Движок «Типы контента» (property-sets + связи + шаблоны + генерик-ресурс) — не модуль, а подсистема ядра; в графе requires не участвует как узел. Пресеты
blog/reviews/galleries/faq/bannersпланируются к переезду на него,faq— кандидат в пресет (модуль уже реализован в коде); ревизия каждого ТЗ-пресета предстоит, не свершившийся факт — engine §16.⚠️ Довнесено под корп-MVP (15.07.2026): 5 новых модулей —
cms/catalog-showcase,cms/section-patterns,cms/form-builder,cms/head-scripts,cms/ai-content— включены в таблицу слоёв (ярус графа сверен по фактическимrequires) и в ASCII-графы ниже; requires/приоритеты P0–P2 — источник build-layer-2 «Группа E». Граница «ядро vs модуль» — реестр критериев ядра §10; манифест и канонический реестр provides — стандарт §2.
Зачем это нужно
Принцип «всё — модуль» без управления зависимостями опасен:
- порядок загрузки: модуль A использует события модуля B — B должен загрузиться раньше;
- обязательные требования: модуль «Заказы» бессмысленен без «Каталога» и «Корзины»;
- каскад включения: включил «Магазин» → должны включиться «Каталог» + «Корзина» + «Заказы» + «Платежи»;
- каскад выключения/удаления: нельзя выключить «Каталог», пока включён «Заказы» (preflight disable — стандарт §3);
- конфликты версий: модуль требует ядро ≥ 2.0, а установлено 1.5 → блокировать.
Два уровня зависимостей
1. Composer-уровень (пакеты)
Технический слой — composer.json каждого модуля:
require: { "cms/core-contracts": "^2.0", "cms/commerce-catalog": "^1.3" }— composer сам решает граф, версии, конфликты, порядок автозагрузки провайдеров. Зависимость от ядра — только черезcms/core-contracts, никогдаcms/core(см. центр обновлений);minimum_core_versionв манифесте (сверх composer — для UI и понятных ошибок);- это бесплатно даёт Laravel/composer — не переизобретаем.
2. Домейн-уровень (включение/иерархия в рантайме)
Логический слой поверх composer — манифест extra.cms (стандарт §2):
| Поле манифеста | Смысл |
|---|---|
requires | Модули, без которых не работает (жёсткая зависимость) |
suggests | Модули, усиливающие функциональность (мягкая, опциональная) |
conflicts | Модули, несовместимые с этим (пример: cms/multisite ↔ cms/multitenancy — одновременное включение запрещено) |
provides | Контракт из канонического реестра (напр. payment-gateway); новый контракт — только ревизией ядра |
load_after | Порядок: загрузиться после указанных (для событий/хуков) |
minimum_core_version | Минимальная версия ядра для UI-проверки |
Реестр модулей ядра строит из деклараций граф зависимостей и:
- не даёт включить модуль без его
requires; - предлагает включить
suggests; - блокирует включение при
conflicts; - сортирует загрузку по
load_after(топологическая сортировка); - при выключении/удалении проверяет обратные зависимости (кто зависит от меня).
Правила направления стрелок (инварианты графа)
Проверяются CI-линтером манифестов; нарушение любого — блокер ревью:
- Ядро не требует ничего. Ни одной стрелки из ядра в модуль. Если способности ядра нужен модуль (поиск, гео, капча, сканер загрузок) — зависимость инвертируется через provides-контракт: ядро резолвит реализацию через DI, 0 реализаций = деградация.
- Циклы запрещены. Сейчас в графе requires циклов нет (проверено топологической сортировкой по всем 117 манифестам); появление цикла — ошибка проектирования, решается выделением общего контракта или события.
- Нижний слой не знает о верхнем.
cms/commerce-ordersне может требоватьcms/loyalty; связь «заказ начисляет бонусы» — событиеOrderPaid, которое слушает loyalty. Стрелка requires всегда направлена к более базовому модулю. - Альтернативные зависимости («ИЛИ») в requires не выражаются. Пример:
cms/seo-filterтребует источник фасетов —cms/commerce-facetsилиcms/services. Канон: оба — вsuggests, а self-test на enable (стандарт §3) проверяет наличие хотя бы одного и отказывает во включении с внятной ошибкой. requiresссылается на конкретные модули. Шорткат ТЗ «cms/commerce-model(заказы)» — ссылка на доменную модель, в манифесте он разворачивается в конкретный модуль слоя: «(заказы)» =cms/commerce-orders, «(каталог)» =cms/commerce-catalogи т.д.
Слои графа (фактическое состояние)
Ярус = максимальная глубина цепочки requires от ядра. Чем глубже модуль, тем длиннее каскад включения и тем позже он в порядке сборки.
Развёртка яруса 2 в рабочее ТЗ волны разработки — Слой 2 — ТЗ волны модулей.
| Ярус | Модули (по манифестам) |
|---|---|
| 0 — ядро | cms/core + cms/core-contracts + cms/testing — не требуют ничего |
| 1 — независимые | api-tokens, session, domains, email-templates, storage-s3, notifications-bus, multisite, multitenancy, onboarding, dev-panel, fleet-dashboard — requires: —, расширяют ядро только через контракты |
| 1 — от ядра | обязательная пятёрка (health, backup, sentry, attack-monitor; updates — ярус 2, см. ниже), integrations-bus, webhooks-in/out, realtime, search, moderation, весь корпсайт-контент (services, blog, faq, galleries, landings, reviews, …), SEO (seo-engine, seo-filter, multicity, multilang), auth-надстройки (oauth, two-factor, magic-login, password-policy), cabinet-b2c, commerce-catalog, корп-MVP на блоках ядра (section-patterns, catalog-showcase, form-builder, head-scripts — requires только ядро, см. build-layer-2 «Группа E») |
| 2–3 | потребители хабов (sms, telegram, web-push, dadata, cbr-rates, transport-providers, …), updates (← backup + health), cabinet-b2b, services-seo/services-axes, ugc (← moderation), chat (← realtime), commerce-pricing → commerce-cart, ai-content (← integrations-bus, сверх ядра — глубже яруса 1; P2, реализация отложена) |
| 4–6 | коммерческая цепочка: commerce-orders (4) → commerce-payments (5) → pay-gateways-ru, crypto-payments, commerce-receipts, commerce-rma, commerce-digital, commerce-subscriptions (6); loyalty (5) → referral (6); commerce-invoices (5) → edo (6) |
Хабы графа (in-degree по requires)
Модуль, который требуют многие, — квази-ядро своего домена: его публичные сервисы и события де-факто контракты. Правило: у хаба (≥ 5 входящих requires) изменение сигнатур/событий проходит по той же дисциплине, что ревизия ядра, — deprecation-окно минимум один минор, мажор только с миграцией потребителей.
| Хаб | Кто требует | Роль |
|---|---|---|
cms/integrations-bus — 16: calendars, cbr-rates, crypto-payments, dadata, delivery-ru, edo, erp, integration-crm/google/yandex/marketplaces, messengers, ofd, pay-gateways-ru, transport-providers, video-hosting | все внешние API | единая точка: креды, rate-limit, ретраи, журнал вызовов |
cms/commerce-orders — 10 (через шорткат «commerce-model (заказы)»): b2b, delivery, digital, payments, promo, receipts, rma, services, tax, vendor | коммерция | центр доменной модели магазина |
cms/commerce-payments — 6: digital, receipts, rma, subscriptions, crypto-payments, pay-gateways-ru | платёжный слой | шлюзы подключаются к нему через payment-gateway |
cms/notifications-bus — 6: messengers, mobile-api, notifications-inbox, sms, telegram, web-push | каналы уведомлений | роутер; ядро шлёт через NotificationDispatch с log-fallback без него |
cms/commerce-catalog — 5, cms/commerce-pricing — 5 | коммерция | базовые слои модели |
cms/webhooks-in — 4: integration-crm, pay-gateways-ru, telegram, messengers | приём внешних колбэков | единая точка подписи/верификации |
Граф по доменам
Стрелка A ◄── B = «B требует A».
Обязательная пятёрка managed-парка (стандарт §3) — все от ядра, updates дополнительно требует своих страховок:
ядро ◄── health ◄──┐
ядро ◄── backup ◄──┤── updates ◄── marketplace-modules
ядро ◄── sentry │
ядро ◄── attack-monitorИнфраструктура и каналы:
ядро ◄── integrations-bus ◄── dadata, cbr-rates, erp, ofd, calendars, video-hosting*,
│ transport-providers, integration-{1c,crm,google,yandex,marketplaces},
│ messengers*, delivery-ru*, pay-gateways-ru*, crypto-payments*, edo*,
│ ai-content (P2, реализация отложена)
├─── webhooks-in ◄── integration-crm, pay-gateways-ru, telegram, messengers
├─── realtime ◄── chat
└─── (без requires) notifications-bus ◄── sms**, telegram, web-push, messengers,
mobile-api, notifications-inbox
(без requires) storage-s3 ◄── video-hosting
* — вторая зависимость в другом домене ** — sms требует ещё transport-providersКорпоративный сайт (приоритет фазы 1 — все на расстоянии 1–2 от ядра, это осознанно):
ядро ◄── services ◄── services-seo ◄── services-axes
├─── blog (пресет), faq (пресет), galleries (пресет), landings, reviews (пресет),
│ comments, calculator, wizard, popups, banners (пресет), surveys,
│ related-content, scheduled-publishing
├─── корп-MVP, ярус 1 (Группа E, build-layer-2): section-patterns (P0),
│ catalog-showcase (P1), form-builder (P1), head-scripts (P1) — requires только
│ ядро, без commerce-*/друг друга
├─── seo-engine, seo-filter (фасеты: commerce-facets ИЛИ services — через suggests),
│ multicity, multilang, search
├─── oauth, two-factor, magic-login, password-policy ─ auth-надстройки
├─── cabinet-b2c ◄── cabinet-b2b
├─── consents ◄── esia cookie-consent ◄── pixels
└─── newsletter ◄── email-marketing ──► email-templates (без requires)Коммерция (фаза 3; самая глубокая ветка графа):
ядро ◄── commerce-catalog ◄── commerce-attributes ◄── commerce-facets
▲ ▲ ▲
│ │ └── commerce-stock ◄── integration-1c*, integration-marketplaces*
│ └── commerce-wishlist
└── commerce-pricing ◄── commerce-currencies ◄── (suggests) cbr-rates
▲
commerce-cart ─┘ ◄── commerce-abandoned
▲
commerce-orders ◄── commerce-invoices ◄── edo*
▲ ▲
│ └── loyalty ◄── referral (loyalty также требует pricing)
commerce-payments ◄── pay-gateways-ru*, crypto-payments*, commerce-digital,
▲ commerce-receipts, commerce-rma, commerce-subscriptions
│
(через «commerce-model (заказы)») commerce-b2b, commerce-delivery ◄── delivery-ru*,
commerce-promo ◄── coupons, commerce-services,
commerce-tax, commerce-vendor
* — вторая зависимость: integrations-bus / webhooks-in / importUGC и модерация: ядро ◄── moderation ◄── ugc; antifraud/antispam — от ядра, подключаются к потребителям контрактами fraud-signal/spam-filter.
Иерархия связей внутри ядра
Даже внутри ядра подсистемы связаны и порядок важен (состав — ТЗ ядра):
| Подсистема ядра | Зависит от | Используется |
|---|---|---|
События/фильтры (FilterBus) | — | всеми |
Движок полей (FieldTypeRegistry) | — | типы контента, формы, блоки, каталог |
Кеш-слой (CacheTags) + RequestContext | настройки | page-cache, все модули |
| API-слой | аутентификация, RBAC | мобильные, интеграции, кабинеты |
| Типы контента | движок полей | каталог, услуги, блог |
Страницы/блоки (BlockRegistry) | движок полей, ревизии | все контентные модули |
SEO-база + SitemapRegistry + RedirectService | страницы | seo-engine, seo-filter, multicity, все с URL |
Формы/заявки (LeadService) | движок полей, очереди | все модули с лид-формами |
Медиатека (MediaService) | хранилище (storage-backend) | все модули с изображениями |
NotificationDispatch | очереди | лиды, auth-события; расширяется notifications-bus |
PrivacyRegistry | — | cms:privacy:export/forget по всем модулям |
ScheduleRegistrar | планировщик | все модули с cron |
| Движок типов контента (property-sets + связи + шаблоны + генерик-ресурс) | движок полей | пресеты типа контента: blog, reviews, galleries, faq, новости, ленты (см. карта пресетов) |
Полный список контрактов — ТЗ ядра.
Граф и граница «ядро vs модуль»
Граф даёт проверяемые дополнения к тесту четырёх «да» (реестр критериев §10):
- направление стрелок: как только проектируемая способность ядра требует стрелку ядро → модуль — способность спроектирована неверно; либо она целиком уезжает в модуль, либо в ядре остаётся контракт (реестр/сервис), а реализация — в модуле;
- in-degree: если способность требуют ≥ 2 модулей из разных доменов — это аргумент за контракт в ядре (критерий 3 теста); если потребители из одного домена — это хаб домена (как
commerce-orders), не ядро; - глубина: способность на ярусе 1 (зависит только от ядра) — здоровый модуль; способность, которую пришлось бы класть на ярус 0, — кандидат в ядро только если проходит остальные три «да».
Санитария графа (проверка 14.07.2026)
- Циклов нет — в том числе потенциально опасная пара
updates ↔ backup/healthнаправлена в одну сторону (updates требует их, не наоборот). cms/geoip— фантом: упоминается в 4 ТЗ (multicity, session, cookie-consent) как реализацияgeo-provider, но в каталоге/реестре модуля нет — у контракта 0 реализаций. Деградация штатна (так и задумано), но для продакшена мультигорода нужен модуль — кандидат зафиксирован в открытых вопросах.cms/captcha— фантом: реальные реализацииcaptcha-provider—cms/integration-yandex(SmartCaptcha) иcms/integration-google(reCAPTCHA); упоминания «cms/captcha» в ТЗ читать как «любая реализация контракта».cms/commerce-model— шорткат ТЗ, не пакет: 10 commerce-модулей используют его как ссылку на доменную модель; в манифестах разворачивается в конкретные модули (правило 5 выше).
Метапакеты (наборы под типовой заказ)
Чтобы не собирать вручную десяток модулей на каждый заказ — composer metapackage, который через зависимости подтягивает набор. Канонические составы — в каталоге модулей; заявки, SEO-база и конструктор форм — ядро, в метапакеты не входят.
Метапакет = один composer require cms/preset-shop, дальше composer + граф разворачивают всё корректно. Глубина коммерческой ветки (6 ярусов) — ещё один довод за метапакеты: ручное включение цепочки из 6+ модулей в правильном порядке — источник ошибок, каскад enables делает это атомарно.
Что это даёт Claude Code
Граф зависимостей — не только рантайм-механизм, но и карта для AI-агента: по манифестам модулей Claude Code видит, что от чего зависит, что можно безопасно менять, а что затронет соседей. Хаб-модули (таблица выше) — сигнал агенту: изменение их публичных сервисов требует прохода по всем потребителям. См. удобство для Claude Code.
Связь с другими разделами
Механика деклараций — стандарт модуля §2–3 (манифест каноничен там; контракт модуля — обзор). Каскад включения/выключения и lifecycle (activate/deactivate/remove) — из исследования Botble. Порядок загрузки по load_after — из Drupal/TYPO3.