Сущности — это базовые бизнес-единицы проекта. Данный раздел описывает унифицированную структуру любой сущности, основанную на строгих архитектурных правилах.
Каждая бизнес-сущность внутри src/entities/* обязана следовать жестко-заданной директориальной структуре для изоляции логики и UI.
Отвечает исключительно за сетевое взаимодействие с backend-приложением в рамках одной сущности. Принимает аргументы для запроса и возвращает сырые DTO, которые затем обязательно передаются в конвертеры.
Изучить архитектуру RTK Query1import { authApi } from "@/entities/auth/api/auth.api";
2
3export const bookingOrderApi = authApi.injectEndpoints({
4 endpoints: (builder) => ({
5 getBookingOrders: builder.query<TResponse, TFilters>({
6 query: (filters) => ({ url: "/booking/orders" }),
7 providesTags: [ENUM_API_TAGS.BOOKING_ORDERS]
8 })
9 })
10});Мапперы данных. Превращают сырой DTO от сервера в удобную Domain Model. Это изолирует UI-компоненты от внезапных изменений контрактов бекенда.
1export const mapBookingOrderToFrontend = (data: IBookingOrderBackend): IOrder => ({
2 orderId: data.order_id,
3 orderType: data.order_type as ENUM_ORDER_TYPE_OPTIONS_TYPE,
4 dateCreated: formatDate(data.date_created),
5 client: data.client,
6 status: data.status as ENUM_ORDER_STATUS_TYPE
7});
8
9export const mapBookingOrderToBackend = (data: Partial<IOrderDetail>): Partial<IBookingOrderDetailBackend> => ({
10 order_id: data.orderId,
11 order_type: data.orderType,
12 date_created: data.dateCreated,
13 client: data.client,
14 status: data.status
15});Обработчики MSW (Mock Service Worker). Используются для перехвата сетевых вызовов во время разработки и в интеграционных тестах (Vitest / RTL).
1import { HttpResponse, http } from "msw";
2import { ENV } from "@/shared/config";
3import { BOOKING_ORDERS_MOCK } from "../mock";
4
5export const bookingOrderHandlers = [
6 http.get(`${ENV.VITE_API_URL}/booking/orders`, ({ request }) => {
7 const url = new URL(request.url);
8 return HttpResponse.json({
9 data: [...BOOKING_ORDERS_MOCK],
10 total: BOOKING_ORDERS_MOCK.length
11 });
12 }),
13 http.get(`${ENV.VITE_API_URL}/booking/orders/:id`, ({ params }) => {
14 const order = BOOKING_ORDERS_MOCK.find((o) => o.orderId === params.id);
15 if (!order) return new HttpResponse(null, { status: 404 });
16 return HttpResponse.json(order);
17 })
18];Статические фикстуры и фейковые данные (mock data), используемые для эмуляции ответа сервера в связке с MSW handlers или для Storybook.
1import type { IOrderDetail } from "../types";
2
3export const BOOKING_ORDERS_MOCK: IOrderDetail[] = [
4 {
5 orderId: "ORD-001",
6 orderType: "individual",
7 dateCreated: "2024-03-15T12:00:00Z",
8 client: "John Doe",
9 pax: 2,
10 dates: { from: "2024-04-01", to: "2024-04-10" },
11 status: "CONFIRMED",
12 // ... full fields
13 }
14];Single Source of Truth для типов данных сущности. Содержит Zod-объекты со встроенной интернационализацией (i18n) ошибок.
1import { z } from "zod";
2import { i18nKey } from "@/shared/config";
3
4const msg = i18nKey<TBookingOrderKeys>();
5
6export const BOOKING_ORDER_SCHEMA = z.object({
7 client: z.string().min(1, { message: msg("errors.client.required") }),
8 status: z.enum(["PENDING", "CONFIRMED"]),
9 pax: z.number().min(1)
10});
11
12export type TBookingOrderSchema = z.infer<typeof BOOKING_ORDER_SCHEMA>;Хранит локальное состояние сущности (UI-слой), не связанное с кэшом сервера. Например: текущий выбранный элемент.
1import { createSlice, PayloadAction } from "@reduxjs/toolkit";
2
3interface IOrderSliceState {
4 selectedOrderId: string | null;
5}
6
7const initialState: IOrderSliceState = {
8 selectedOrderId: null
9};
10
11export const orderSlice = createSlice({
12 name: "bookingOrder",
13 initialState,
14 reducers: {
15 setSelectedOrder: (state, action: PayloadAction<string>) => {
16 state.selectedOrderId = action.payload;
17 }
18 }
19});Набор "глупых" (dumb) компонентов отображения, привязанных к данной сущности. Не содержат логики запросов.
1interface IOrderCardProps {
2 order: IOrder;
3 onSelect: (id: string) => void;
4}
5
6export const OrderCard = ({ order, onSelect }: IOrderCardProps) => (
7 <Card onClick={() => onSelect(order.orderId)}>
8 <CardHeader>{order.client}</CardHeader>
9 <CardContent>Pax: {order.pax}</CardContent>
10 </Card>
11);Каталог TypeScript интерфейсов, описывающих сущность. Подразделяется на *.interface.ts и *.types.ts.
1export interface IOrderDates {
2 from: string;
3 to: string;
4}
5
6export interface IOrder {
7 orderId: string;
8 orderType: ENUM_ORDER_TYPE_OPTIONS_TYPE;
9 dateCreated: string;
10 client: string;
11 pax: number;
12 dates: IOrderDates;
13 status: ENUM_ORDER_STATUS_TYPE;
14}