Back to articles

Architecting React Apps with Redux, Sagas & Services

Structuring Redux, Sagas, and Services for Scalable React Applications

May 11, 2019
Diagram showing a scalable React application architecture with Redux, Sagas, and Services folders
react
redux
architecture
web development
6 min read

This article is a continuation of my previous article - Architecting React Applications - where I wrote about a simple way to architect almost any React application into a modular structure. In this article, I am going to write about a relatively complex codebase with things such as application state management.

We’ll build upon the same directory structure so that we can also determine whether our previously prepared codebase scales well in more complex scenarios rather than just having a few pages or components. We’ll follow the same steps, .i.e, take a look at the directory and then briefly go through the parts one by one.


Setting Up the Basic Redux Structure

Let’s add some of the redux’s magic to our application to manage its global state. ✨

But wait, we need to get the structure ready first. So, here we go —

The initial source directory structure for our React application with basic Redux folders (actions, reducers, middlewares)

Directory Organization Rationale

This structure might seem familiar to you, and this one of the most popular ways among the developers and is pretty intuitive. All the actions go into a directory called actions, reducers in their directory, and the same for middlewares. One thing that is not very common here is a root.reducer and root.store file at the src root. Now, many developers I have known to prefer keeping the root.reducer ( sometimes stored as an index.js ) inside the reducers directory because it is then ‘closer’ to all the reducers. I agree it might make much sense to keep it that way, but I prefer keeping my root.reducer and root.store in the root of my src. And here’s why :

  • reducers directory is strictly kept for storing individual reducers. The index.js inside it is used as the main entry point to export all the reducers. ☝️
  • root.reducer and root.store seem closer ( or hooked? ) rather than all the reducers closer to the root.reducer — since root.reducer here is being used to configure the reducer before we hook it up with store ( which is done inside root.store later ).

So it pretty much makes sense — to keep all the reducers separately in a place and then just imported through a single entry point to our root.reducer, which stays close to our root.store. The simple reason is it is easier to find at the root of src directory than inside another nested directory. That is one of the reasons why it is named root.reducer and not index.js.

Actions and Middlewares Structure

Similarly, our actions directory contains all our actions, an action.types file for all the action types ( we can even have a directory named shared in the src and put the type file there, I used to do it when I had started using redux ) and the main entry file which exports all the actions. Each file inside our actions directory can contain a set of actions that are related to a single aspect of our application, for example, a user, or a user interface state or perhaps some data synchronization. The same goes for the middlewares directory, which holds our custom middlewares, if any, and a single entry point which exports all of them.

note

Note that all three new directories added to our previous structure have the main entry point, which exports all the individual parts — mainly because it makes the imports cleaner and also makes it look modular.

So, we’re done with the basic stuff that could be added to any react application which implements an application state ( A little secret — you can do it without using redux too! ). 🤓


Integrating Sagas, Services, and Selectors

Let’s add more volume to the codebase. The first thing that comes to my mind is sagas — mainly because any real-world application with a considerable codebase usually has asynchronous actions going on in parallel. Let’s assume we need redux-saga for our application, and we can’t do away with thunks!

Oh wait, let’s have some services too — for fun! 💥

And while we’re at it — let’s not forget about making our state management which we had set up earlier a little better by adding selectors to our application — which is a must by the way if we have many things going on inside our application store!

Did I add too much? Well, to cover all of it, without implying that your application can not have all of it, which can pretty much if required.

Directory Structure

Okay, let’s follow the same pattern and make the directories first. I’ll go ahead and create directories for them, like so :

The expanded source directory structure including sagas, services, and selectors folders

This structure might look pretty much self-explanatory, and you already have an idea of how we might structure them internally. All our sagas go inside the sagas directory, with our root saga also inside it ( you can name it whatever you want — root.saga or index.js ) just like our entry points to reducers and actions inside their directories.

But you might ask..

Why Keep root.saga Inside the sagas Directory? 🤔

Well, here’s a pretty simple explanation. When we discussed about the reducers and actions, we kept the entry point of the directory as something we’re directly using in our application — entry point in reducers directory for importing all reducers to root.reducer and entry point in actions directory for importing them in various parts of our react application. Similarly, for our root.saga or index.js inside the sagas directory, which is going to be used in our root.store while initialization, makes more sense to be seen as an entry/access point and not anything more complicated than that. Usually, it’ll contain our root saga, which spawns/calls/forks/ invoke other sagas accordingly.

Structure for Selectors and Services

The same goes for selectors and services. Both contain an entry point that exports all the selectors and service modules from the directory. Keep in mind that it is there to provide us a cleaner import and better view of the structure!


Conclusion: A Scalable Foundation

And there we go! We have pretty much completed setting up our react application to start with a complicated project — but with a relatively simple structure which anyone can get used to and something that scales well too! In my experience, a similar structure has fared well in scaling up along with regular and extensive application-wide changes while keeping our productivity high.


Did you find this architecture helpful? Have suggestions or questions?

Please leave a comment below, or reach out via my social media profiles.

Thank you for reading! 😄


Happy hacking! Cheers! 🎉


You may also be interested in

React hooks implementation for state management without Redux

Managing React application state - without using Redux!

May 19, 2019

In this article, we explore managing React application state using hooks. We leverage the React.useContext and React.useReducer hooks to create a Redux-like state management system without external dependencies.

Read article
Diagram illustrating React application architecture concepts and directory structures.

Scalable React Application Architecture: Best Practices for Directory Structure

April 11, 2019

Learn how to structure your React applications with a scalable and maintainable architecture. Discover practical directory organization patterns based on real-world experience.

Read article
Lighthouse audit automation dashboard showing performance metrics for a Progressive Web App

Automate Lighthouse Audits for Your Progressive Web App

June 29, 2019

Learn how to automate your Lighthouse audits with Mocha and Chai instead of manually performing audits on your Progressive Web Application. Run tests programmatically in CI/CD environments or locally to maintain consistent quality standards.

Read article
React Native logo with encryption overlay showing security implementation

Secure Data Storage in React Native: Encryption Guide

September 21, 2018

Learn how to implement secure, encrypted data storage in React Native applications using redux-persist and redux-persist-transform-encrypt to protect sensitive user information.

Read article
Abstract representation of links flowing between nodes

Mastering Link Juice, Nofollow & More: An SEO Guide for Developers

June 11, 2024

Understand link juice, nofollow, noreferrer, and link balancing strategies to optimize internal and external links for better SEO and page rank.

Read article
JSON-LD structured data implementation example with code and rich search result preview

JSON-LD for SEO: Boost Your Website's Search Visibility

February 14, 2024

Learn how to use JSON-LD structured data to enhance your website's SEO, improve rich snippets, and help search engines better understand your content.

Read article