Модули
Раздел описывает модули SLM: что такое модуль, из чего он состоит и как взаимодействует с остальным кодом.
Определение
Модуль — универсальный строительный блок архитектуры. Живёт на слое и содержит всё необходимое для своей работы: компоненты, хуки, сторы, сервисы, типы, стили. Набор содержимого не фиксирован — включаются только нужные части.
Модуль vs компонент
Компонент — один .tsx файл. Не имеет своих сегментов, использует сегменты родительского модуля. Живёт в корне или ui/ сегменте модуля.
Модуль — папка, которая может содержать корневой компонент, сегменты (hooks/, types/, styles/, ui/, parts/ и т.д.) и публичный API (index.ts).
auth/
├── ui/
│ ├── auth-guard.tsx
│ └── logout-button.tsx
├── parts/
│ ├── login-form/
│ ├── registration-form/
│ └── restore-form/
├── hooks/
├── stores/
├── types/
├── auth.tsx # корневой компонент (опционален)
└── index.tsСтруктура
Модуль состоит из сегментов. Ни один сегмент не обязателен — модуль может состоять даже из одного index.ts с реэкспортом типов.
{module-name}/
├── {module-name}.tsx # корневой компонент (опционален)
├── ui/ # компоненты модуля (только .tsx)
├── parts/ # вложенные модули (со своими сегментами)
├── hooks/ # хуки
├── stores/ # сторы состояния
├── services/ # внешние источники данных
├── mappers/ # трансформация данных между форматами
├── types/ # типы
├── styles/ # стили
├── lib/ # утилиты модуля
├── config/ # константы
└── index.ts # публичный APIПодробное описание каждого сегмента — в разделе Сегменты.
Публичный API
Модуль экспортирует наружу только то, что нужно другим. Всё остальное — внутреннее.
// business/auth/index.ts
export type { User, Session } from './types/user.types'
export { useAuth } from './hooks/use-auth.hook'
export { AuthGuard } from './ui/auth-guard'Импорт в обход index.ts запрещён:
// Плохо
import { validateToken } from '@/business/auth/lib/tokens'
// Хорошо
import { useAuth } from '@/business/auth'Фабрика
Если модуль зависит от кода другого бизнес-домена — он экспортирует фабрику. Фабрика декларирует необходимые зависимости и возвращает API модуля. Точка использования (screen, widget, layout) предоставляет зависимости при вызове.
Модуль без cross-domain зависимостей экспортирует API напрямую. Типы всегда экспортируются напрямую — import type не является runtime-зависимостью.
Модуль без зависимостей — прямой экспорт:
// business/auth/index.ts
export { useAuth } from './hooks/use-auth'
export { useCurrentUser } from './hooks/use-current-user'
export type { User, Session } from './types'Модуль с зависимостями — фабрика:
// business/chat/types/deps.ts
import type { User } from '@/business/auth'
export interface ChatDeps {
useCurrentUser: () => User | null
}// business/chat/index.ts
import type { ChatDeps } from './types/deps'
export function chatFactory(deps: ChatDeps) {
return {
useMessages: (roomId: string) => {
const user = deps.useCurrentUser()
// ...
},
useSendMessage: (roomId: string) => {
const user = deps.useCurrentUser()
return (text: string) => { /* ... */ }
},
useChatRooms: () => {
const user = deps.useCurrentUser()
// ...
},
ChatBadge: ({ count }: { count: number }) => { /* ... */ },
}
}
export type { Message, ChatRoom } from './types'
export type { ChatDeps } from './types/deps'Использование на странице:
// screens/support/support.tsx
import { useCurrentUser } from '@/business/auth'
import { chatFactory } from '@/business/chat'
const chat = chatFactory({ useCurrentUser })
export function SupportScreen() {
const { useMessages, useSendMessage, ChatBadge } = chat
const messages = useMessages('support')
const sendMessage = useSendMessage('support')
return (
<div>
<ChatBadge count={messages.length} />
{messages.map(m => <MessageBubble key={m.id} {...m} />)}
<MessageInput onSend={sendMessage} />
</div>
)
}Жизненный цикл
Модуль рождается на самом низком уровне использования и поднимается выше только при реальной потребности.
- Нужен на одной странице →
screens/{name}/parts/ - Появился в 2+ местах → поднимается по природе:
- абстрактный UI →
ui/ - блок с данными/логикой →
widgets/ - представление бизнес-домена →
business/{area}/parts/
- абстрактный UI →
Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность.