Перейти к содержанию

0014 — Структура продуктовых модулей в монорепозитории

  • Дата: 2026-08-22.
  • Статус: Принято пользователем.
  • Ответственный: владелец проекта.

Контекст

Решение 0012 определило продуктовые модули, тарифные module entitlements и права внутри модуля, но не закрепило физическое размещение кода. По мере роста SKUARO код одной функции мог быть разнесён по apps, packages и общим тестам, что усложнило бы управление модулями, правами и тарифами.

Решение

Обязательная классификация

До создания новой папки, пакета, сервиса или маршрута новая разработка обязательно классифицируется как:

  1. продуктовый модуль;
  2. общая возможность платформы;
  3. внешняя интеграция;
  4. общий технический пакет;
  5. runtime-application;
  6. инфраструктура.

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

Физическая структура

  • Продуктовые модули размещаются в modules/<module-id>.
  • Первый каталог — modules/today; маршрут модуля — /today.
  • Общие SaaS-возможности размещаются в platform/.
  • Адаптеры внешних систем размещаются в integrations/.
  • packages/ содержит только общие технические пакеты без бизнес-логики одного модуля.
  • apps/web, apps/api и apps/worker являются точками запуска и композиции, а не хранилищем модульной бизнес-логики.
  • infra/ остаётся общим контуром deploy, runtime, backup и reverse proxy.
  • Пути и идентификаторы используют английские kebab-case или принятые в коде имена; русские названия остаются в UI и документации.

Граница продуктового модуля

Каталог modules/<module-id> может содержать:

  • module.yaml с постоянным ID, UI-названием, lifecycle status, маршрутами, permissions и явными зависимостями;
  • contracts/, domain/, server/, web/, опциональные worker/ и database/;
  • tests/ для модульных проверок;
  • README.md и docs/ для связанного с кодом технического описания.

Необязательные папки заранее не создаются. Состав каталога отражает фактическую реализацию.

Связь и зависимости

  • Модуль раскрывает только явные публичные entrypoints и contracts.
  • Прямой импорт внутренних repository, domain implementation или таблиц другого модуля запрещён.
  • Межмодульная связь идёт через application services, contracts или события.
  • Циклические зависимости запрещены.
  • Общие SQL-миграции и OpenAPI собираются центральными runner/aggregator из модульных исходников.
  • Cross-module E2E и restore checks остаются в корневом tests/.

Интеграция как часть модуля

Внешняя система не считается продуктовым модулем автоматически. Например:

  • если Telegram — только канал доставки уведомлений, Bot API adapter живёт в integrations/telegram;
  • если Telegram-бот — отдельная тарифная функция со своими сценариями и permissions, бизнес-часть живёт в modules/telegram-bot, а транспорт — в integrations/telegram.

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

Последствия

  • Реализовано 2026-08-22: код today перенесён из apps/web и packages/ui в modules/today, а общие компоненты UI оставлены в packages/ui после классификации.
  • Общие Auth/Admin/Account capabilities размещены в platform/, Gmail adapter — в integrations/email; apps выполняют только композицию.
  • Введена автоматическая проверка границ импорта, публичных entrypoints, manifest и циклов; перенос проверяется unit, integration, E2E и visual tests.
  • Новый каталог продуктового модуля не создаётся до принятия его назначения, границ, интеграций и матрицы прав.

Откат или замена

Изменение верхнеуровневой классификации, отказ от modules/<module-id> или разрешение прямых межмодульных зависимостей требует нового ADR и плана миграции.