File and folder structure for Angular applications

Alberto Basalo
10 min readNov 23, 2023

--

An organized monolith: Optimizing monolithic projects with folders.

Updated on July 1st, 2024.

Having good scaffolding is the basis for successful development. Following the Angular Style Guide is just the first step, but more guidance is needed when starting a new project in the new Angular Renaissance without modules.

Angular scaffolding standalone

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.

Folder structure for a medium-size Angular application

File naming convention.

Single Responsibility

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, the artifact serves a purpose and belongs to a specific syntactic category. Solution: concatenate both separated by a dot: functional.technical.ts

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, bookings.list.ts

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 --type attribute can generate components with specific names (called types) that help 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.ts for those used in routes and *.widget.ts for any component that gets and writes data on its own.
  • Dumb components: The default *.component.ts or, in certain cases, go for specifics like *.form.ts or *.table.ts

You can do this with the CLI for components, but nothing prevents you from using the same idea with services getting *.repository.ts or *.store.ts, *.validations.ts

🏗️ Name files using functional and technical descriptors, leveraging the type attribute with custom tags like: page, widget, repository, store… As an example you will end with login.page.ts, auth.repository.ts, user.store.ts…

File names mixing feature and technical tags for Angular artifacts.

A folder universal base agreement.

Group files in folders

It’s easy if you only have a dozen files. However, for real-world solutions we will face hundreds or thousands of 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.

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. no surprise this time, the name chosen was shared.
  • Being the module the unit of compiled result, some people tend to create modules just for the sake of having smaller .js chunks.

Even today, without the need for modules, we still need to divide our code across a myriad of components and services… some to be injected at the root of the app 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. Ant his is my proposal for the first-level folders.

🏗️ The top 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)

Top folders for an Angular application

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…

🏗️ Rule 5–15:

A folder with less than 5 elements does not need to be subdivided

A folder with more than 15 elements must 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. Choose whatever makes sense for you.)

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? Yes, a tree is a common 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 dumb 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.

🏗️ Each page should have a folder in a path related as much as possible with the url that resolves. In this folder you should mix components and services directly used by the page component.

The Angular routes folder

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 the lifetime of the application. Things we used to call singletons were provided at the root as injectable services.

Nowadays, injectable provided 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 this functions still acts as root providers, and we only reference them when the app starts. Reason enough to keep them in their own folder and, out of respect for tradition, keep the venerable core name.

Some features are also used once at the top component tree. Ia ma thinking about the header or any navigational component. Some dialogs or user-feedback modals are also the kind of thins not related with any particular route.

This layout artifacts can be located at the core folder, meaning they are root features.

All in all, it’s not like it’s going to be very numerous folder, 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. Fro example having a layout and a providers folder to separate the former I mention.

🏗️ Use a core folder for providers and configurations used across the entire application. Consider adding features not related to routes like a header, footer, dialogs and any layout components.

The core forder in a Modenr Angular application

Any other than routed and core features, could go very well in our last folder. Because at the shared folder there’s room for everything else.

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. Specially in big apps you will find a lot of repeated code that can be abstracted and reused.

So this is going to grow. A lot if you follow clean code advisings. 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 (Angular components and other reusable declarables)

In this way, everything shared is organized into these three subfolders of shared: domain, services, and ui.

🏗️ Divide the shared folder using this technical criterion that 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.

The shared folder with Angular declarables and injectables. And a domain folder for no related Angular

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.

Shared folder structure

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:

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:

Folder structure for Angular without modules

Note: You can read this sibling post about folder structure for Node Express services. TLDR: I follow the same principles everywhere.

Also, for really big projects, you can go deeper and read my article about dividing a monolith into a set of libraries.

Summary:

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:

+---app
| +---core
| +---routes
| | +---activities
| | | +---new-activity
| | | \---slug
| | | \---admin
| | +---auth
| | | +---login
| | | +---profile
| | | \---register
| | +---bookings
| | | \---new-booking
| | \---home
| \---shared
| +---domain
| +---services
| | +---auth
| | \---log
| \---ui
\---assets

I hope you find this useful and that you share it if you think it can help others.

Alberto Basalo

Elevating Code Quality

--

--

Alberto Basalo
Alberto Basalo

Written by Alberto Basalo

Advisor and instructor for developers. Keeping on top of modern technologies while applying proven patterns learned over the last 25 years. Angular, Node, Tests

Responses (1)