Как реализовать модульную структуру проекта?

Модульная структура проекта в React Native — это способ организации кода таким образом, чтобы каждая функциональная часть (или "модуль") была изолирована, независима и могла масштабироваться отдельно от других. Такая структура особенно важна в больших и средних приложениях, поскольку она помогает лучше управлять зависимостями, упростить поддержку, повторное использование и тестирование компонентов.

Принципы модульной архитектуры

  1. Функциональное разделение — каждый модуль отвечает за конкретную область бизнес-логики: пользователи, заказы, корзина, чат и т.д.

  2. Инкапсуляция — модули должны изолировать внутренние детали от остального приложения, экспортируя только публичный API.

  3. Переиспользуемость — модули могут быть перенесены в другой проект с минимальными изменениями.

  4. Слабое зацепление (loose coupling) — модули не должны напрямую зависеть от реализации других модулей.

  5. Явная зависимость — если модуль зависит от другого, это должно быть видно в коде (через импорт, 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()}
&lt;/&gt;
) : (
{AuthNavigator()}
)}
&lt;/Stack.Navigator&gt;
&lt;/NavigationContainer&gt;
);

/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 = () => (
&lt;Stack.Navigator&gt;
&lt;Stack.Screen name="ProductList" component={ProductListScreen} /&gt;
&lt;Stack.Screen name="ProductDetail" component={ProductDetailScreen} /&gt;
&lt;/Stack.Navigator&gt;
);

Общие хуки для работы с данными

В каждом модуле можно создать 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 и т.п.

Модульная структура требует чёткого соблюдения границ модулей, осознанной зависимости и использования внутренних и публичных интерфейсов. Она помогает избежать «спагетти-кода», обеспечивает масштабируемость и совместную работу большой команды.