Skip to content

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-модуль отвечает за смысл этих данных в продукте.