diff --git a/docs/docs/MAP.md b/docs/docs/MAP.md index 9cb1c18..f4d196a 100644 --- a/docs/docs/MAP.md +++ b/docs/docs/MAP.md @@ -63,4 +63,4 @@ - [Biome](./applied/biome.md) — Установка и настройка линтера-форматтера в новом проекте. - [PostCSS](./applied/postcss.md) — Установка и настройка CSS-процессора в новом проекте. - [VS Code](./applied/vscode.md) — Единые настройки редактора и расширений для команды. -- [Локализация](./applied/localization.md) — Как организовать локализацию как infrastructure-модуль. +- [Локализация](./applied/localization.md) — Как организовать локализацию как infra-модуль. diff --git a/docs/docs/applied/aliases.md b/docs/docs/applied/aliases.md index 056e7e3..8fce894 100644 --- a/docs/docs/applied/aliases.md +++ b/docs/docs/applied/aliases.md @@ -1,7 +1,7 @@ --- title: Алиасы импортов description: Какие алиасы импортов есть в проекте и как ими пользоваться. -keywords: [алиасы, aliases, paths, tsconfig, импорты, baseUrl, app, layouts, screens, widgets, business, infrastructure, ui, shared] +keywords: [алиасы, aliases, paths, tsconfig, импорты, baseUrl, app, layouts, screens, widgets, business, infra, ui, shared] --- # Алиасы импортов @@ -21,7 +21,7 @@ keywords: [алиасы, aliases, paths, tsconfig, импорты, baseUrl, app, "screens/*": ["./src/screens/*"], "widgets/*": ["./src/widgets/*"], "business/*": ["./src/business/*"], - "infrastructure/*": ["./src/infrastructure/*"], + "infra/*": ["./src/infra/*"], "ui/*": ["./src/ui/*"], "shared/*": ["./src/shared/*"] } diff --git a/docs/docs/applied/localization.md b/docs/docs/applied/localization.md index c65b7f6..6b58703 100644 --- a/docs/docs/applied/localization.md +++ b/docs/docs/applied/localization.md @@ -1,22 +1,22 @@ --- title: Локализация -description: Как организовать локализацию как infrastructure-модуль. +description: Как организовать локализацию как infra-модуль. --- # Локализация -Как организовать локализацию как infrastructure-модуль. +Как организовать локализацию как infra-модуль. ## Назначение Локализация — инфраструктурная подсистема приложения. Она отвечает за текущую локаль, словари, форматирование переводов и API для компонентов. -Код локализации живёт в `src/infrastructure/i18n/`. Компоненты и модули не читают словари напрямую — они используют публичный API infrastructure-модуля. +Код локализации живёт в `src/infra/i18n/`. Компоненты и модули не читают словари напрямую — они используют публичный API infra-модуля. ## Структура ```text -src/infrastructure/i18n/ +src/infra/i18n/ ├── config/ │ └── i18n.config.ts ├── dictionaries/ @@ -31,16 +31,16 @@ src/infrastructure/i18n/ └── index.ts ``` -Набор сегментов может отличаться, но публичная точка входа остаётся одна — `infrastructure/i18n`. +Набор сегментов может отличаться, но публичная точка входа остаётся одна — `infra/i18n`. ## Подключение -`app/` только подключает готовый провайдер локализации. Реализация провайдера, словари и конфиг остаются в `infrastructure/i18n/`. +`app/` только подключает готовый провайдер локализации. Реализация провайдера, словари и конфиг остаются в `infra/i18n/`. ```tsx // src/app/layout.tsx import type { ReactNode } from 'react' -import { I18nProvider } from 'infrastructure/i18n' +import { I18nProvider } from 'infra/i18n' type RootLayoutProps = { children: ReactNode @@ -62,7 +62,7 @@ export default function RootLayout({ children }: RootLayoutProps) { Компоненты получают переводы через готовый API модуля локализации: ```tsx -import { useTranslation } from 'infrastructure/i18n' +import { useTranslation } from 'infra/i18n' export const ProfileTitle = () => { const { t } = useTranslation() @@ -73,9 +73,9 @@ export const ProfileTitle = () => { ## Правила -- Локализация живёт в `infrastructure/i18n/`. +- Локализация живёт в `infra/i18n/`. - `app/` только подключает готовый provider и передаёт locale. - Словари не импортируются напрямую в компоненты, screens или business-модули. - Ключи переводов не собираются динамически из строк, если это ломает типизацию и поиск. - Тексты интерфейса не хардкодятся в переиспользуемых компонентах, если они должны переводиться. -- Форматирование дат, чисел и валют должно проходить через API локализации или отдельные утилиты infrastructure-модуля. +- Форматирование дат, чисел и валют должно проходить через API локализации или отдельные утилиты infra-модуля. diff --git a/docs/docs/applied/page-level.md b/docs/docs/applied/page-level.md index 0c3e7d1..2013f1d 100644 --- a/docs/docs/applied/page-level.md +++ b/docs/docs/applied/page-level.md @@ -25,7 +25,7 @@ description: Как работать со страницами и другими | Прогрев SWR-кеша, начальное состояние, подключение провайдеров | `src/app/**`, только через готовые обёртки из нижних слоёв | | UI страницы | `screens/` | | Каркас страницы: header, footer, sidebar | `layouts/` | -| Провайдеры, сторы, хуки, API-клиенты, сервисы | нижние слои (`screens/`, `business/`, `infrastructure/`, `shared/`) | +| Провайдеры, сторы, хуки, API-клиенты, сервисы | нижние слои (`screens/`, `business/`, `infra/`, `shared/`) | | CSS Modules и стили компонентов | рядом с компонентами, не в `src/app/**` | ## Что можно делать в `page.tsx` @@ -79,7 +79,7 @@ export default async function ProfilePage({ params }: ProfilePageProps) { ```tsx import { notFound } from 'next/navigation' -import { userApi } from 'infrastructure/backend-api' +import { userApi } from 'infra/backend-api' import { UserScreen } from 'screens/user' type UserPageProps = { @@ -109,7 +109,7 @@ import { backendApi, getCurrentUserKey, getPostListKey, -} from 'infrastructure/backend-api' +} from 'infra/backend-api' type FeedLayoutProps = { children: ReactNode diff --git a/docs/docs/applied/project-structure.md b/docs/docs/applied/project-structure.md index 75bc6fd..3262ae5 100644 --- a/docs/docs/applied/project-structure.md +++ b/docs/docs/applied/project-structure.md @@ -46,7 +46,7 @@ src/ ├── screens/ # Контент конкретной страницы ├── widgets/ # Составные блоки интерфейса, не привязанные к домену ├── business/ # Бизнес-домены (auth, catalog, orders) -├── infrastructure/ # Техсервисы (theme, i18n, API-адаптеры) +├── infra/ # Техсервисы (theme, i18n, API-адаптеры) ├── ui/ # UI-кит без бизнес-логики └── shared/ # Общие ресурсы (утилиты, типы, стили) ``` diff --git a/docs/docs/basics/architecture/index.md b/docs/docs/basics/architecture/index.md index 26c587f..544cc22 100644 --- a/docs/docs/basics/architecture/index.md +++ b/docs/docs/basics/architecture/index.md @@ -1,17 +1,18 @@ +--- +title: SLM Design +description: Назначение архитектуры, ключевые принципы и карта разделов документации +--- + # SLM Design Scoped Layered Module Design — модульная архитектура фронтенд-приложений. Код организован по слоям ответственности, а модуль содержит всё, что ему нужно: компоненты, хуки, сторы, типы, стили. -::: warning Локальная копия -Документация по архитектуре — локальная копия. Оригинал находится на сайте [slm-design.gromlab.ru](https://slm-design.gromlab.ru/). -::: - ## Разделы спецификации Спецификация SLM Design состоит из нескольких связанных разделов. Этот обзор даёт общий контекст, а детальные правила описаны дальше: -- [Слои](./layers.md) — уровни организации `src/`, направление зависимостей и зона ответственности каждого слоя. -- [Модули](./modules.md) — границы ответственности, публичный API, типы модулей и отличие модуля от компонента. -- [Сегменты](./segments.md) — внутренние папки модуля (`ui/`, `parts/`, `hooks/`, `types/` и другие) и правила размещения файлов. +- [Слои](/architecture/layers) — уровни организации `src/`, направление зависимостей и зона ответственности каждого слоя. +- [Модули](/architecture/modules) — границы ответственности, публичный API, типы модулей и отличие модуля от компонента. +- [Сегменты](/architecture/segments) — внутренние папки модуля (`ui/`, `parts/`, `hooks/`, `types/` и другие) и правила размещения файлов. Рекомендуемый порядок чтения: обзор → слои → модули → сегменты. diff --git a/docs/docs/basics/architecture/layers.md b/docs/docs/basics/architecture/layers.md index 59f15ee..92e7438 100644 --- a/docs/docs/basics/architecture/layers.md +++ b/docs/docs/basics/architecture/layers.md @@ -1,3 +1,8 @@ +--- +title: Слои +description: Иерархия слоёв от app до shared, правила зависимостей и зона ответственности каждого слоя +--- + # Слои Раздел описывает слои SLM: что такое слой, какие бывают, как между ними направлены зависимости и какие правила действуют на каждом. @@ -246,4 +251,4 @@ src/shared/ ### Требования -- Не имеет runtime-состояния \ No newline at end of file +- Не имеет runtime-состояния diff --git a/docs/docs/basics/architecture/modules.md b/docs/docs/basics/architecture/modules.md index 66afe3b..3edf889 100644 --- a/docs/docs/basics/architecture/modules.md +++ b/docs/docs/basics/architecture/modules.md @@ -1,3 +1,8 @@ +--- +title: Модули +description: Структура модуля, типы (UI, бизнес, инфра), публичный API, отличие модуля от компонента +--- + # Модули Раздел описывает модуль как границу ответственности в SLM: что считается модулем, что такое компонент внутри модуля и как модуль взаимодействует с остальным кодом. @@ -146,7 +151,7 @@ backend-api/ └── index.ts # публичный API ``` -Подробное описание сегментов — в разделе [Сегменты](./segments.md). +Подробное описание сегментов — в разделе [Сегменты](/architecture/segments). ## Публичный API @@ -281,4 +286,4 @@ export const HomeScreen = () => { - блок с данными/логикой → `widgets/` - представление бизнес-домена → `business/{area}/parts/` -Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность. \ No newline at end of file +Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность. diff --git a/docs/docs/basics/architecture/segments.md b/docs/docs/basics/architecture/segments.md index 7305e67..86cdea2 100644 --- a/docs/docs/basics/architecture/segments.md +++ b/docs/docs/basics/architecture/segments.md @@ -1,3 +1,8 @@ +--- +title: Сегменты +description: Сегменты внутри модуля (ui/, model/, lib/ и др.), назначение и правила размещения файлов +--- + # Сегменты Раздел описывает сегменты SLM: что такое сегмент, какие бывают и что в каждом из них лежит. @@ -37,7 +42,7 @@ - Не получает данные самостоятельно, не выбирает источник данных и не композирует данные. - Не содержит бизнес-логику или сценарную логику. -Если UI-сущности нужно что-то за пределами этих ограничений, она должна быть оформлена как модуль. Полная граница описана в разделе [Компонент](./modules.md#компонент). +Если UI-сущности нужно что-то за пределами этих ограничений, она должна быть оформлена как модуль. Полная граница описана в разделе [Компонент](/architecture/modules#компонент). Корневой файл модуля в `ui/` не размещается. Он лежит в корне модуля: `{module-name}.tsx`. @@ -173,4 +178,4 @@ lib/ config/ ├── routes.ts └── constants.ts -``` \ No newline at end of file +``` diff --git a/docs/docs/creating-project/manual.md b/docs/docs/creating-project/manual.md index 5fa9012..f5aa36d 100644 --- a/docs/docs/creating-project/manual.md +++ b/docs/docs/creating-project/manual.md @@ -25,7 +25,7 @@ keywords: [создать проект, новый проект, с нуля, in ## Канон раскладки -В `src/` допустимы только слои SLM: `app/`, `layouts/`, `screens/`, `widgets/`, `business/`, `infrastructure/`, `ui/`, `shared/`. Любая другая папка в `src/` — нарушение канона ([Структура проекта](/docs/applied/project-structure), [Архитектура](/docs/basics/architecture/)). +В `src/` допустимы только слои SLM: `app/`, `layouts/`, `screens/`, `widgets/`, `business/`, `infra/`, `ui/`, `shared/`. Любая другая папка в `src/` — нарушение канона ([Структура проекта](/docs/applied/project-structure), [Архитектура](/docs/basics/architecture/)). В частности: `src/app/` содержит только файлы роутинга Next.js и инициализации, без каталогов `styles/`, `assets/`, `components/`. @@ -85,6 +85,6 @@ CSS-процессор поверх базовых стилей: `@custom-media` - **Порядок шагов фиксирован.** Перестановка ломает зависимости (PostCSS требует базовых стилей, VS Code — установленного Biome). - **Между шагами обязательна проверка** из соответствующего раздела. Не переходить дальше, пока чеклист текущего шага не пройден. -- **Слои `src/`** (`layouts/`, `screens/`, `widgets/`, `business/`, `infrastructure/`, `ui/`) не создавать авансом. Появляются по мере первого модуля. Исключения — `src/app/` (создаётся `create-next-app`), `src/shared/styles/` (шаг 1) и `src/shared/sprites/icons/` (шаг 6). +- **Слои `src/`** (`layouts/`, `screens/`, `widgets/`, `business/`, `infra/`, `ui/`) не создавать авансом. Появляются по мере первого модуля. Исключения — `src/app/` (создаётся `create-next-app`), `src/shared/styles/` (шаг 1) и `src/shared/sprites/icons/` (шаг 6). - **Посторонние каталоги в `src/`** (`assets/`, `utils/`, `lib/`, `components/` и т.п.) — запрещены. - **Подмножество шагов допустимо.** Можно ставить только Next.js и часть инструментов; полный набор — это эталон, а не обязательство. diff --git a/docs/docs/creating-project/nextjs.md b/docs/docs/creating-project/nextjs.md index 34801cf..edc2302 100644 --- a/docs/docs/creating-project/nextjs.md +++ b/docs/docs/creating-project/nextjs.md @@ -89,7 +89,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) mkdir -p src/shared/styles ``` -Остальные слои (`layouts/`, `screens/`, `widgets/`, `business/`, `infrastructure/`, `ui/`) заводятся при появлении первого модуля в них. `src/shared/styles/` — единственный подкаталог `shared/`, который заводится сразу: без него не настроить стили на следующих шагах. +Остальные слои (`layouts/`, `screens/`, `widgets/`, `business/`, `infra/`, `ui/`) заводятся при появлении первого модуля в них. `src/shared/styles/` — единственный подкаталог `shared/`, который заводится сразу: без него не настроить стили на следующих шагах. ## Правила diff --git a/docs/docs/data/index.md b/docs/docs/data/index.md index a2682ad..da9c0c9 100644 --- a/docs/docs/data/index.md +++ b/docs/docs/data/index.md @@ -1,7 +1,7 @@ --- title: Источники данных description: Какие источники данных используются в проекте и как с ними работать. -keywords: [данные, api, rest, realtime, клиент, swr, infrastructure, введение, карта раздела] +keywords: [данные, api, rest, realtime, клиент, swr, infra, введение, карта раздела] --- # Источники данных @@ -10,7 +10,7 @@ keywords: [данные, api, rest, realtime, клиент, swr, infrastructure, ## Принципы раздела -- **Клиент — в `infrastructure/`.** Каждый внешний сервис — отдельный модуль слоя `infrastructure/{service-name}/`. +- **Клиент — в `infra/`.** Каждый внешний сервис — отдельный модуль слоя `infra/{service-name}/`. - **Прямой `fetch` запрещён.** Запросы идут только через клиент модуля. Исключения — точечные и обоснованные. - **Источник данных диктует канал.** REST, realtime и т.п. — независимые подразделы, у каждого своя модель клиента и своё потребление. - **Серверные и клиентские компоненты потребляют по-разному.** Server Components — прямой `await` метода клиента, клиентские — через готовые GET-хуки REST-клиента (`useGetUserList`, `useGetPostDetail` и т.п.). SWR инкапсулирован в хуке, компонент про него не знает. @@ -40,7 +40,7 @@ keywords: [данные, api, rest, realtime, клиент, swr, infrastructure, Канал push-данных: WebSocket, SSE, событийные шины. Транспорт не зашит в правила — важна абстракция «подписка». -- [Realtime](/docs/data/realtime) — клиент realtime в `infrastructure/`, потребление через `useSWRSubscription` или прямые подписки. +- [Realtime](/docs/data/realtime) — клиент realtime в `infra/`, потребление через `useSWRSubscription` или прямые подписки. ## Что даёт раздел @@ -48,7 +48,7 @@ keywords: [данные, api, rest, realtime, клиент, swr, infrastructure, - Где живёт код работы с API и почему именно там. - Когда генерировать клиент автоматически, а когда писать вручную, и как структурирован каждый из вариантов. -- Какие GET-хуки относятся к REST-клиенту и почему они живут в `infrastructure/{service-name}/hooks/`. +- Какие GET-хуки относятся к REST-клиенту и почему они живут в `infra/{service-name}/hooks/`. - Как выбрать стратегию получения REST-данных под конкретную ситуацию. - Как подключать realtime-источники в общую модель работы с данными. - Какие правила обязательны и какие отклонения допустимы. diff --git a/docs/docs/data/realtime.md b/docs/docs/data/realtime.md index 7463cfe..f284c2b 100644 --- a/docs/docs/data/realtime.md +++ b/docs/docs/data/realtime.md @@ -10,7 +10,7 @@ keywords: [realtime, websocket, sse, подписка, swr subscription, useSWRS ## Принципы -- **Клиент realtime — в `infrastructure/`** отдельным модулем по имени канала. То же правило, что и для REST: никаких прямых соединений в коде приложения. +- **Клиент realtime — в `infra/`** отдельным модулем по имени канала. То же правило, что и для REST: никаких прямых соединений в коде приложения. - **Подписка — единица потребления.** Клиент даёт функцию `subscribe(topic, handler) → unsubscribe`. Внутри — конкретный транспорт. - **Использование на клиенте — два сценария:** - **`useSWRSubscription`** — для данных, которые показываются в UI и должны кешироваться/синхронизироваться с REST. @@ -19,7 +19,7 @@ keywords: [realtime, websocket, sse, подписка, swr subscription, useSWRS ## Размещение клиента ```text -src/infrastructure/ +src/infra/ └── {channel-name}/ ├── connection.ts # установление соединения, реконнект ├── subscribe.ts # subscribe(topic, handler) → unsubscribe @@ -33,7 +33,7 @@ src/infrastructure/ 'use client' import useSWRSubscription from 'swr/subscription' -import { subscribe } from 'infrastructure/notifications' +import { subscribe } from 'infra/notifications' export function NotificationCounter() { const { data: count } = useSWRSubscription( @@ -56,7 +56,7 @@ export function NotificationCounter() { 'use client' import { useEffect } from 'react' -import { subscribe } from 'infrastructure/notifications' +import { subscribe } from 'infra/notifications' import { showToast } from 'ui/toast' export function NotificationsToaster() { @@ -74,6 +74,6 @@ export function NotificationsToaster() { ## Запрет прямых соединений -Создавать `new WebSocket(...)`, `new EventSource(...)` или подписываться на событийные шины напрямую в коде приложения — запрещено. Все соединения проходят через клиент в `infrastructure/`. +Создавать `new WebSocket(...)`, `new EventSource(...)` или подписываться на событийные шины напрямую в коде приложения — запрещено. Все соединения проходят через клиент в `infra/`. Исключения — точечные и обоснованные (например, диагностический скрипт), помечаются комментарием. diff --git a/docs/docs/data/rest/clients/auto.md b/docs/docs/data/rest/clients/auto.md index 41b79e8..d2093ef 100644 --- a/docs/docs/data/rest/clients/auto.md +++ b/docs/docs/data/rest/clients/auto.md @@ -19,7 +19,7 @@ https://petstore3.swagger.io/api/v3/openapi.json Имена модуля: ```text -src/infrastructure/pet-store-api/ +src/infra/pet-store-api/ petStoreApi pet-store-api.generated.ts ``` @@ -31,7 +31,7 @@ pet-store-api.generated.ts ```json { "scripts": { - "codegen:pet-store-api": "npx @gromlab/api-codegen@latest -i https://petstore3.swagger.io/api/v3/openapi.json -o src/infrastructure/pet-store-api/generated -n pet-store-api.generated" + "codegen:pet-store-api": "npx @gromlab/api-codegen@latest -i https://petstore3.swagger.io/api/v3/openapi.json -o src/infra/pet-store-api/generated -n pet-store-api.generated" } } ``` @@ -53,7 +53,7 @@ npm run codegen:pet-store-api Ожидаемый результат: ```text -src/infrastructure/pet-store-api/generated/ +src/infra/pet-store-api/generated/ └── pet-store-api.generated.ts ``` @@ -77,7 +77,7 @@ petStoreApi.pet.getPetById(...) Сгенерированный код не должен напрямую использоваться из приложения. Сначала создаётся настроенный инстанс клиента. ```ts -// src/infrastructure/pet-store-api/client.ts +// src/infra/pet-store-api/client.ts import { Api, HttpClient } from './generated/pet-store-api.generated' const httpClient = new HttpClient({ @@ -102,7 +102,7 @@ export const petStoreApi = new Api(httpClient) Сгенерированный файл не правится руками. Если OpenAPI-спецификация неполная или генератор дал слишком общий тип (`object`, `unknown`, отсутствующее поле), расширения живут в `types/`. ```text -src/infrastructure/biocad-less-api/ +src/infra/biocad-less-api/ ├── generated/ │ └── biocad-less-api.generated.ts ├── types/ @@ -115,7 +115,7 @@ src/infrastructure/biocad-less-api/ Пример расширения generated-типа: ```ts -// src/infrastructure/biocad-less-api/types/term.ts +// src/infra/biocad-less-api/types/term.ts import type { TermRecordItem } from '../generated/biocad-less-api.generated' declare module '../generated/biocad-less-api.generated' { @@ -149,7 +149,7 @@ export type TermRecordItemExtended = Omit< ``` ```ts -// src/infrastructure/biocad-less-api/types/index.ts +// src/infra/biocad-less-api/types/index.ts export type { TermRecordItemExtended } from './term' ``` @@ -158,18 +158,18 @@ export type { TermRecordItemExtended } from './term' ## Публичный API ```ts -// src/infrastructure/pet-store-api/index.ts +// src/infra/pet-store-api/index.ts export { petStoreApi } from './client' export type { Pet } from './generated/pet-store-api.generated' export * from './hooks' ``` -Наружу импортируют только из `infrastructure/pet-store-api`, не из `generated/`. +Наружу импортируют только из `infra/pet-store-api`, не из `generated/`. Если у модуля есть расширенные типы, они тоже реэкспортируются через `index.ts`: ```ts -// src/infrastructure/biocad-less-api/index.ts +// src/infra/biocad-less-api/index.ts export type { TermRecordItemExtended } from './types' ``` diff --git a/docs/docs/data/rest/clients/hooks.md b/docs/docs/data/rest/clients/hooks.md index ccd2e7c..c8fa121 100644 --- a/docs/docs/data/rest/clients/hooks.md +++ b/docs/docs/data/rest/clients/hooks.md @@ -1,7 +1,7 @@ --- title: GET-хуки REST-клиента description: Прозрачные SWR-обёртки над GET-методами REST-клиента. -keywords: [rest, swr, get-хуки, client components, infrastructure] +keywords: [rest, swr, get-хуки, client components, infra] --- # GET-хуки REST-клиента @@ -13,7 +13,7 @@ GET-хуки REST-клиента — прозрачные SWR-обёртки н GET-хуки принадлежат REST-клиенту конкретного сервиса и живут рядом с ним: ```text -src/infrastructure/ +src/infra/ └── pet-store-api/ ├── client.ts ├── generated/ @@ -41,7 +41,7 @@ src/infrastructure/ ## Пример списка ```ts -// src/infrastructure/pet-store-api/hooks/use-get-pet-list.hook.ts +// src/infra/pet-store-api/hooks/use-get-pet-list.hook.ts import useSWR from 'swr' import type { SWRConfiguration } from 'swr' import { petStoreApi } from '../client' @@ -74,7 +74,7 @@ import { SWRConfig, unstable_serialize } from 'swr' import { getPetListKey, petStoreApi, -} from 'infrastructure/pet-store-api' +} from 'infra/pet-store-api' export default function PetsLayout({ children }: { children: ReactNode }) { const petsPromise = petStoreApi.pet.findPetsByStatus({ status: 'available' }) @@ -102,7 +102,7 @@ const { data: pets } = useGetPetList('available') ## Пример detail-запроса ```ts -// src/infrastructure/pet-store-api/hooks/use-get-pet-detail.hook.ts +// src/infra/pet-store-api/hooks/use-get-pet-detail.hook.ts import useSWR from 'swr' import type { SWRConfiguration } from 'swr' import { petStoreApi } from '../client' @@ -141,23 +141,23 @@ const key = isReady ? getPetDetailKey(id) : null ## Экспорт ```ts -// src/infrastructure/pet-store-api/hooks/index.ts +// src/infra/pet-store-api/hooks/index.ts export { getPetListKey, useGetPetList } from './use-get-pet-list.hook' export type { PetStatus } from './use-get-pet-list.hook' export { getPetDetailKey, useGetPetDetail } from './use-get-pet-detail.hook' ``` ```ts -// src/infrastructure/pet-store-api/index.ts +// src/infra/pet-store-api/index.ts export { petStoreApi } from './client' export type { Pet } from './generated/pet-store-api.generated' export * from './hooks' ``` -## Где заканчивается infrastructure +## Где заканчивается infra ```ts -// Хорошо: infrastructure, прозрачный GET-хук +// Хорошо: infra, прозрачный GET-хук const { data: pets } = useGetPetList('available') ``` @@ -184,7 +184,7 @@ const { data } = useSWR( () => petStoreApi.pet.findPetsByStatus({ status }), ) -// Плохо — несколько GET внутри infrastructure-хука +// Плохо — несколько GET внутри infra-хука export const usePetDashboard = () => { const available = useGetPetList('available') const sold = useGetPetList('sold') diff --git a/docs/docs/data/rest/clients/index.md b/docs/docs/data/rest/clients/index.md index 4bc6587..d834ae6 100644 --- a/docs/docs/data/rest/clients/index.md +++ b/docs/docs/data/rest/clients/index.md @@ -1,12 +1,12 @@ --- title: Создание клиента description: Из чего состоит REST-клиент и какие части нужно подготовить перед использованием API. -keywords: [rest, клиент, infrastructure, методы, openapi, get-хуки, swr] +keywords: [rest, клиент, infra, методы, openapi, get-хуки, swr] --- # Создание клиента -REST-клиент — это infrastructure-модуль, через который проект работает с внешним REST API. +REST-клиент — это infra-модуль, через который проект работает с внешним REST API. На этом этапе нужно подготовить клиент сервиса: создать оболочку клиента, получить методы API и добавить GET-хуки для клиентских компонентов. @@ -57,7 +57,7 @@ GET-хуки именуются с префиксом `useGet`: `useGetPetList`, ## Структура модуля ```text -src/infrastructure/{service-name}/ +src/infra/{service-name}/ ├── client.ts # самописная оболочка и инстанс клиента ├── generated/ или methods/ # методы API ├── hooks/ # GET-хуки REST-клиента diff --git a/docs/docs/data/rest/clients/manual.md b/docs/docs/data/rest/clients/manual.md index d636cfa..d4f648c 100644 --- a/docs/docs/data/rest/clients/manual.md +++ b/docs/docs/data/rest/clients/manual.md @@ -1,7 +1,7 @@ --- title: Ручное создание description: Создание REST-клиента вручную, когда OpenAPI нет или он неполный. -keywords: [rest, ручной клиент, fetch, methods, dto, errors, infrastructure] +keywords: [rest, ручной клиент, fetch, methods, dto, errors, infra] --- # Ручное создание @@ -13,7 +13,7 @@ keywords: [rest, ручной клиент, fetch, methods, dto, errors, infrast ## Что нужно создать ```text -src/infrastructure/ +src/infra/ └── pet-project-api/ ├── methods/ │ └── posts.ts @@ -43,7 +43,7 @@ src/infrastructure/ DTO запросов и ответов живут в `types/`. `client.ts` не содержит DTO и доменные типы. ```ts -// src/infrastructure/pet-project-api/types/post.ts +// src/infra/pet-project-api/types/post.ts export type PostDto = { id: string slug: string @@ -57,14 +57,14 @@ export type PostListQueryDto = { ``` ```ts -// src/infrastructure/pet-project-api/types/index.ts +// src/infra/pet-project-api/types/index.ts export type { PostDto, PostListQueryDto } from './post' ``` Типы, которые нужны только базовому транспорту, можно держать отдельно: ```ts -// src/infrastructure/pet-project-api/types/client.ts +// src/infra/pet-project-api/types/client.ts export type QueryParams = Record ``` @@ -73,7 +73,7 @@ export type QueryParams = Record Ошибка API тоже относится к REST-модулю. ```ts -// src/infrastructure/pet-project-api/errors/pet-project-api.error.ts +// src/infra/pet-project-api/errors/pet-project-api.error.ts export class PetProjectApiError extends Error { constructor( public readonly status: number, @@ -90,7 +90,7 @@ export class PetProjectApiError extends Error { `client.ts` содержит только транспортную оболочку и сборку инстанса. Прямой `fetch` живёт здесь, а не в компонентах и не в методах верхних слоёв. ```ts -// src/infrastructure/pet-project-api/client.ts +// src/infra/pet-project-api/client.ts import { PetProjectApiError } from './errors/pet-project-api.error' import type { QueryParams } from './types/client' @@ -131,7 +131,7 @@ export class PetProjectApiClient { Методы группируются по сущностям в `methods/`. Они не знают про React, SWR и UI. ```ts -// src/infrastructure/pet-project-api/methods/posts.ts +// src/infra/pet-project-api/methods/posts.ts import type { PetProjectApiClient } from '../client' import type { PostDto, PostListQueryDto } from '../types/post' @@ -155,7 +155,7 @@ export function postsMethods(client: PetProjectApiClient) { `index.ts` собирает именованный API-объект и открывает наружу только публичные части модуля. ```ts -// src/infrastructure/pet-project-api/index.ts +// src/infra/pet-project-api/index.ts import { PetProjectApiClient } from './client' import { postsMethods } from './methods/posts' @@ -173,7 +173,7 @@ export type { PostDto, PostListQueryDto } from './types' export * from './hooks' ``` -Внешний код импортирует только из `infrastructure/pet-project-api`, не из внутренних файлов модуля. +Внешний код импортирует только из `infra/pet-project-api`, не из внутренних файлов модуля. ## Правила diff --git a/docs/docs/data/rest/index.md b/docs/docs/data/rest/index.md index 9c43715..0801025 100644 --- a/docs/docs/data/rest/index.md +++ b/docs/docs/data/rest/index.md @@ -1,7 +1,7 @@ --- title: REST description: Как правильно работать с REST API в проекте. -keywords: [rest, api, данные, infrastructure, клиент, swr, стратегии] +keywords: [rest, api, данные, infra, клиент, swr, стратегии] --- # REST @@ -15,7 +15,7 @@ REST в проекте проходит через два главных эта ## 1. Создание клиента -На этом этапе внешний API оформляется как модуль слоя `infrastructure/`. +На этом этапе внешний API оформляется как модуль слоя `infra/`. Клиент отвечает за: diff --git a/docs/docs/data/rest/strategies/business-composition.md b/docs/docs/data/rest/strategies/business-composition.md index 5234443..b9250e5 100644 --- a/docs/docs/data/rest/strategies/business-composition.md +++ b/docs/docs/data/rest/strategies/business-composition.md @@ -15,13 +15,13 @@ Business-композиция используется, когда просто - Нужно преобразовать DTO в доменную модель. - Нужно спрятать бизнес-сценарий за доменным API. -Такая логика не пишется в `infrastructure/`. REST-клиент остаётся прозрачным адаптером к API. +Такая логика не пишется в `infra/`. REST-клиент остаётся прозрачным адаптером к API. ## Пример поверх одного GET-хука ```ts // src/business/pets/hooks/use-available-pets.hook.ts -import { useGetPetList } from 'infrastructure/pet-store-api' +import { useGetPetList } from 'infra/pet-store-api' /** * Доменный список доступных питомцев. @@ -36,13 +36,13 @@ export const useAvailablePets = () => { } ``` -`useGetPetList` — infrastructure-хук. `hasPets` — бизнес-интерпретация, поэтому она появляется в `business/pets`. +`useGetPetList` — infra-хук. `hasPets` — бизнес-интерпретация, поэтому она появляется в `business/pets`. ## Пример композиции нескольких GET-хуков ```ts // src/business/pets/hooks/use-pets-dashboard.hook.ts -import { useGetPetList } from 'infrastructure/pet-store-api' +import { useGetPetList } from 'infra/pet-store-api' /** * Данные dashboard по питомцам. @@ -64,13 +64,13 @@ export const usePetsDashboard = () => { } ``` -Композиция нескольких запросов не добавляется в `infrastructure/pet-store-api/hooks/`, потому что это уже сценарий потребления данных. +Композиция нескольких запросов не добавляется в `infra/pet-store-api/hooks/`, потому что это уже сценарий потребления данных. ## Пример auth-состояния ```ts // src/business/auth/hooks/use-auth-state.hook.ts -import { useGetCurrentUser } from 'infrastructure/backend-api' +import { useGetCurrentUser } from 'infra/backend-api' /** * Состояние авторизации текущего пользователя. @@ -107,7 +107,7 @@ src/business/ ## Что запрещено ```ts -// Плохо — business-смысл внутри infrastructure-хука +// Плохо — business-смысл внутри infra-хука export const useGetPetList = (status: PetStatus) => { const query = useSWR(...) diff --git a/docs/docs/data/rest/strategies/client-get-hook.md b/docs/docs/data/rest/strategies/client-get-hook.md index 314d597..359b027 100644 --- a/docs/docs/data/rest/strategies/client-get-hook.md +++ b/docs/docs/data/rest/strategies/client-get-hook.md @@ -21,8 +21,8 @@ keywords: [rest, client components, swr, get-хук, client state] 'use client' import { useState } from 'react' -import { useGetPetList } from 'infrastructure/pet-store-api' -import type { PetStatus } from 'infrastructure/pet-store-api' +import { useGetPetList } from 'infra/pet-store-api' +import type { PetStatus } from 'infra/pet-store-api' const statuses: PetStatus[] = ['available', 'pending', 'sold'] @@ -60,7 +60,7 @@ export function PetTabs() { Хук добавляется в REST-модуль сервиса: ```text -src/infrastructure/pet-store-api/hooks/use-get-pet-list.hook.ts +src/infra/pet-store-api/hooks/use-get-pet-list.hook.ts ``` Не создавайте локальный `useSWR` в компоненте. diff --git a/docs/docs/data/rest/strategies/client-hooks-initial-data.md b/docs/docs/data/rest/strategies/client-hooks-initial-data.md index 98a72e7..2d9fe27 100644 --- a/docs/docs/data/rest/strategies/client-hooks-initial-data.md +++ b/docs/docs/data/rest/strategies/client-hooks-initial-data.md @@ -30,7 +30,7 @@ keywords: [rest, swr, fallback, initial data, client hooks, unstable_serialize, ## Ключ хука ```ts -// src/infrastructure/pet-store-api/hooks/use-get-pet-list.hook.ts +// src/infra/pet-store-api/hooks/use-get-pet-list.hook.ts export const getPetListKey = (status: PetStatus) => ['pet-store-api', 'pet', 'list', status] as const ``` @@ -46,7 +46,7 @@ import { SWRConfig, unstable_serialize } from 'swr' import { getPetListKey, petStoreApi, -} from 'infrastructure/pet-store-api' +} from 'infra/pet-store-api' type PetsLayoutProps = { children: ReactNode @@ -78,7 +78,7 @@ export default async function PetsLayout({ children }: PetsLayoutProps) { ```tsx 'use client' -import { useGetPetList } from 'infrastructure/pet-store-api' +import { useGetPetList } from 'infra/pet-store-api' export function PetList() { const { data: pets, isLoading } = useGetPetList('available') diff --git a/docs/docs/data/rest/strategies/parallel-server-requests.md b/docs/docs/data/rest/strategies/parallel-server-requests.md index 4470a89..b7375ae 100644 --- a/docs/docs/data/rest/strategies/parallel-server-requests.md +++ b/docs/docs/data/rest/strategies/parallel-server-requests.md @@ -17,7 +17,7 @@ keywords: [rest, promise.all, параллельные запросы, server co ## Хорошо ```tsx -import { petStoreApi } from 'infrastructure/pet-store-api' +import { petStoreApi } from 'infra/pet-store-api' import { PetsDashboardScreen } from 'screens/pets-dashboard' export default async function PetsDashboardPage() { diff --git a/docs/docs/data/rest/strategies/pass-promise-down.md b/docs/docs/data/rest/strategies/pass-promise-down.md index 9a1ac7f..d647d01 100644 --- a/docs/docs/data/rest/strategies/pass-promise-down.md +++ b/docs/docs/data/rest/strategies/pass-promise-down.md @@ -19,10 +19,10 @@ keywords: [rest, promise, suspense, streaming, server components] ```tsx // src/app/(routes)/pets/page.tsx import { Suspense } from 'react' -import { petStoreApi } from 'infrastructure/pet-store-api' +import { petStoreApi } from 'infra/pet-store-api' import { PetListSection } from 'widgets/pet-list-section' import { PetListSkeleton } from 'widgets/pet-list-section' -import type { Pet } from 'infrastructure/pet-store-api' +import type { Pet } from 'infra/pet-store-api' export default function PetsPage() { const petsPromise = petStoreApi.pet.findPetsByStatus({ status: 'available' }) diff --git a/docs/docs/data/rest/strategies/server-await.md b/docs/docs/data/rest/strategies/server-await.md index 850862b..1ee6d81 100644 --- a/docs/docs/data/rest/strategies/server-await.md +++ b/docs/docs/data/rest/strategies/server-await.md @@ -29,7 +29,7 @@ SSR/dynamic rendering нужен, когда данные зависят от т ```tsx // src/app/(routes)/pets/page.tsx -import { petStoreApi } from 'infrastructure/pet-store-api' +import { petStoreApi } from 'infra/pet-store-api' import { PetsScreen } from 'screens/pets' export default async function PetsPage() { @@ -48,7 +48,7 @@ export default async function PetsPage() { ```tsx // src/app/(routes)/pets/[id]/page.tsx import { notFound } from 'next/navigation' -import { petStoreApi } from 'infrastructure/pet-store-api' +import { petStoreApi } from 'infra/pet-store-api' import { PetDetailScreen } from 'screens/pet-detail' type PetPageProps = {