Scalable JS SPA Project Structure
Just a short note about project structure. Not really specific for JS, or SPA, or whatever. It doesn’t have any fancy name, but instead of inventing the name I will provide you with reasoning and explanation of principles behind it. I will explain it on the example of React SPA, but its template is really generic and doesn’t even depend on the programming language.
Let’s start with the structure itself. Inside of your “JS” folder where you have three main folders:
- js/pages/. There are the modules defining your application pages which are associated with routes. So, for every route like myapp/some/route, there is the corresponding someRoutePage.js file containing the “view”.
- js/ui/. Views (which are or React Components, in our case) might consist of the subviews. Whenever we have the React Component which is used in two (or more) different pages, it must go to the js/ui folder.
- js/server/. Explicit I/O hardcoded in the UI components is denied. All stuff involved in communication with a server must (1) have a clean interface and (2) be placed in the server folder.
Before we start the reasoning, let’s define an “architecture” term. There are so many definitions of this term that it became practically meaningless. But for the purpose of explanation, we need the clear meaning.
So, system structure is commonly referred as its “design”. Common “architecture” definitions fail to make the clear difference with a design; which is the source of endless discussions whenever the particular thing is related to this or that… We don’t have time for this, so let’s just quickly finish it:
An architecture is the set of rules people use to create the design.
Every part of this sentence is important and have consequences. And strangely enough, it requires explanations. Architecture is obviously more general than the structure of the system. One may refer to it as a “high-level structure” (as it’s commonly defined), but the major problem of this point of view is that it’s static. It tries to look at the architecture as if it would be the building itself. But it’s not.
Architecture is some general concept, an idea which exists in people’s mind. You can’t touch it. What is seen and can be touched, is an implementation of this idea in the form of the building or the structure of the software system. Which is the particular design.
To uncover the true meaning of this term, we need to introduce the notion of time in the definition. People use some ideas to create the system’s structure. And they use these ideas in form of rules which they follow. Or refuse to follow. Or they might not have any idea of what they are doing at all, but as people are really bad at doing truly random things they still follow a kind of ad-hoc rules and use some repetitive patterns.
So, there are rules they follow when they do their job are an architecture. When they finished — there are no rules. There is just the structure left. The design.
So, let’s reformulate our JS SPA project structure as the set of rules. Considering the fact that it’s the people who choose to follow or ignore the particular rules, it would be wise to provide some reasoning thus these rules would make sense. Is it fun to follow the rules which don’t make any sense?
- js/pages. We would probably want to create the separate bundle for every page in our SPA. Which is easy to achieve with a webpack, if you have the separate js module for every page. Then, if you will use AMD require it will create dynamically loaded bundle automatically. Therefore, we create the rule: every React Component implementing SPA page must be placed in the separate *.js file. And it would be beneficial to have some naming convention for these files, so it will be trivial for the developer to locate them.
- js/ui. Some pages might rely on the same UI widgets. And different pages might be created by different developers. When the developer works on the particular page changing the stuff inside, he could easily break other pages if they will reference local UI components. And he would not have any idea that he is doing anything wrong. Solution? We introduce the rule: all UI components which are used in more than one place must be moved to js/ui.
And as the changes to these components are dangerous as they can break a lot, we introduce additional rules to the process of making changes in this folder. Say, everything must be documented, everything must be covered with unit tests, and all changes there must pass the code review.
- js/server. Server API can be implemented by the different team. It might change over the time. And the same API points can be used by different application pages or UI widgets. It seems to be a good idea to wrap it on the client to some abstraction and to cover it with unit tests. Thus, it’s helpful to keep it together and therefore, we introduce the rule: ad-hoc I/O from the UI components is denied; data layer classes must not depend on UI components and must be placed in js/server.
At the top level, you see some high-level structure. In this case, it’s the set of big boxes (folders) you’re using to sort the mess out. But it’s not the structure itself which make sense here; it’s the sorting rules which do.
And if the rules will change (say, you don’t like the reasoning I provided — that’s fine, come up with your own reasoning and rules) the structure will follow. But only in case if:
- The people are aware of the existence of the rules. So, the rules must be clearly communicated.
- Rules are simple, so people are able to remember them when needed.
- Rules are reasonable, so people don’t feel themselves as idiots when they follow them.
If all of these three criteria are met, you have a chance of having some “architecture” in your system. Otherwise, it will exist mostly in the imagination.
Now you see how important every part of our definition is? Thus, think about the reasoning, discuss it in a team, formulate the rules, and communicate them clearly. And may the good project structure be with you.