How to set a good project file structure
There isn't a formula to create a perfect project file structure but there are some "best practices" that can definitely help you make a good one.
A common problem among software developers is having to decide where to put the source code, images, libraries, documentation or anything else inside of a project directory. And though it's a common issue, it's also fairly easy to tackle by having in mind a set of "best practices" so that the project ends up happily living in its container folder and a developer can enjoy a comprehensible navigation within it.
โ Disclaimer: This article does not provide a CORRECT nor a SUBLIME way of doing things. Instead, it provides tips based on the writer's experience.
๐ฑ The root of the file tree
In order to end up with a usable project file structure, this article will guide you on the definition of one meant to be used by a react application. We'll begin with the very root of the directory assuming that an NPM package has already been initiated. It should look like this:
. <-- represents the directory root
โโโ package.json <-- NPM package definition
It's so clean and perfect to this point but extra stuff needs to go inside of it. This root of the project's directory should be used to allocate files that might not be totally necessary for the final project's product but for the developer to know that to work with and tools to know how to act like. Here's a list of example files one can commonly find in this location:
- ๐ README - Documentation about the project itself. It may contain the purpose of the project, description of its functionality, documentation on build steps, etc.
- โ LICENSE - Specifies how the project can be used or distributed along with its constraints to do so.
- ๐
โโ๏ธ IGNORE - This is commonly found when there's a version control system set on the project. If Git is being used, the file
.gitignore
could be included. - โ CONFIG - Depending on the project you're building, you might find tools configurations on the root directory, too. For example, if the project is a .net application, it's common to find an
.sln
file, if the project is a nodejs application using typescript, a bundler such as webpack and the documents get automatically formatted by Prettier, you may find files such astsconfig.json
,webpack.config.js
and.prettierrc
respectively. - โป ENVIRONMENT - Some projects may rely on environment variables. Most of the times, these variables are defined in the root directory. For example, the Azure Functions Core Tools rely on a
local.settings.json
file for this purpose while NodeJS relies on.env
files instead.
A root directory hosting some the aforementioned examples may look similar to this:
.
โโโ .env.development
โโโ .gitignore
โโโ LICENSE
โโโ README.md
โโโ package.json
โโโ tsconfig.json
โโโ webpack.config.js
As you may notice, none of the files are really needed by a project's final product but they are important for a developer to work on the project. Any other files that have similar purposes should be stored on the root the project's directory to avoid losing track of them or get them mixed up with the project's actual source code.
๐ Project sub-directories
It's common to find several sub-directories in a project, and each of them serves a different purpose. It totally depends on the kind of project you're building but usually one can find a structure similar to the following:
- โ .DIRECTORIES - Many tools depend on more than a single configuration file and instead of storing a dot-file on the root of the project's directory, they may create a sub-directory with its name starting with a "." (dot) to store their configurations in a single place. Good examples you may be familiar with are VSCode and Git bucease if you're making use of them, you for sure can find the
.vscode
and.git
directories in your project. - ๐คนโโ๏ธ ASSETS - Contains files that may not need modifications and can be simply copied as part of the project's resulting output. Common files that are stored here are favicons, avatar pictures, type fonts, manifests, re-distributable scripts, etc. Common names used for this directory are
assets
,resources
andstatic
. - ๐ DOCUMENTATION - You may know every bolt and nut holding your project together but when you plan to share it with other developers, it's a very good practice to let them know how the project works, looks, gets used and gets built via written documentation. Frequently, this directory is named
docs
. - ๐ OUTPUT - The project gets compiled and its resulting product is usually put apart from the source code in a specific directory so that it can be easily grabbed. Usual names for this directory can be
out
,result
,public
,dist
andbin
. Also, it's customary to exclude this directory from source version control systems like Git. - ๐จโ๐ป SOURCE CODE - Here's where the golden nuggets, the jewels, the juicy stuff gets stored at. More to come about this special directory below ๐. An habitual name for this directory is
src
. - ๐งช TESTS - Another good practice among developers is to create and share automated tests for our project. This helps ensure that what the project is aimed to do is actually what it does. Most tests are written code, too. But mixing the tests code with the source code can crate confusion, so, it's better to separate them and give them their own place.
test
is a very common name for this directory.
Here's an example of a project directory containing some of the sections mentioned above:
.
โโโ assets
โ โโโ avatar.jpg
โ โโโ favicon.ico
โโโ docs
โ โโโ README.md
โ โโโ api-reference.md
โ โโโ build.md
โโโ out
โโโ src
โ โโโ app.tsx
โ โโโ footer.tsx
โ โโโ nav.tsx
โโโ test
โ โโโ app.test.js
โ โโโ nav.test.js
โโโ .env.development
โโโ .gitignore
โโโ LICENSE
โโโ README.md
โโโ package.json
โโโ tsconfig.json
โโโ webpack.config.js
๐จโ๐ป Source Code file structure
๐ฅช The layered approach
Not all applications will have the same structure, but they all can be divided into "concern layers." For example, when developing an API, you may want to include all controllers
into their own directory with a separate business logic within a services
directory and data definitions within a models
directory. This to aim for a better understanding of the project structure, scalability and ease of maintenance.
Another example is a desktop application wherein a UI
directory may hold all window designs but a services
directory would feature all business logic, a models
directory could hold all data definitions and a data
directory all database transactions logic.
With this approach in mind, we can elaborate our src
directory on our structure and add a pages
directory for entities that will make use of several modules at a time, a layouts
directory for pages "themes", a modules
directory where entities of the app are defined such as a header, welcome, posts list, footer, etc. All these make the src
directory look similar to this:
src
โโโ layouts
โโโ models
โโโ modules
โ โโโ footer
โ โโโ header
โ โโโ post-list
โโโ pages
๐ SOLID principles
These principles mainly apply to Object Oriented Programming but taking them as guidance will also give you a nice result with other methodologies.
- Single responsibility: A class should only have a single responsibility. That is, is should only affect a single specification of a program.
- Open-Closed principle: Software entities such as classes, modules, functions, etc. Should be open to be extended in functionality without having to be modified at their source code level.
- Liskov substitution principle: Taking
S
as a subtype ofT
, objects of typeT
may be replaced by objects of typeS
without the program suffering a change on its desired properties. - Interface segregation principle: Have you ever had a bad experience using a "universal" remote control? Well, the same thing happens with programming; a remote control may be thought like an interface for controlling a device such as a TV. The interface defines the actions that a recipient of it can do. This principle states that a recipient or client should not be forced to feature an action it's not going to use, therefore, it's always better to have several small interfaces rather than a big one.
- Dependency inversion principle: The summary of this principle is "High level objects should not depend on low level implementations." This is, any high-level entity in a program should be able to work under several scenarios and its functionality should be dependent of low-level implementations. For example, a "User Administration" module should be able to take care of its business without being dependent of a "User" class where features such as name, age, registration date or other data is defined. This same module should be able to leverage the task of saving data to a database by receiving the indication to do so, but the actual task should be delegated to a low-level class that takes care of saving the information.
More about this here
Knowing this, we can then think of adding files to the src
directory sub-paths like this:
src
โโโ layouts
โ โโโ blog-layout.tsx
โ โโโ common-layout.tsx
โ โโโ home-layout.tsx
โโโ models
โ โโโ blog-post.ts
โ โโโ navbar-item.ts
โโโ modules
โ โโโ footer
โ โ โโโ clickable-icon.tsx
โ โ โโโ external-links.tsx
โ โ โโโ index.tsx
โ โ โโโ site-sections.tsx
โ โโโ header
โ โ โโโ index.tsx
โ โ โโโ logo.tsx
โ โ โโโ menu-button.tsx
โ โ โโโ nav-button.tsx
โ โ โโโ navbar.tsx
โ โโโ post-list
โ โโโ index.tsx
โ โโโ list.tsx
โ โโโ post-card.tsx
โโโ pages
โโโ about.tsx
โโโ blog.tsx
โโโ error404.tsx
โโโ home.tsx
I hope you've enjoyed this article. Now, you have a couple of guidance points to follow when having to design a project's file structure.
Cover credits: Diagram made with diagrams.net | Banner vector created by pch.vector - www.freepik.com | FiraCode