A simple Angular folder structure that makes development feel natural and easy.
Tags: angular, tutorial, architecture
Published: 2023-03-11 20:06:49

After years of working on multiple Angular projects, I have had firsthand experience with the difficulties of poorly thought out designs. Projects that were supposed to be scalable were unscalable, had unreadable code, and did not adhere to the [Angular coding style guide](https://angular.io/guide/styleguide). Luckily, the coding style guide already gives us a place to start when it comes to project structure, with a [shared module](https://angular.io/guide/styleguide#shared-feature-module) and [a module for each feature](https://angular.io/guide/styleguide#feature-modules).

In this guide, I will go over the feature-based approach in greater detail and explain the whys and hows.

First of all, why is a well-designed folder structure important?
* It allows us to be more flexible with our application. We can add new functionality without breaking anything.
* A well-designed system is simple to understand and navigate.
* It improves testing, maintainability, and reusability.

I like to split the project into six main folders, each with its own responsibility: `core`, `features`, `shared`, `apis`, `types`, and `store`.

#### Core folder

The `CoreModule` is located in the core folder and contains essential services, components, and other functionality that are required by many parts of the application, either once (components) or as a singleton (services). Here are some examples of such components and services:

- Components (header, footer, navbar, error, etc.)
- Logging service
- Exception handling service
- Configuration service
- Localization service
- Auth service
- Http interceptors
- Guards

By encapsulating this functionality into a single module, we gain a centralized location for managing dependencies and configuring the application at a high level.

The `CoreModule` is typically imported once in the application's root module. Its components and services are made available to other modules via dependency injection.

To avoid accidentally importing the `CoreModule` more than once, include the following code in the constructor:

```typescript
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
    if (parentModule) {
      throw new Error(`CoreModule has already been loaded. Import it in the AppModule only.`);
  }
}
```

This is how the core folder might look.:

```
|-- core
|   |-- components
|   |   |-- header
|   |   |   |-- header.component.ts
|   |   |   |-- header.component.html
|   |   |   |-- header.component.scss
|   |   |-- footer
|   |   |   |-- footer.component.ts
|   |   |   |-- footer.component.html
|   |   |   |-- footer.component.scss
|   |-- services
|   |   |-- auth.service.ts
|   |   |-- logging.service.ts
|   |   |-- exception.service.ts
|   |-- interceptors
|   |   |-- auth.interceptor.ts
|   |-- guards
|   |   |-- auth.guard.ts
|   |-- core.module.ts
```

#### Shared folder

The shared folder contains the `SharedModule` which typically contains reusable UI components used throughout the application, such as buttons, form fields, and input validation. It may also include directives and pipes that are used by numerous modules. 

Developers can avoid code duplication across multiple modules by encapsulating these shared components in the 'SharedModule,' which would otherwise result in code bloat and maintenance issues. It also helps to enforce UI consistency because all modules will be using the same shared components.

**Bonus**: Create a `ThirdPartyModule` to differentiate between own components and vendor components such as `NgbModule`. This module should be imported into the `SharedModule`.

The shared folder might look like this:

```
|-- shared
|   |-- components
|   |   |-- spinner
|   |   |   |-- spinner.component.ts
|   |   |   |-- spinner.component.html
|   |   |   |-- spinner.component.scss
|   |   |-- modal
|   |   |   |-- modal.component.ts
|   |   |   |-- modal.component.html
|   |   |   |-- modal.component.scss
|   |-- directives
|   |   |-- highlight.directive.ts
|   |-- pipes
|   |   |-- capitalize.pipe.ts
|   |-- shared.module.ts
|   |-- third-party.module.ts
```

#### Features folder

This folder contains the features of the application. Each feature has its own module that contains a collection of components, services, directives, pipelines, and other code that encapsulates a specific aspect of the application's functionality.

I like to think of a feature as a slice of pie. You should be able to easily remove a slice of pie without making a mess. This means that our features should not be dependent on one another.

The features folder can look like this:

```
|-- features
|   |-- products
|   |   |-- components
|   |   |   |-- product-list
|   |   |   |   |-- product-list.component.ts
|   |   |   |   |-- product-list.component.html
|   |   |   |   |-- product-list.component.scss
|   |   |   |-- product-details
|   |   |   |   |-- product-details.component.ts
|   |   |   |   |-- product-details.component.html
|   |   |   |   |-- product-details.component.scss
|   |   |-- product-root.component.html
|   |   |-- product-root.component.ts
|   |   |-- product-routing.module.ts
|   |   |-- product.module.ts
```

#### Apis folder

The apis folder is used to store files related to consuming RESTful APIs or other web services. These files may include service classes, models or interfaces, and helper functions related to working with the APIs.

```
|-- apis
|   |-- product.service.ts
```

#### Types folder

The types folder is a location for storing type definitions that are used across the application.

```
|-- types
|   |-- user
|   |   |-- user.ts
|   |   |-- user-status.enum.ts
|   |-- product
|   |   |-- product.ts
|   |   |-- product-type.enum.ts
```


#### Store folder

If you're using `NgRx` for state management, the store folder is ideal for providing a centralized location for managing application state, which can help to simplify the codebase and make complex state interactions easier to manage.

```
|-- store
|   |-- user
|   |   |-- actions
|   |   |   |-- user.actions.ts
|   |   |-- reducers
|   |   |   |-- user.reducers.ts
|   |   |-- selectors
|   |   |   |-- user.selectors.ts
|   |-- product
|   |   |-- actions
|   |   |   |-- product.actions.ts
|   |   |-- reducers
|   |   |   |-- product.reducers.ts
|   |   |-- selectors
|   |   |   |-- product.selectors.ts
|   |-- store.module.ts
```

In conclusion, the Angular world and the best practices are evolving, and so do we. I will come back again in a year and revise what can be improved (hello, signals!).

Keep learning, stay curious, and never stop coding!

_If you liked my article and would like to buy me a coffee, you can do it [here](https://www.buymeacoffee.com/vixero)._