At work we wanted to move away from our app that uses knockoutJS to react. After some experimentation with redux and contextAPI, we disliked how quickly things could get complicated with a large store even with a relatively simple app. So we decided to go with the most similar thing to knockoutJS, mobx. Familiarity is a powerful force.
After this decision, we started looking at architecture suggestions online and found many build apps basically the same as redux: one large store that is of many others, the component subscribes to the large store and trigger the store to make API calls.
This felt very deja vu and something we rather want to avoid. We found that the large responsibility of the store made it necessary to code complex ways of getting data to the component. Once our use cases grew, so did the redux store and consequently the size or number of reducers and actions files. At some point we just lost the ability to come up with creative names and module organizations, and things went off the rails. The difficulty to understand code also increased as there were logic flow loops of actions, reducers, and stores. Luckily for us, the redux app has not become a money maker for the company so we can put it off to the side at ease.
For our new app we propose something that closely follows our current knockoutJS architecture:
1.) Use mobx for global states, such as whether the user has logged in and have what kind of permissions. Global states are important informations that needs to be shared between different features/component trees (this echos the inspiration for RecoilJs).
2.) For states within a feature/component tree, we use state + props and contextAPI. This follows the intended use as written on the React website.
3.) For services, the components will make and consume the requests themselves. This allows the components to do whatever processing necessary for each use cases without possibly affecting others. We would happily refactor and make a new controller module for repeated use cases; this should be minimized though.
This architecture brings the following benefits:
1.) a clear division of responsibilities as problems with mobx stores will unlikely be conflated with problems with either the state/props or service layer. The vice versa will likely to be true.
2.) no part of this architecture needs to become complicated as each part is responsible for simple goals. The exceptions may be the component since it will contain most of the business logic. However, this can be remedied by abstracting the logic out into a functional controller module whenever necessary.
3.) lessen the application's tie with individual libraries. This has been a major motivation for us. The current knockoutJS app is so tied up with utilities provided by knockoutJS that it is impossible to relocate any part of the application without heavy abstraction removing the library first. The library is a liability for us.
please let me know what you think, I am very welcoming of criticism :)