DEV Community

ZeeshanAli-0704
ZeeshanAli-0704

Posted on

How do you structure Redux for a 200+ screen enterprise application?

Explanation in Simple Terms

Think of your app like a big office building with 200+ rooms (screens).

Redux is the central storage room where shared files are kept.

To avoid chaos:

  • Each floor = a large feature (Payments, Orders, Users)
  • Each room = a screen inside the feature
  • Each file cabinet = slice (collection of related state + reducers)
  • The main building manager = root store

You NEVER mix files from different floors.
Each floor manages its OWN files.
The building manager (store) just connects all floors.


🎯 Goal of Good Redux Structure

  • Easy to find state related to a feature
  • Easy to debug
  • Easy to scale (add more screens)
  • Shared logic goes into shared slices
  • Feature logic stays inside feature folders

⭐ Recommended Structure for 200+ Screens

/src
 ├── app/
 │    ├── store.js
 │    └── rootReducer.js
 │
 ├── features/
 │    ├── users/
 │    │    ├── api/
 │    │    │   └── usersApi.js     (RTK Query optional)
 │    │    ├── components/
 │    │    │   └── UserTable.jsx
 │    │    ├── pages/
 │    │    │   └── UserListPage.jsx
 │    │    ├── slices/
 │    │    │   └── usersSlice.js
 │    │    └── index.js
 │    │
 │    ├── payments/
 │    │    ├── slices/
 │    │    │    └── paymentSlice.js
 │    │    ├── pages/
 │    │    └── components/
 │    │
 │    ├── orders/
 │    ├── dashboard/
 │    └── ...
 │
 ├── shared/
 │    ├── slices/
 │    │    ├── authSlice.js        (login, roles)
 │    │    ├── uiSlice.js          (global loader, modals)
 │    │    └── appConfigSlice.js   (themes, global config)
 │    ├── hooks/
 │    └── utils/
 │
 ├── components/ (truly generic UI)
 └── index.js
Enter fullscreen mode Exit fullscreen mode

🧠 Why This Structure Works for Enterprise Apps?

✔ Every feature is isolated

Each feature folder contains everything it needs:

  • slice
  • pages
  • components
  • API logic

Makes deleting or moving a feature VERY easy.

✔ Store stays clean

Root store only cares about:

{
   users: usersReducer,
   payments: paymentReducer,
   orders: ordersReducer,
   auth: authReducer,
   ui: uiReducer
}
Enter fullscreen mode Exit fullscreen mode

✔ Shared logic stays in /shared

Only truly global state sits here.

✔ Very easy to scale

When a new feature arrives:

features/newFeature/
   slices/
   pages/
   components/
   api/
Enter fullscreen mode Exit fullscreen mode

No breaking changes.


🧩 Sample Enterprise Redux Store (Very Clean)

app/store.js

import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './rootReducer';

export const store = configureStore({
  reducer: rootReducer,
});
Enter fullscreen mode Exit fullscreen mode

app/rootReducer.js

import { combineReducers } from '@reduxjs/toolkit';

import usersReducer from '../features/users/slices/usersSlice';
import paymentReducer from '../features/payments/slices/paymentSlice';
import orderReducer from '../features/orders/slices/orderSlice';

import authReducer from '../shared/slices/authSlice';
import uiReducer from '../shared/slices/uiSlice';

export default combineReducers({
  users: usersReducer,
  payments: paymentReducer,
  orders: orderReducer,
  auth: authReducer,
  ui: uiReducer,
});
Enter fullscreen mode Exit fullscreen mode

🍰 Example Users Slice (Feature-level Slice)

src/features/users/slices/usersSlice.js
Enter fullscreen mode Exit fullscreen mode
import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  list: [],
  loading: false,
};

const usersSlice = createSlice({
  name: "users",
  initialState,
  reducers: {
    setUsers(state, action) {
      state.list = action.payload;
    },
    setLoading(state, action) {
      state.loading = action.payload;
    }
  },
});

export const { setUsers, setLoading } = usersSlice.actions;
export default usersSlice.reducer;
Enter fullscreen mode Exit fullscreen mode

📄 Example Feature Organization (Users)

/features/users
 ├── slices/
 │    └── usersSlice.js
 ├── api/
 │    └── usersApi.js  (RTK Query or axios calls)
 ├── pages/
 │    └── UserListPage.jsx
 ├── components/
 │    └── UserTable.jsx
 └── index.js
Enter fullscreen mode Exit fullscreen mode

This makes the feature self-contained, modular, portable.


🧠 Where should Async API calls go?

Option A: Inside slice using createAsyncThunk

Good for large enterprise apps that need unified loading/error handling.

Option B: RTK Query

Best for auto caching, invalidation, etc.

Option C: Custom hook in feature folder

