Organizing Large Redux Projects

TL;DR: Split Redux projects by data/ui and match component structure with store shape.

Since we moved to Redux, our data management is finally properly split from our UI components. Now, data handling is no longer buried deep in the UI component files but split out in separated files for action types, reducers, actions, and selectors.

Organize by feature

Initially we kept the Redux files alongside the UI components, which we organize by feature area (e.g. users-related functionality is in /components/users/):

components/
users/
action-types.html
actions.html
reducers.html
selectors.html

user-ui.html
users-ui.html
...
groups/
action-types.html
actions.html
reducers.html
selectors.html

group-ui.html
group-members-ui.html
groups-ui.html
...
...
redux-store.html

This structure worked well as far as e.g. user-related functionality is grouped together — being it UI components or data logic. But the structure has also two major flaws:

  • Redux related functionality is not grouped: The Redux code has to interact across features, e.g. a group has members, which are modeled as users.
  • Data management for UI and for data model is mixed up: The users reducer manages a lookup table byId (i.e. access to the real data) as well as the ID of the currently viewed user. Whereas the currently viewed user is specific to the user UI (user-ui.html), the byId lookup table is widely used, e.g. by the group members view (group-members-ui.html), which itself only holds the IDs of the members.

Match UI components, Redux code, and state shape

To addressed these two issues, we re-organized the code by UI components vs. Redux and then by data vs. UI:

components/
users/
user-ui.html
users-ui.html
...
groups/
group-ui.html
group-members-ui.html
groups-ui.html
...
...
redux/
data/
users/

action-types.html
actions.html
reducers.html
selectors.html
groups/
action-types.html
actions.html
reducers.html
selectors.html
reducers.html
ui/
users/
action-types.html
actions.html
reducers.html
selectors.html
groups/
action-types.html
actions.html
reducers.html
selectors.html
reducers.html
reducers.html

redux-store.html

This structure makes navigating the code trivial:

  • Something UI related is wrong? Check the files in /redux/ui that match the UI component.
  • Something in the shared data model is wrong? Have a look to the files in /redux/data.

On the downside, the new structure leads to far more files. Partially, that’s actually a good thing because the files are also considerably smaller. To keep boilerplate to a minimum we heavily use factories that create similar reducers, selectors, etc. For example, the reducer for group members is created by a generic users reducers factory:

groupMemberReducer = createUsersReducers('members');

Finally, we also match the state shape to the directory structure. This allows to easily compose the reducers, e.g. the UI reducer brings together the reducers for the users and groups UI:

uiReducer = Redux.combineReducers({
users: uiUsersReducer,
groups: uiGroupsReducer

});

Happy coding!

Want to learn more about Polymer? Have a look to all our Medium posts on Polymer.