← Назад к вопросам
Как связать в один проект Frontend на React, менеджер состояния, GraphQL и гексагональный сервер на Backend?
2.0 Middle🔥 221 комментариев
#React
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как связать Frontend на React, менеджер состояния, GraphQL и гексагональный сервер на Backend?
Архитектурная картина
Это вопрос о связывании нескольких компонентов системы в единое целое. Давай разберём, как они взаимодействуют.
1. Слой API (Communication)
Инициализация GraphQL клиента:
// lib/apollo-client.ts
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
const httpLink = new HttpLink({
uri: process.env.REACT_APP_GRAPHQL_URL || 'http://localhost:4000/graphql',
credentials: 'include', // Отправляем cookies
});
export const apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: { fetchPolicy: 'cache-first' },
query: { fetchPolicy: 'cache-first' },
},
});
2. Слой State Management (Zustand)
Zustand пример (более лёгкий):
// store/userStore.ts
import { create } from 'zustand';
import { apolloClient } from '@/lib/apollo-client';
interface User {
id: string;
name: string;
email: string;
}
interface UserStore {
user: User | null;
loading: boolean;
error: string | null;
fetchUser: (id: string) => Promise<void>;
updateUser: (id: string, input: Partial<User>) => Promise<void>;
}
export const useUserStore = create<UserStore>((set) => ({
user: null,
loading: false,
error: null,
fetchUser: async (id) => {
set({ loading: true, error: null });
try {
const response = await apolloClient.query({
query: GET_USER,
variables: { id },
});
set({ user: response.data.user, loading: false });
} catch (error: any) {
set({ error: error.message, loading: false });
}
},
updateUser: async (id, input) => {
set({ loading: true });
try {
const response = await apolloClient.mutate({
mutation: UPDATE_USER,
variables: { id, input },
});
set({ user: response.data.updateUser, loading: false });
} catch (error: any) {
set({ error: error.message, loading: false });
}
},
}));
3. Пользовательский слой (React компоненты)
Компонент с использованием state manager:
// components/UserProfile.tsx
import { useEffect } from 'react';
import { useUserStore } from '@/store/userStore';
interface UserProfileProps {
userId: string;
}
export function UserProfile({ userId }: UserProfileProps) {
const { user, loading, fetchUser, updateUser } = useUserStore();
useEffect(() => {
fetchUser(userId);
}, [userId]);
if (loading) return <div>Загрузка...</div>;
if (!user) return <div>Пользователь не найден</div>;
const handleUpdateName = async (newName: string) => {
await updateUser(userId, { name: newName });
};
return (
<div className="profile">
<h1>{user.name}</h1>
<p>{user.email}</p>
<button onClick={() => handleUpdateName('Новое имя')}>
Изменить имя
</button>
</div>
);
}
4. Backend (Гексагональный сервер)
GraphQL Resolver подключается к Domain через Use Cases:
// resolvers/user.resolver.ts
import { GetUserUseCase } from '@/application/use-cases/get-user.use-case';
export const userResolvers = {
Query: {
user: async (_, { id }, context) => {
const getUserUseCase = context.container.get(GetUserUseCase);
return await getUserUseCase.execute(id);
},
},
};
Use Case слой (бизнес-логика):
// application/use-cases/get-user.use-case.ts
import { UserRepository } from '@/domain/repositories/user.repository';
import { User } from '@/domain/entities/user';
export class GetUserUseCase {
constructor(private userRepository: UserRepository) {}
async execute(id: string): Promise<User> {
const user = await this.userRepository.findById(id);
if (!user) {
throw new Error('User not found');
}
return user;
}
}
Domain Layer (независимая от frameworks):
// domain/entities/user.ts
export interface User {
id: string;
name: string;
email: string;
createdAt: Date;
}
// domain/repositories/user.repository.ts
export interface UserRepository {
findById(id: string): Promise<User | null>;
update(id: string, data: Partial<User>): Promise<User>;
}
5. Полный поток данных
- Пользователь кликает кнопку в React Component
- Вызывается updateUser из store
- Store отправляет GraphQL Mutation через Apollo Client
- Backend получает GraphQL запрос
- Resolver вызывает UpdateUserUseCase
- UseCase изменяет Domain Entity через Repository
- Repository выполняет SQL запрос
- БД возвращает данные
- Данные идут обратно: Repository -> UseCase -> Resolver -> Apollo -> Store -> Component
- UI обновляется
Ключевые принципы интеграции
1. Разделение ответственности
- Компоненты отвечают за UI
- Store отвечает за состояние
- Apollo отвечает за сетевой слой
- Backend разделён на слои (Domain, Use Cases, Infrastructure)
2. Однонаправленный поток данных
- Компонент -> Store -> Apollo -> Backend
- Backend -> Apollo -> Store -> Компонент
3. Гексагональная архитектура на Backend
- Domain слой (независимый от фреймворков)
- Use Cases (бизнес-логика)
- Infrastructure (реализация репозиториев)
- GraphQL Resolver (порт в мир)
Инициализация приложения
import { ApolloProvider } from '@apollo/client';
import { apolloClient } from '@/lib/apollo-client';
export function App() {
return (
<ApolloProvider client={apolloClient}>
<UserProfile userId="123" />
</ApolloProvider>
);
}
Типичная ошибка
Неправильно - логика в компоненте:
function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
fetch('/api/user').then(setUser);
}, []);
}
Правильно - логика в store, компонент просто использует:
function UserProfile() {
const user = useUserStore(s => s.user);
useEffect(() => {
useUserStore.getState().fetchUser('123');
}, []);
}
Заключение
Интеграция всех компонентов - это вопрос разделения ответственности. Каждый слой:
- Знает только о слое ниже
- Не зависит от слоев сверху
- Имеет чётко определённые обязанности
Это обеспечивает масштабируемость, тестируемость и простоту поддержки кода.