Fractal — A react app structure for infinite scale
After having worked with React and flux (Redux) with over 8 significant projects (100+ screens each), we learned a few things about React apps.
Over time, a way of structuring projects evolved, which was so natural that on-boarding new frontend developers (with next to 0 experience with React), became a piece of cake.
We call it the Fractal. It is (and will always be) a work in progress.
Fractal allows you to:
- Reason about the location of files
- Manage and create complex user interfaces
- Iterate quickly
- And scale repeatably
Rather than being a set of rules or conventions, the essence of Fractal can be summarised in just one guiding principle: All units repeat themselves.
The app layout
Think of your app as a tree (data structure), every project gives a new shape to the tree, but the roots nodes remain constant. The root nodes is the app layout. These are shown in the screenshot below.
- Pablo is the name of the product.
- build is where all the built files go, you never touch this except when you need to deploy.
- public is where your index.html file and <script src> assets go. (They automatically get copied to build while using create-react-app)
- src is where you code.
- src.pages are the root level components, ones which are directly mounted on level 1 routes. (Ex. if you have a route called /login that mounts a Login component, then Login.js will be present in pages directory).
- src.modules handles your state (actions + reducers using ducks file structure).
- src.components have shared components, like Button, Input etc.
- src.utils have utilities like your API wrapper, date utils, string utils etc.
- config is where you store your environment variables like API endpoints. Don’t commit this to git.
- store initializes the redux store.
- index registers the routes and renders the app.
We don’t have the concept of smart and dumb components, because we found that idea to be limiting.
The Fractal pattern: make all units repeat
The magic happens when you try to leverage the power to compose React components.
Assume a login component that gets mounted on /login, has a form, and a forgot button that opens a password reset modal.
For the sake of this example, we break this down into 3 components :
- Login.js
- Form.js
- ForgotModal.js
This will be structured as follows :
The Pages directory have a Login.js component.
All components are CamelCased.
The sub-components of Login.js are present in a folder called login. This is the node that will help set up a repeating pattern.
All nodes are lowerCamelCased. So login in this case. If ForgotModal.js had another sub-component to show a success message, then we can have a folder named forgotModal in the login node, and place the SuccessMessage.js component there.
The following screenshot shows what it looks like in a real app.
All components required by a page go to its node folder. If the component is required by more than one components, like a branded button, then it goes to the src.components folder. When you need to break down a component into sub-components, use the Fractal. The screenshot below shows how we use The Trackers component in the same way.
The Fractal State
It turned out to be a sound practice to manage components in the Fractal pattern, so we asked ourselves if we can do the same for the state? (Yes we can.)
We use redux in our examples. Assume a design where your frontend has to show a list of apps, each of which can have many reviews. A classic one to many details flow. In standard redux, we can have two reducers, one for [C]reating, [R]eading, [U]pdating and [D]eleting apps and another for CRUDing reviews.
Because we use the ducks file structure, we are supposed to have an apps.js file and a reviews.js file in our modules folder. But this will not help us emulate the shape of the API, and as the size of the codebase grows, it becomes hard to reason for a flat store.
Instead, we use the Fractal state to save our modules as follows :
With the design above, we can emulate the shape of the database directly in our stores.
Q. Ask yourself, if along with reviews, each app had reviewers object as well, where will the module for reviewers go?
A. If you chose to put it in the modules.app folder, then you understand Fractal right.
This gives us a phenomenal scale. Imagine each review had a source associated with it, which was to be fetched from a separate API. All we need to do is create a review folder in modules.app and add a file named source.js to it and inside the combineReducers on line 11, add another key = review and value = combineReducers({source: reviewSource}).
The background
The idea of the Fractal was coined by my mentor Kapil Verma. This idea was successfully implemented in Express based APIs, and was borrowed by me to create scalable react apps.
This has worked well for us in all kinds and sizes of apps. We are even experimenting with the Fractal pattern on our python code bases.
A note for advanced developers
- One thing to note is the absence of index.js inside folders. This causes conflicts with module resolution (because of the casing).
- All imports in the example are absolute as a convention (we prefer absolute). They can be relative if you wish.
Update 1:
A few readers asked for sample code. I’ve managed to put a little something together.
Update 2 (July 30, 2018):
This article has just exploded. I see this mentioned on so many subreddits and git repos. People have emailed me and asked me questions about Fractal. I’m honestly humbled and delighted to be able to payback with my knowledge.
To continue this effort, I’m planning to start a slack community. This will enable you to ask questions(about Fractal and React) related to your use case. It will also give you access to me and other developers (at various skill levels) in real time. You can join it here.
If you liked this article and want to stay updated, follow me on: Medium, Github or Twitter
You might also like: