--- 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 } ``` ```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 } ``` ## Жизненный цикл Модуль рождается на самом низком уровне использования и поднимается выше только при реальной потребности. - Нужен на одной странице → `screens/{name}/parts/` - Появился в 2+ местах → поднимается по природе: - абстрактный UI → `ui/` - блок с данными/логикой → `widgets/` - представление бизнес-домена → `business/{area}/parts/` Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность.