Как реализовать модульную структуру проекта?
Модульная структура проекта в React Native — это способ организации кода таким образом, чтобы каждая функциональная часть (или "модуль") была изолирована, независима и могла масштабироваться отдельно от других. Такая структура особенно важна в больших и средних приложениях, поскольку она помогает лучше управлять зависимостями, упростить поддержку, повторное использование и тестирование компонентов.
Принципы модульной архитектуры
-
Функциональное разделение — каждый модуль отвечает за конкретную область бизнес-логики: пользователи, заказы, корзина, чат и т.д.
-
Инкапсуляция — модули должны изолировать внутренние детали от остального приложения, экспортируя только публичный API.
-
Переиспользуемость — модули могут быть перенесены в другой проект с минимальными изменениями.
-
Слабое зацепление (loose coupling) — модули не должны напрямую зависеть от реализации других модулей.
-
Явная зависимость — если модуль зависит от другого, это должно быть видно в коде (через импорт, DI и т.д.).
Пример базовой модульной структуры
/src
/modules
/auth
components/
screens/
hooks/
services/
authSlice.ts
index.ts
/products
components/
screens/
hooks/
services/
productsSlice.ts
index.ts
/cart
...
/shared
components/
utils/
constants/
theme/
/navigation
/store
App.tsx
Описание ключевых уровней
/modules
Каждый модуль — изолированный кусок функциональности, содержащий:
-
components/ — UI-компоненты (только этой фичи)
-
screens/ — экраны (используют внутренние компоненты и хук)
-
hooks/ — бизнес-логика в виде хуков (useLogin, useCartTotal)
-
services/ — API-клиенты, localStorage, asyncStorage
-
slice/ или state.ts — redux/zustand/observable хранилища, если нужно
-
index.ts — экспорт публичного API модуля
Преимущество: любой модуль можно выделить в npm-пакет или библиотеку.
/shared
Общие ресурсы, которые используются во всём приложении:
-
components/ — кнопки, заголовки, инпуты, иконки (атомарные или переиспользуемые)
-
utils/ — вспомогательные функции: формат даты, debounce, validation
-
constants/ — общие строки, статусы, цвета, типы данных
-
theme/ — общая тема, шрифты, отступы, цвета
/navigation
Навигационные стеки могут быть разбиты по модулям и объединены:
// /navigation/AppNavigator.tsx
import AuthNavigator from '../modules/auth/navigation';
import ProductNavigator from '../modules/products/navigation';
const AppNavigator = () => (
<NavigationContainer>
<Stack.Navigator>
{user ? (
<>
{ProductNavigator()}
</>
) : (
{AuthNavigator()}
)}
</Stack.Navigator>
</NavigationContainer>
);
/store
Глобальное хранилище, объединяющее состояния всех модулей (если используется Redux, Zustand или Jotai):
// /store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import authReducer from '../modules/auth/authSlice';
import productsReducer from '../modules/products/productsSlice';
export const store = configureStore({
reducer: {
auth: authReducer,
products: productsReducer,
},
});
Использование index.ts как публичного API
Каждый модуль экспортирует наружу только то, что необходимо другим модулям:
// /modules/auth/index.ts
export { default as AuthScreen } from './screens/AuthScreen';
export { useLogin, useRegister } from './hooks/useAuth';
export { authReducer } from './authSlice';
Это позволяет подключать модуль как «чёрный ящик», не зная внутренностей:
import { AuthScreen, useLogin } from '@/modules/auth';
Модульная маршрутизация
Каждый модуль может иметь собственный стек или навигацию:
// /modules/products/navigation/index.tsx
const ProductNavigator = () => (
<Stack.Navigator>
<Stack.Screen name="ProductList" component={ProductListScreen} />
<Stack.Screen name="ProductDetail" component={ProductDetailScreen} />
</Stack.Navigator>
);
Общие хуки для работы с данными
В каждом модуле можно создать useCase-хуки, объединяющие бизнес-логику:
// /modules/products/hooks/useProductList.ts
export function useProductList() {
const { data, isLoading } = useQuery('products', fetchProducts);
return { products: data ?? \[\], isLoading };
}
Используется в screens/ProductListScreen.tsx, не пересекается с другими модулями.
Архитектурные шаблоны, сочетаемые с модульной структурой
-
Feature-sliced design (FSD) — популярный паттерн, где каждый модуль = фича.
-
Clean Architecture — можно выделять слои: domain, data, presentation.
-
DDD (Domain Driven Design) — привязка структуры к бизнес-объектам и моделям.
Разделение по слоям внутри модуля
Например:
/modules/chat
/ui
ChatList.tsx
MessageBubble.tsx
/hooks
useSendMessage.ts
useMessages.ts
/services
chatApi.ts
chatStorage.ts
/model
types.ts
chatSlice.ts
index.ts
Это соответствует архитектуре UI / Hooks / Model / API, и позволяет минимизировать связи между слоями.
Интеграция зависимостей
Модуль может зависеть от:
-
API-клиентов (например, Axios, RTK Query)
-
Навигации (React Navigation)
-
Сторонних библиотек (Formik, Recoil и др.)
Лучше всего, чтобы зависимости были инкапсулированы в пределах модуля, или явно прокидывались через props/hooks.
Вариант для монорепозиториев
При необходимости масштабирования:
-
Выделяются модули в отдельные пакеты: packages/auth, packages/products
-
Используется Lerna, Turborepo, Nx
-
Появляется централизованный shared UI, theme, utils и т.п.
Модульная структура требует чёткого соблюдения границ модулей, осознанной зависимости и использования внутренних и публичных интерфейсов. Она помогает избежать «спагетти-кода», обеспечивает масштабируемость и совместную работу большой команды.