- Добавлен лендинг на 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
290 lines
12 KiB
Markdown
290 lines
12 KiB
Markdown
---
|
||
title: Модули
|
||
description: Структура модуля, типы (UI, бизнес, инфра), публичный API, отличие модуля от компонента
|
||
---
|
||
|
||
# Модули
|
||
|
||
Раздел описывает модуль как границу ответственности в SLM: что считается модулем, что такое компонент внутри модуля и как модуль взаимодействует с остальным кодом.
|
||
|
||
## Определение
|
||
|
||
**Модуль — минимальная архитектурная единица SLM. Он живёт на одном из слоёв, владеет конкретной областью ответственности и предоставляет наружу только публичный API.**
|
||
|
||
Модуль может содержать всё, что нужно этой области: компоненты, вложенные модули, хуки, сторы, сервисы, типы, стили, конфиги и утилиты. Набор сегментов не фиксирован — модуль включает только то, что реально нужно.
|
||
|
||
Модуль не обязан быть UI-блоком. Это может быть страница, виджет, бизнес-домен, инфраструктурный сервис или UI-kit сущность.
|
||
|
||
Главная граница модуля — не папка, а ответственность.
|
||
|
||
## Компонент
|
||
|
||
**Компонент — презентационная единица модуля, которая находится только в `ui/` своего родительского модуля и отвечает за отображение части интерфейса.**
|
||
|
||
Компонент не является архитектурной единицей: он не владеет сценарием, зависимостями, данными или внутренней структурой. Он работает только внутри границы родительского модуля.
|
||
|
||
> Компонент отображает. Модуль организует.
|
||
|
||
Компонент не может:
|
||
|
||
- Импортировать код проекта за пределами родительского модуля.
|
||
- Владеть архитектурными зависимостями.
|
||
- Содержать любые компоненты.
|
||
- Содержать любые модули.
|
||
- Делать внешние запросы.
|
||
- Самостоятельно получать данные.
|
||
- Выбирать источник данных.
|
||
- Композировать данные.
|
||
- Вызывать сценарные хуки.
|
||
- Оркестрировать сценарий.
|
||
- Композировать модули.
|
||
- Решать, как устроен процесс.
|
||
- Содержать бизнес-логику.
|
||
- Содержать сценарную логику.
|
||
|
||
Если компоненту требуется что-то из этого списка, он перестаёт быть компонентом и должен быть оформлен как модуль.
|
||
|
||
```text
|
||
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` файл в корне:
|
||
|
||
```text
|
||
header/
|
||
├── header.tsx
|
||
└── index.ts
|
||
```
|
||
|
||
`ui/` внутри такого модуля используется только для компонентов, которые помогают корневому `.tsx` файлу.
|
||
|
||
### Бизнес-модуль
|
||
|
||
Бизнес-модуль — модуль, который строится вокруг публичного runtime API.
|
||
|
||
Бизнес-модуль обязан иметь фабрику в корне:
|
||
|
||
```text
|
||
auth/
|
||
├── auth.factory.ts
|
||
├── index.ts
|
||
└── types/
|
||
```
|
||
|
||
Фабрика возвращает публичный runtime API модуля.
|
||
|
||
### Инфраструктурный модуль
|
||
|
||
Инфраструктурный модуль — модуль, который строится вокруг технического сервиса или интеграции.
|
||
|
||
Инфраструктурный модуль не обязан иметь фиксированный корневой файл. Его структура определяется природой сервиса.
|
||
|
||
```text
|
||
theme/
|
||
├── index.ts
|
||
├── config/
|
||
├── hooks/
|
||
├── styles/
|
||
└── ui/
|
||
```
|
||
|
||
```text
|
||
backend-api/
|
||
├── backend-api.client.ts
|
||
├── config/
|
||
├── types/
|
||
└── index.ts
|
||
```
|
||
|
||
## Структура
|
||
|
||
Модуль состоит из сегментов. Ни один сегмент не обязателен — модуль включает только те части, которые нужны его ответственности.
|
||
|
||
```text
|
||
{module-name}/
|
||
├── {module-name}.factory.ts # фабрика (для business-модулей)
|
||
├── {module-name}.tsx # корневой файл модуля (опционален)
|
||
├── ui/ # компоненты модуля
|
||
├── parts/ # вложенные модули
|
||
├── hooks/ # хуки
|
||
├── stores/ # сторы состояния
|
||
├── services/ # внешние источники данных
|
||
├── mappers/ # трансформация данных между форматами
|
||
├── types/ # типы
|
||
├── styles/ # стили
|
||
├── lib/ # утилиты модуля
|
||
├── config/ # константы и конфигурация
|
||
└── index.ts # публичный API
|
||
```
|
||
|
||
Подробное описание сегментов — в разделе [Сегменты](/architecture/segments).
|
||
|
||
## Публичный API
|
||
|
||
Внешний код импортирует модуль только через публичный API.
|
||
|
||
```ts
|
||
// Хорошо
|
||
import { customerFactory } from '@/business/customer'
|
||
import type { Customer } from '@/business/customer'
|
||
```
|
||
|
||
```ts
|
||
// Плохо
|
||
import { validateToken } from '@/business/auth/lib/tokens'
|
||
```
|
||
|
||
`index.ts` модуля не обязан экспортировать всё содержимое. Он экспортирует только то, что действительно нужно снаружи.
|
||
|
||
Внутренние сегменты модуля остаются деталями реализации.
|
||
|
||
Business-модуль экспортирует из `index.ts` только фабрику и type-only экспорты. Хуки, компоненты, сервисы, мапперы и утилиты напрямую из `index.ts` не экспортируются — они доступны через API, который возвращает фабрика.
|
||
|
||
```ts
|
||
// 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 type` — `import type` не является runtime-зависимостью.
|
||
|
||
Компоновка фабрик происходит на уровне модуля-потребителя: screen, layout, widget или любой другой модуль группы «Композиция».
|
||
|
||
### Структура business-модуля
|
||
|
||
```text
|
||
business/customer/
|
||
├── customer.factory.ts
|
||
├── index.ts
|
||
└── types/
|
||
├── customer.type.ts
|
||
├── customer-api.type.ts
|
||
├── customer-deps.type.ts
|
||
└── customer-factory.type.ts
|
||
```
|
||
|
||
### Типы
|
||
|
||
```ts
|
||
// business/customer/types/customer-api.type.ts
|
||
export type CustomerApi = {
|
||
useCustomer: () => Customer
|
||
CustomerCard: (props: CustomerCardProps) => ReactNode
|
||
}
|
||
```
|
||
|
||
```ts
|
||
// business/order/types/order-deps.type.ts
|
||
export type OrderDeps = {
|
||
customer: Pick<CustomerApi, 'useCustomer'>
|
||
}
|
||
```
|
||
|
||
```ts
|
||
// business/order/types/order-factory.type.ts
|
||
export type OrderFactory = (deps: OrderDeps) => OrderApi
|
||
```
|
||
|
||
### Фабрика без зависимостей
|
||
|
||
```ts
|
||
// business/customer/customer.factory.ts
|
||
import type { CustomerFactory } from './types/customer-factory.type'
|
||
|
||
export const customerFactory: CustomerFactory = () => {
|
||
return {
|
||
useCustomer,
|
||
CustomerCard,
|
||
}
|
||
}
|
||
```
|
||
|
||
### Фабрика с зависимостями
|
||
|
||
```ts
|
||
// business/order/order.factory.ts
|
||
import type { OrderFactory } from './types/order-factory.type'
|
||
|
||
export const orderFactory: OrderFactory = (deps) => {
|
||
return {
|
||
useOrder,
|
||
OrderCard,
|
||
}
|
||
}
|
||
```
|
||
|
||
### Композиция на уровне screen
|
||
|
||
```tsx
|
||
// 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/`
|
||
|
||
Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность.
|