docs: добавить стайлгайд nextjs-style-guide в репозиторий
- Добавлена документация SLM-архитектуры, базовых правил и прикладных разделов - Добавлены разделы: стили, SVG-спрайты, шаблоны генерации, PostCSS, REST, Realtime - Удалены устаревшие файлы (спрайты, скрипты, стили из app/)
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
---
|
||||
title: Business-композиция
|
||||
description: Когда REST-данные нужно объединить или интерпретировать в бизнес-модуле.
|
||||
keywords: [rest, business, композиция, hooks, domain, isAuth]
|
||||
---
|
||||
|
||||
# Business-композиция
|
||||
|
||||
Business-композиция используется, когда простого GET-метода или прозрачного GET-хука недостаточно: нужно объединить несколько источников, преобразовать DTO или вычислить доменное состояние.
|
||||
|
||||
## Когда использовать
|
||||
|
||||
- Нужно объединить несколько GET-запросов.
|
||||
- Нужно вычислить `isAuth`, `canEdit`, `hasAccess`, `hasPets`.
|
||||
- Нужно преобразовать DTO в доменную модель.
|
||||
- Нужно спрятать бизнес-сценарий за доменным API.
|
||||
|
||||
Такая логика не пишется в `infrastructure/`. REST-клиент остаётся прозрачным адаптером к API.
|
||||
|
||||
## Пример поверх одного GET-хука
|
||||
|
||||
```ts
|
||||
// src/business/pets/hooks/use-available-pets.hook.ts
|
||||
import { useGetPetList } from 'infrastructure/pet-store-api'
|
||||
|
||||
/**
|
||||
* Доменный список доступных питомцев.
|
||||
*/
|
||||
export const useAvailablePets = () => {
|
||||
const query = useGetPetList('available')
|
||||
|
||||
return {
|
||||
...query,
|
||||
hasPets: Boolean(query.data?.length),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`useGetPetList` — infrastructure-хук. `hasPets` — бизнес-интерпретация, поэтому она появляется в `business/pets`.
|
||||
|
||||
## Пример композиции нескольких GET-хуков
|
||||
|
||||
```ts
|
||||
// src/business/pets/hooks/use-pets-dashboard.hook.ts
|
||||
import { useGetPetList } from 'infrastructure/pet-store-api'
|
||||
|
||||
/**
|
||||
* Данные dashboard по питомцам.
|
||||
*/
|
||||
export const usePetsDashboard = () => {
|
||||
const availablePets = useGetPetList('available')
|
||||
const pendingPets = useGetPetList('pending')
|
||||
const soldPets = useGetPetList('sold')
|
||||
|
||||
return {
|
||||
availablePets,
|
||||
pendingPets,
|
||||
soldPets,
|
||||
total:
|
||||
(availablePets.data?.length ?? 0) +
|
||||
(pendingPets.data?.length ?? 0) +
|
||||
(soldPets.data?.length ?? 0),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Композиция нескольких запросов не добавляется в `infrastructure/pet-store-api/hooks/`, потому что это уже сценарий потребления данных.
|
||||
|
||||
## Пример auth-состояния
|
||||
|
||||
```ts
|
||||
// src/business/auth/hooks/use-auth-state.hook.ts
|
||||
import { useGetCurrentUser } from 'infrastructure/backend-api'
|
||||
|
||||
/**
|
||||
* Состояние авторизации текущего пользователя.
|
||||
*/
|
||||
export const useAuthState = () => {
|
||||
const currentUser = useGetCurrentUser()
|
||||
const user = currentUser.data
|
||||
|
||||
return {
|
||||
...currentUser,
|
||||
user,
|
||||
isAuth: Boolean(user),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`isAuth` не является частью REST-клиента. Это доменный смысл результата запроса.
|
||||
|
||||
## Где размещать
|
||||
|
||||
```text
|
||||
src/business/
|
||||
└── pets/
|
||||
├── hooks/
|
||||
│ └── use-available-pets.hook.ts
|
||||
├── mappers/
|
||||
│ └── map-pet-dto-to-pet.ts
|
||||
├── types/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Модуль `business/` экспортирует наружу готовый доменный API через `index.ts`.
|
||||
|
||||
## Что запрещено
|
||||
|
||||
```ts
|
||||
// Плохо — business-смысл внутри infrastructure-хука
|
||||
export const useGetPetList = (status: PetStatus) => {
|
||||
const query = useSWR(...)
|
||||
|
||||
return {
|
||||
...query,
|
||||
hasPets: Boolean(query.data?.length),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
REST-модуль отвечает за доступ к API. Business-модуль отвечает за смысл этих данных в продукте.
|
||||
Reference in New Issue
Block a user