If business logic is complex.


📌 Rules for Enterprise Redux

✔ RULE 1: NEVER put everything in one big store

Each feature = 1 slice.

✔ RULE 2: Pages should never call the store directly

Use hooks:

const users = useSelector((state) => state.users.list);
const dispatch = useDispatch();
Enter fullscreen mode Exit fullscreen mode

✔ RULE 3: Keep shared state small

Only:

  • auth
  • modals
  • notifications
  • app settings
  • global loader

✔ RULE 4: Use createSlice everywhere

Its cleaner, faster, less boilerplate.

✔ RULE 5: Keep features isolated

Feature folders should be independent.


If a feature like Users grows big, you absolutely can have multiple slices inside the same feature.

Let me explain it in the simplest, cleanest way.


Yes, you can have multiple slices inside src/features/users/slices/

Think of the feature like a mini-module.

Example:

src/features/users/
   slices/
      usersListSlice.js
      userDetailsSlice.js
      userPermissionsSlice.js
      userFiltersSlice.js
Enter fullscreen mode Exit fullscreen mode

Each slice manages its own independent part of the Users domain.


🧠 Why Use Multiple Slices?

Because enterprise apps often have:

  • Users list screen
  • User detail screen
  • User permissions screen
  • User role management
  • Filters, sorting, pagination
  • Local UI state (modal open/close)

Keeping all of this inside one huge usersSlice.js will turn it into a 4000-line monster.

Instead, you create small, focused slices.


🎯 Recommended Folder Structure for Multiple Slices

src/features/users/
 ├── slices/
 │    ├── usersListSlice.js
 │    ├── userDetailsSlice.js
 │    ├── userPermissionsSlice.js
 │    └── userFiltersSlice.js
 │
 ├── api/
 ├── pages/
 ├── components/
 └── index.js
Enter fullscreen mode Exit fullscreen mode

🧩 Combine All User Slices Into One "users" Reducer

Inside your feature folder, create:

src/features/users/index.js

import { combineReducers } from "@reduxjs/toolkit";

import usersListReducer from "./slices/usersListSlice";
import userDetailsReducer from "./slices/userDetailsSlice";
import userPermissionsReducer from "./slices/userPermissionsSlice";
import userFiltersReducer from "./slices/userFiltersSlice";

export default combineReducers({
  list: usersListReducer,
  details: userDetailsReducer,
  permissions: userPermissionsReducer,
  filters: userFiltersReducer,
});
Enter fullscreen mode Exit fullscreen mode

🏬 Now Your Root Reducer Uses Only One Key

Even though you have 4 slices, the store still sees them as ONE users domain:

rootReducer.js

import usersReducer from '../features/users';

export default combineReducers({
  users: usersReducer,  
  payments: paymentReducer,
  orders: orderReducer,
  auth: authReducer,
});
Enter fullscreen mode Exit fullscreen mode

📦 Final Redux State Looks Like This

{
  users: {
    list: { ... },
    details: { ... },
    permissions: { ... },
    filters: { ... }
  },
  payments: { ... },
  orders: { ... },
  auth: { ... }
}
Enter fullscreen mode Exit fullscreen mode

Clean.
Organized.
Enterprise ready.


🎁 Example Slice (usersListSlice.js)

import { createSlice } from "@reduxjs/toolkit";

const usersListSlice = createSlice({
  name: "usersList",
  initialState: {
    data: [],
    loading: false,
    error: null
  },
  reducers: {
    setUsers(state, action) {
      state.data = action.payload;
    },
    setLoading(state, action) {
      state.loading = action.payload;
    }
  }
});

export const { setUsers, setLoading } = usersListSlice.actions;
export default usersListSlice.reducer;
Enter fullscreen mode Exit fullscreen mode

Each slice stays small and readable.


🎯 When Should You Split Slices?

Split when a slice grows beyond 300–400 lines or handles different screens.

Examples:

Slice Purpose
usersListSlice list table, pagination, loading
userFiltersSlice search, filters, sorting
userDetailsSlice profile page, details API
userPermissionsSlice roles, permissions matrix

This is perfect for a 200+ screen enterprise app.


🚀 Summary (Very Simple)

✔ Yes, put multiple slices inside the same feature

✔ Combine them inside features/users/index.js

✔ Root reducer still uses only users:

✔ Clean and scalable

🏆 TL;DR (Easiest Summary)

Redux for 200+ screens should be structured like this:

  • app/ = store + root reducer
  • features/ = one folder per big feature
  • shared/ = global slices like auth/ui
  • pages/components/api inside each feature
  • each feature has its own slice (usersSlice, ordersSlice, etc.)

This makes the application:

  • Easy to scale
  • Easy to debug
  • Easy to onboard new developers
  • Modular and maintainable

Top comments (0)