Entities are the core business units of the project. This section describes the unified structure of any entity based on strict architectural rules.
Each business entity inside src/entities/* must follow a strict directory structure for logic and UI isolation.
A complete entity consists of these required modules:
Responsible exclusively for network interaction with the backend application within a single entity. It takes arguments for the request and returns raw DTOs, which must then be passed to converters.
1import { mainApi } from "@/shared/api";
2
3export const entityApi = mainApi.injectEndpoints({
4 endpoints: (builder) => ({
5 getEntities: builder.query<TResponse, TFilters>({
6 query: (filters) => ({ url: "/entities" }),
7 providesTags: [ENUM_API_TAGS.ENTITIES]
8 })
9 })
10});Data mappers. They transform a raw server DTO into a convenient Domain Model. This isolates UI components from sudden changes in backend contracts.
1export const mapEntityToFrontend = (data: IEntityBackend): IEntity => ({
2 id: data.id,
3 type: data.type as ENUM_ENTITY_TYPE,
4 dateCreated: formatDate(data.date_created),
5 client: data.client,
6 status: data.status as ENUM_ENTITY_STATUS
7});
8
9export const mapEntityToBackend = (data: Partial<IEntityDetail>):
10 Partial<IEntityDetailBackend> => ({
11 id: data.id,
12 type: data.type,
13 date_created: data.dateCreated,
14 client: data.client,
15 status: data.status
16});
17MSW (Mock Service Worker) handlers. Used to intercept network calls during development and in integration tests (Vitest / RTL).
1import { HttpResponse, http } from "msw";
2import { ENV } from "@/shared/config";
3import { ENTITIES_MOCK } from "../mock";
4
5export const entityHandlers = [
6 http.get(`${ENV.VITE_API_URL}/entities`, ({ request }) => {
7 const url = new URL(request.url);
8 return HttpResponse.json({
9 data: [...ENTITIES_MOCK],
10 total: ENTITIES_MOCK.length
11 });
12 }),
13 http.get(`${ENV.VITE_API_URL}/entities/:id`, ({ params }) => {
14 const entity = ENTITIES_MOCK.find((e) => e.id === params.id);
15 if (!entity) return new HttpResponse(null, { status: 404 });
16 return HttpResponse.json(entity);
17 })
18];Static fixtures and fake data (mock data) used to emulate server responses in conjunction with MSW handlers or for Storybook.
1import type { IEntityDetail } from "../types";
2
3export const ENTITIES_MOCK: IEntityDetail[] = [
4 {
5 id: "ENT-001",
6 type: "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 for entity data types. Contains Zod objects with built-in internationalization (i18n) of errors.
1import { z } from "zod";
2import { i18nKey } from "@/shared/config";
3
4const msg = i18nKey<TEntityKeys>();
5
6export const ENTITY_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 TEntitySchema = z.infer<typeof ENTITY_SCHEMA>;Redux state of the entity (if it stores global data). Selectors and actions often reside here as well. Note: state should depend solely on the Domain Model, not on DTOs.
1import { createSlice, PayloadAction } from "@reduxjs/toolkit";
2
3interface IEntitySliceState {
4 selectedId: string | null;
5}
6
7const initialState: IEntitySliceState = {
8 selectedId: null
9};
10
11export const entitySlice = createSlice({
12 name: "entity",
13 initialState,
14 reducers: {
15 setSelectedEntity: (state, action: PayloadAction<string>) => {
16 state.selectedId = action.payload;
17 }
18 }
19});Dump or Smart components at the entity level. For example: UserCard, UserAvatar. Can be reused across different features and widgets.
1interface IEntityCardProps {
2 entity: IEntity;
3 onSelect: (id: string) => void;
4}
5
6export const EntityCard = ({ entity, onSelect }: IEntityCardProps) => (
7 <Card onClick={() => onSelect(entity.id)}>
8 <CardHeader>{entity.client}</CardHeader>
9 <CardContent>Pax: {entity.pax}</CardContent>
10 </Card>
11);Global TS interfaces and types for a specific entity (e.g., Domain Model interface if Zod doesn't cover all logic). Type inference from Zod schemas is recommended, but when not possible, types are defined here.
1export interface IEntityDates {
2 from: string;
3 to: string;
4}
5
6export interface IEntity {
7 id: string;
8 type: ENUM_ENTITY_TYPE;
9 dateCreated: string;
10 client: string;
11 pax: number;
12 dates: IEntityDates;
13 status: ENUM_ENTITY_STATUS;
14}