Files
slm-design/docs/architecture/modules.md
S.Gromov 54b4060b6f feat: добавить лендинг, переписать документацию и унифицировать генерацию
- Добавлен лендинг на React + Vite с темой и карточками навигации
- Добавлен модуль темы (src/infra/theme) с поддержкой system/light/dark
- Документация переписана: разделы «Модули», «Сегменты», «Компонент»
- Добавлена страница навигации docs/index.md
- Генерация llms.txt переведена на парсинг сайдбара VitePress
- Описания для llms.txt вынесены в frontmatter (поле description)
- Удалена директория generated/, архив ZIP убран с лендинга
- Удалены английская документация, README_RU, concat-md.js
- Добавлен vite-плагин для UTF-8 заголовков текстовых артефактов
- Caddyfile обновлён: charset=utf-8 для llms.txt и ARCHITECTURE.md
2026-05-01 21:08:38 +03:00

12 KiB
Raw Blame History

title, description
title description
Модули Структура модуля, типы (UI, бизнес, инфра), публичный API, отличие модуля от компонента

Модули

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

Определение

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

Модуль может содержать всё, что нужно этой области: компоненты, вложенные модули, хуки, сторы, сервисы, типы, стили, конфиги и утилиты. Набор сегментов не фиксирован — модуль включает только то, что реально нужно.

Модуль не обязан быть UI-блоком. Это может быть страница, виджет, бизнес-домен, инфраструктурный сервис или UI-kit сущность.

Главная граница модуля — не папка, а ответственность.

Компонент

Компонент — презентационная единица модуля, которая находится только в ui/ своего родительского модуля и отвечает за отображение части интерфейса.

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

Компонент отображает. Модуль организует.

Компонент не может:

  • Импортировать код проекта за пределами родительского модуля.
  • Владеть архитектурными зависимостями.
  • Содержать любые компоненты.
  • Содержать любые модули.
  • Делать внешние запросы.
  • Самостоятельно получать данные.
  • Выбирать источник данных.
  • Композировать данные.
  • Вызывать сценарные хуки.
  • Оркестрировать сценарий.
  • Композировать модули.
  • Решать, как устроен процесс.
  • Содержать бизнес-логику.
  • Содержать сценарную логику.

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

auth/
├── ui/
│   └── logout-button/
│       ├── logout-button.tsx
│       ├── styles/
│       │   └── logout-button.module.css
│       ├── types/
│       │   └── logout-button-props.type.ts
│       └── index.ts
└── index.ts

Что считается модулем

Модулем считается папка, которая представляет самостоятельную область ответственности и имеет публичную границу.

Примеры модулей:

  • screens/home/ — модуль страницы.
  • widgets/page-heading/ — модуль виджета.
  • business/auth/ — модуль бизнес-домена.
  • infra/theme/ — модуль инфраструктурного сервиса.
  • ui/button/ — модуль UI-kit сущности.
  • screens/home/parts/hero-section/ — вложенный модуль страницы.

Не считаются модулями:

  • ui/, parts/, hooks/, types/, styles/, config/ — это сегменты.
  • screens/shop/, business/commerce/ — это группы, если в них нет index.ts.
  • screens/home/ui/user-card/ — это компонент, если он находится в ui/ и соблюдает ограничения компонента.

Типы модулей

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

UI-модуль

Модуль строится вокруг основного UI-компонента и обязан иметь основной .tsx файл в корне:

header/
├── header.tsx
└── index.ts

ui/ внутри такого модуля используется только для компонентов, которые помогают корневому .tsx файлу.

Бизнес-модуль

Бизнес-модуль — модуль, который строится вокруг публичного runtime API.

Бизнес-модуль обязан иметь фабрику в корне:

auth/
├── auth.factory.ts
├── index.ts
└── types/

Фабрика возвращает публичный runtime API модуля.

Инфраструктурный модуль

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

Инфраструктурный модуль не обязан иметь фиксированный корневой файл. Его структура определяется природой сервиса.

theme/
├── index.ts
├── config/
├── hooks/
├── styles/
└── ui/
backend-api/
├── backend-api.client.ts
├── config/
├── types/
└── index.ts

