Skip to content

Instantly share code, notes, and snippets.

@kjendrzyca
Forked from jaszczw/structure-rationale.md
Created May 6, 2019 09:24
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save kjendrzyca/cb1e8cbe3e7c40604636fc9dc90e6a35 to your computer and use it in GitHub Desktop.
Suggested project structure

Redux structure rationale

Goal

We wanted to create an application that is easy to extend and also easy to maintain. In order to achieve that, there has to be clear separation of concerns made by design. We believe that project folder structure is the part where you can enforce many good practices from ground-up. Essenially decreasing the burden on developers.

Introduction

We have couple of building blocks and 'binders' in react-redux application. We create components that represent the view, we create containers that connect views to state and we create actions/sagas/reducers trio to maintain the state. We will also need pages/routes that will display all the containers on one page to user.

We can easly say that the biggest building block of web applications are pages (routes). They represent the view user will get entering our page, contain information what he will and not see on given address. We decided that this will be good point of splitting the responsibilites.

Implementation

As we we decided to split concerns on the level of page we create a container for each page in our application. In our terminology container will mean component that is connected to state and has actions/sagas/reducers the trio (if neccessary).

Example If page has it's own state - let's say - edit form will have validation results that only this page is concerned with. The reducer and appropriate actions for this page would be created and would land in container of this page, they would exist right beside component representing the View and the redux-container that connects the page to state.

If there was a need for having a more granular approach we suggest you to create a smaller container, that would also contain all it's concers - the view in a seperate file, the connector and the the trio.

If you got to the point where single component you want to display changes behaviour completly based on place you use it on, you should create a pure component inside Components/ folder that any container can access.

To access state that is on global level we suggest modules/ approach. In there the trio (sagas/reducer/actions) resides, providing access to domain level state, as for example list of users, list of processes etc. any state, that isn't connected to any page/container but needs to be shared across application.

You can communicate with different containers via imports of their actions that should look something like:

import {changeStatsFilter} from 'containers/ProcessesStatistics/actions'

Example structure

Based on this we created an example structure like this:

.
│   client.jsx
│   routes.js
│   store.js
│
├───components
│   └───StyledProcessesSearch
│           index.jsx
│           style.scss
│
├───containers
│   ├───App                             //Application level container - Main layout etc.
│   │       actions.js
│   │       constants.js
│   │       index.js
│   │       reducer.js
│   │       sagas.js
│   │       selectors.js
│   │
│   ├───ProcessDetailsPage              //Page without specific state for it's own, relies on already defined selectors (modules/)
│   │       constants.js
│   │       index.jsx
│   │       ProcessDetailsAppBar.jsx
│   │       ProcessDetailsContainer.jsx
│   │
│   ├───ProcessEditPage                 //Rich page - has own actions, and own sagas, that impact its state.
│   │   │   index.jsx
│   │   │   ProcessEditAppBar.jsx
│   │   │   ProcessEditContainer.jsx   //Container that may use some generic components, but can also defined it's own structure
│   │   │   reducer.js
│   │   │   selectors.js
│   │   │
│   │   ├───actions
│   │   │       clearEditProcessForm.js
│   │   │       editProcess.js
│   │   │       editProcessesInputValidate.js
│   │   │       index.js
│   │   │
│   │   └───sagas                       //sagas specific for ProcessEditPage
│   │           handleEditProcess.js
│   │           handleValidationResult.js
│   │           index.js
│   │
│   ├───ProcessesStatistics        //Container without specific state, relies on global state
│   │       constants.js
│   │       index.jsx
│   │       ProcessesStatistics.jsx
│   │       ProcessesStatisticsViewModel.d.ts  //Typescript definition that is valid for this container (could be typings.d.ts)
│   │       selectors.js           //selector that takes neccessary data from global state and maps to container
│   │
│   └───ProcessesTable
│           actions.js
│           constants.js
│           index.jsx
│           ProcessesTableComponent.jsx
│           ProcessesTableContainer.jsx
│           reducer.js
│           sagas.js
│           selectors.js
│
├───i18n
└───modules                     //State and actions that is shared across components most often 'domain' related
    └───processes
            actions.js
            constants.js
            models.d.ts
            reducer.js
            sagas.js
            selectors.js
            services.js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment