Create a Scalable Angular 7 Project Structure: Complete Example
Angular Project Structure

Create a Scalable Angular 7 Project Structure: Complete Example

A scalable angular project structure is something I struggled to implement when I started working on my first real-world Angular project. I generated my fist app in the command-line and kept adding components, services and pipes on the app module.

Although Angular CLI does a great job at generating a new project, it does not force you to use a specific structure. Your project contains a single module, which contains all of the components.

Having worked on several Angular projects, I’ve come up with a structure that helped me build scalable and maintainable applications. I aim to share this project structure to help developers who are in the same position.

While the following structure fits my needs, it is by no means the best structure. It is a guideline and your project structure may need be different to accommodate certain requirements.

Project Structure

|-- app
      |-- core module
           |-- guards
           |-- models
           |-- services
      |-- shared module
           |-- components
           |-- pipes
           |-- directives
           |-- validators
           |-- layout
      |-- customers (feature module)
           |-- customer-list
                |-- component files
           |-- customer.routing
      |-- users (feature module)
           |-- user-list
                |-- component files
           |-- user.routing
      |-- root module
           |-- module files
|-- assets 
      |-- favicon
      |-- fonts
      |-- images
|-- environments
      |-- dev
      |-- prod

I created an Angular starter project template based on this guide, check it out at GitHub or view the demo below.

Overall Project Organisation

The project closely follows the folders-by-feature structure from the Angular style guide. I love the fact that the style guide is highly opinionated as this sets standards for colleague developers to follow.

In this structure, the application has four fundamental types of modules. Together, they form what I call the scalable project structure.

  • Root module
  • Core module
  • Shared module
  • Feature modules

Let’s take a look at the responsibility of each module and see how they help to create a better project strtucture.

Root Module

AppModule is the entry point of the application and has the following responsibilities:

  • Bootstrap the application with the AppComponent.
  • Import global dependencies such as BrowserModule, CoreModule and LoggerModule.
  • Setup routing for the whole application, enabling lazy-loading for feature modules.

Core Module

The CoreModule of the application is responsible for keeping global services.

Most likely, these services will be HTTP services to communicate with a back-end API. I also use the core module to store guards, models and other global dependencies such as http interceptor and global error handler.

Please note that there should only ever be a single core module. This is so that the services registered in the core module are only instantiated once in the lifetime of the app. You can conveniently force the single-use of the core module.

Shared Module

SharedModule contains code that will be used across your feature modules. You only import the SharedModule into the specific feature modules. Ensure you don’t import the SharedModule into your AppModule or CoreModule.

Application-wide singleton services do not belong to the SharedModule, they should be in the CoreModule. SharedModule is only for keeping common components, pipes & directives. Layout component is a great example of a shared component.

// shared.module.ts
@NgModule({
  imports: [
    RouterModule,
    FormsModule,
    ReactiveFormsModule,
    FlexLayoutModule,
  ],
  declarations: [
    LayoutComponent
  ],
  exports: [
    FormsModule,
    ReactiveFormsModule,
    FlexLayoutModule
  ],
  entryComponents: [ ]
})
export class SharedModule { }

Feature Modules

Finally, the last type of module in this structure is the feature module. Feature modules are used to organise a distinct feature of an application.

For example, you can create a feature module to encapsulate all functionality regarding customer management or account management.

Similarly, you can create a feature module to keep all imports of Angular Material or another third party control library such as Kendo UI.

Feature modules not only make your application structure better organised, they also allow isolated testing. In addition, assigning a feature module to a fellow developer is a great advantage to support development in parallel.

// customers.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomersRoutingModule } from './customers-routing.module';
import { SharedModule } from '../shared/shared.module';
import { CustomerListComponent } from './customer-list/customer-list.component';

@NgModule({
  imports: [
    CommonModule,
    CustomersRoutingModule,
    SharedModule
  ],
  declarations: [
    CustomerListComponent
  ],
  entryComponents: []
})
export class CustomersModule { }

Routing

AppModule enables lazy-loaded routes to feature modules.

This allows each feature module has its own routing configuration, which keeps routes in corresponding feature modules. This approach makes it easy to identify and isolate the feature content.

// app-routing.module.ts
const appRoutes: Routes = [
    {
        path: '',
        loadChildren: './customers/customers.module#CustomersModule'
    },
    {
        path: 'customers',
        loadChildren: './customers/customers.module#CustomersModule'
    },
    {
        path: 'users',
        loadChildren: './users/users.module#UsersModule'
    },
    {
        path: '**',
        redirectTo: '',
        pathMatch: 'full'
    }
];

First item in the routes array loads the customers module.

We then lazy-load other feature modules using a custom URL pattern and the path to the module.

Finally, we ensure that any path that does not match the routing configuration is redirected to the default module with ** pattern.

As an example, you can setup child routes in a feature module like below.

// customers-routing.module.ts

const routes: Routes = [
  {
    path: '',
    component: LayoutComponent,
    children: [
      { path: '', component: CustomerListComponent },
    ]
  }
];

Summary

To summarise, we explored a very opinionated and an easy way to structure an Angular application following the official style guide. I hope this guide helps you build better applications. Check out the starter template at GitHub and drop a comment below to let me know what you think!

Umut Esen

Umut is an enthusiastic software developer, latest web and mobile technology adapter and primary author of onthecode.

This Post Has 2 Comments

  1. Hi there! Such a wonderful article, thanks!

  2. Having read this I believed it was extremely enlightening. I appreciate you finding the time and effort to put this content together. I once again find myself spending way too much time both reading and commenting. But so what, it was still worthwhile!

Leave a Reply

Close Menu