Having good scaffolding is the basis for successful development. Following the Angular Style Guide is just the first step. I should almost say it was… because we need much more guidance when starting a new project in the new Angular Renaissance without modules.
In this article and the companion demo code, I’m going to compile some best practices and tips for creating file and folder structures for Angular applications inspired by the official guide and its LIFT principles, which is what I’m going to start with:
Locate: Group coherently
Identify: Name to indicate the content
Flat: Create subfolders if necessary
Try-DRY: Some redundancy can be beneficial
Note: Go to the bottom of the article if you are looking for tips and the proposed solution. TLDR: here you have the folder structure screenshot. Keep reading if you want to know why.
File naming convention.
The Angular Style Guide encourages us to dedicate a file to each necessary artifact, i.e., encapsulate the related code in classes or modules and export it into its own file.
Then, to name each file, we are told to compose the name of the file from its functional and technical nature. That is, that artifact serves a purpose and belongs to some syntactic category. Solution: concatenate both separated by a dot:
For example, a service to manage user data should be called
users.service.ts, while a component to display your bookings should be called
booking-list.component.ts or modernly and abbreviated, as I show you below,
The Type attribute.
Services and components are common technical artifacts in Angular; the same goes for guards, interceptors, etc. But you can go beyond and use your own types.
Indeed, since Angular 14, the CLI with the
--typea attribute can generate components with specific names (called types) that help us subclassify our otherwise generic concept of components.
I usually have a small set of types to discriminate the main purpose of my components::
- Smart components: I use
*.page.tsfor those used in routes and
*.widget.tsfor any component that gets and writes data on its own.
- Dumb components: The default
*.component.tsor, in certain cases, go for specifics like
You can do this with the CLI for components, but nothing prevents you from using the same idea with services getting
Group in folders
Easy peasy if you only have a dozen of files. However, for real-world solutions ee will face hundreds or thousands files. How do we keep them under control?
You might expect it to jump right into the famous dichotomy: group by function or by type? Of course, this is a legitimate question; It is based on the two dimensions that we apply to name our files (functional.technical). But it’s not always that simple.
Let’s try to find a solution.
A universal base agreement.
In the early days of Angular, dividing applications into modules was almost mandatory. The nature of the things we used to declare in those old modules led us to segregate them into three main categories:
- Things used by the root. It typically includes the layout of the application and many injectable services provided at the root. We called it the core module.
- The functionalities that are assigned to routes. For each one, it’s lazy load and its module with its routes.
- Things used on feature pages. Here, we used to find silly components, directives, and presentation pipes. This time, the name was shared.
Even today, without the need for modules, we still create things to be appraised at the root and others to be used on the pages…
So, it’s only natural for experienced Angular developers to feel at home with the routes, core, and shared concepts. My proposal for the first-level folders.
The first level folders should reflect that this is an Angular web application:
/core (services to be used from the root)
/routes (the functional branches of the navigation tree)
/shared (utilities to be used from functional branches)
Now that we know how to name the files and the three folders to generate them to save them, it’s time to analyze these folders.
Flat or deep, a matter of size.
From here, there is no longer a fixed rule but something that depends on the number of files (application size) and the architectural style applied.
Adhering to the Flat principle of LIFT, I will keep the folder structure as nested as little as possible. Some tips may come from this rule that I make up…
A folder with less than 5 elements does not need to be subdivided
A folder with more than 15 elements has to be subdivided
Folders between 6 and 14 items are divided if an obvious criterion arises
Let’s apply this tip to the main folders: routes, core, and shared.
The Routes are a navigation tree.
Let’s start with the functional folder, which I call routes. (I’ve seen people call it features, and others call it pages.)
You can view all of your app’s paths as an array with nested arrays… In computer science, we call it a tree. And what else is a tree as well? The paradigm and visualization of folders/files for half a century.
So, I propose implementing the path tree as a folder tree. When possible, of course. But if I’m working on a path like
/activities/surf/bookings I’d like to have my
bookings.component.ts underneath this /activities/name/ folder path.
This paths folder is dedicated to containing all the functionality of the application. We’ll need more than page components to meet users’ needs. You should at least support the page with some service and some silly presentational component.
Keep all those related things close to each other, write custom services for each page, and provide them locally. At this level, we’re grouping by functionality, not by technology, so we mix components and services at the same functional level.
You’ll gradually detect common services, duplicate logic, and similar presentation components. They should all be abstracted and reused as much as possible. It’s time for the old core and shared friends.
The Core is short and should be shallow.
This folder will be home to things you’ll use once in a lifetime… of the application. Things we used to call singletons were provided at the root as injectable services.
Nowadays, injectable and in-root classes are used less. For example, interceptors and guards are coded as functions and provided with a special syntax. Being a function doesn’t mean anything anymore in terms of singletons…
But they’re still root providers, and we only reference them when the app starts. Reason enough to keep your own folder and, out of respect for tradition, keep the respectable core name.
It’s not like it’s going to be very numerous, either, so you’ll usually be able to manage without subdividing it. But when you have 15 or more files, you will surely find a natural criterion to group them.
Alternatively, all of this could go very well in our last folder Because there’s room for everything.
The Shared catch-all can be deep.
All things not related to a specific feature or not provided as services at the application level have a place here.
So this is going to grow. A lot. Sooner rather than later, it will far exceed the 15-item limit. I can only recommend that you stick to the rule of as shallow as possible.
Here, it is not so important to group things by their function or by their technical nature. It’s a matter of common sense.
So, having a /shared/components/ folder might be fine at this level. Likewise, a more functional folder like /shared/auth/ for security-related things.
But you’ve come this far for advice, so I’m going for it. To begin with, I propose a generic structure based on grouping things by their nature and dependence.
- /domain (types, functions, and utilities without Angular dependencies)
- /services (Angular injectable services and functions)
- /ui (components and other reusable declarables)
In this way, everything shared is organized into these three subfolders of shared: domain, services, and ui.
This technical criterion will be more helpful if you take the leap to dividing the monolith into libraries. That way, each can have its own ecosystem of dependencies and specific testing tools.
Again, a functional distribution criterion is equally appropriate.
For example, let’s talk about the services folder (these are utilities based on Angular technology and used in various paths or core functionalities). Over time, it will grow, and some of your files will be cohesively related and deserve their own folder. The most straightforward cases may be those of instrumentation and security. In my case, it gives rise to the log and auth folders.
I put both folders inside the services folder to maintain that technological coherence. But it’s equally valid to take them up a notch and make them the direct offspring of shared.
Sample code :
Show me the code. To familiarize yourself with this architecture, navigate it on your own. Here’s an example with all those tips applied:
GitHub - AlbertoBasalo/ng-lab: 🅰️ Angular 1️⃣7️⃣ 🔬🧫🧪 Laboratory
🅰️ Angular 1️⃣7️⃣ 🔬🧫🧪 Laboratory. Contribute to AlbertoBasalo/ng-lab development by creating an account on GitHub.
It’s a lab project for my workshops, and it can help you understand how to name things in Angular. Here’s the final image I’m started with:
Note: You can read this sibling post about folder structure for Node Express services. TLDR: I follow the same principles everywhere.
File and folder structure for Node-Express applications
Master the LIFT principles for Node-Express app structure. Learn file naming, first-level folder creation, and flat…
Also, for really big projects, you can go deeper and read my article about dividing a monolith into a set of libraries.
Optimizing the Architecture of Large Web Applications with Angular
Optimizing monolithic Angular projects with libraries using Nx.
When it comes to organizing your angular code, there could be as many ways of doing it as developers. Here are some guidelines that I saw in successful codebases. I hope they can help you.
- LIFT principles: Locate, Identify, Flat, and Try-DRY. These principles help to organize the code coherently, clearly, and concisely.
- File naming convention: Use a combination of functional and technical domains to name the files, such as `functional-name.technological-type.ts`
- First-level folders: Three main folders to reflect the basic functionality of Angular:
/routes /core /shared
- Shared can be grouped by technology: In the shared folder, a group according to dependencies and technologies in /domain /services /ui
- Flat over deep structure: Keep the folder structure as flat as possible, and only create subfolders when there are more than 15 files or when there is an obvious criterion.
In short, the folder tree looks something like this:
| | +---activities
| | | +---new-activity
| | | \---slug
| | | \---admin
| | +---auth
| | | +---login
| | | +---profile
| | | \---register
| | +---bookings
| | | \---new-booking
| | \---home
| | +---auth
| | \---log
I hope this helps.
Elevating Code Quality