Структура

Модуль состоит из сегментов. Ни один сегмент не обязателен — модуль включает только те части, которые нужны его ответственности.

{module-name}/
├── {module-name}.factory.ts     # фабрика (для business-модулей)
├── {module-name}.tsx            # корневой файл модуля (опционален)
├── ui/                          # компоненты модуля
├── parts/                       # вложенные модули
├── hooks/                       # хуки
├── stores/                      # сторы состояния
├── services/                    # внешние источники данных
├── mappers/                     # трансформация данных между форматами
├── types/                       # типы
├── styles/                      # стили
├── lib/                         # утилиты модуля
├── config/                      # константы и конфигурация
└── index.ts                     # публичный API

Подробное описание сегментов — в разделе Сегменты.

Публичный API

Внешний код импортирует модуль только через публичный API.

// Хорошо
import { customerFactory } from '@/business/customer'
import type { Customer } from '@/business/customer'
// Плохо
import { validateToken } from '@/business/auth/lib/tokens'

index.ts модуля не обязан экспортировать всё содержимое. Он экспортирует только то, что действительно нужно снаружи.

Внутренние сегменты модуля остаются деталями реализации.

Business-модуль экспортирует из index.ts только фабрику и type-only экспорты. Хуки, компоненты, сервисы, мапперы и утилиты напрямую из index.ts не экспортируются — они доступны через API, который возвращает фабрика.

// business/customer/index.ts
export { customerFactory } from './customer.factory'

export type { Customer } from './types/customer.type'
export type { CustomerApi } from './types/customer-api.type'
export type { CustomerDeps } from './types/customer-deps.type'
export type { CustomerFactory } from './types/customer-factory.type'

Фабрика

Business-модуль всегда экспортирует фабрику. Фабрика лежит в корне модуля ({name}.factory.ts), типизируется через {Name}Factory и возвращает публичный runtime API модуля.

Всё, что нужно внешнему коду в runtime, должно быть частью API, который возвращает фабрика.

Модуль без cross-domain зависимостей экспортирует фабрику без аргументов. Модуль с зависимостями — фабрику, принимающую зависимости доменными именами. Типы всегда экспортируются напрямую через export typeimport type не является runtime-зависимостью.

Компоновка фабрик происходит на уровне модуля-потребителя: screen, layout, widget или любой другой модуль группы «Композиция».

Структура business-модуля

business/customer/
├── customer.factory.ts
├── index.ts
└── types/
    ├── customer.type.ts
    ├── customer-api.type.ts
    ├── customer-deps.type.ts
    └── customer-factory.type.ts

Типы

// business/customer/types/customer-api.type.ts
export type CustomerApi = {
  useCustomer: () => Customer
  CustomerCard: (props: CustomerCardProps) => ReactNode
}
// business/order/types/order-deps.type.ts
export type OrderDeps = {
  customer: Pick<CustomerApi, 'useCustomer'>
}
// business/order/types/order-factory.type.ts
export type OrderFactory = (deps: OrderDeps) => OrderApi

Фабрика без зависимостей

// business/customer/customer.factory.ts
import type { CustomerFactory } from './types/customer-factory.type'

export const customerFactory: CustomerFactory = () => {
  return {
    useCustomer,
    CustomerCard,
  }
}

Фабрика с зависимостями

// business/order/order.factory.ts
import type { OrderFactory } from './types/order-factory.type'

export const orderFactory: OrderFactory = (deps) => {
  return {
    useOrder,
    OrderCard,
  }
}

Композиция на уровне screen

// screens/home/home.screen.tsx
import { customerFactory } from '@/business/customer'
import { orderFactory } from '@/business/order'

const customer = customerFactory()
const order = orderFactory({ customer })

const { useOrder, OrderCard } = order

export const HomeScreen = () => {
  const currentOrder = useOrder()

  return <OrderCard order={currentOrder} />
}

Жизненный цикл

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

  • Нужен на одной странице → screens/{name}/parts/
  • Появился в 2+ местах → поднимается по природе:
    • абстрактный UI → ui/
    • блок с данными/логикой → widgets/
    • представление бизнес-домена → business/{area}/parts/

Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность.