DEV Community

Dan Bui
Dan Bui

Posted on

Structure Angular app with Nx workspace

Building a robust, maintainable, and scalable front-end application isn’t easy; many things have to be done. Well structure is one of the most important parts. In this post, I will share my preferred Angular structure with the Nx workspace

Prerequisite

  • Angular 17
  • Nx workspace

What is Nx?

In recent years, Nx workspace has become the most popular tool for building and managing applications. Nx is an open-source, technology-agnostic build platform designed to efficiently manage codebase of any scale. Nx understands our project relationship and dependencies, executes tasks smarter and faster!

Group

First of all, I separate the app into three main groups:

  • Core - singleton services or other parts that should import only one when the application is instantiated
  • Shared - Components or other parts that are shared entire our application
  • Feature - Smart components that map one-to-one with our business

Each part of the group has a prefix that corresponds to the group’s name. For instance, core-service, shared-button-ui, feature-product-list. As my style, I like a flat folder over a nested folder 🙂.( nested folder style we structure like this: core/services shared/button-ui )

App Structure

After identifying which group we should put into. We have the structure below:

├── apps
   └── my-app
└── libs
    ├── core-api
    ├── core-guard
    ├── core-interceptor
    ├── core-layout
    ├── core-service
    ├── customer
       ├── feature-detail
       └── feature-list
    ├── shared-button-ui
    ├── shared-hover-directive
    ├── shared-percent-pipe
    └── shared-upper-case-pipe
Enter fullscreen mode Exit fullscreen mode

Let's explain from top to bottom

Core-service

Type: lib (folder)

Tag: type: core

  • Services which we intend to keep as a singleton, such as user-service,theme-service, often @injectable({provideIn:’root’})

Tip

Prevent CoreModule from being imported twice:

import { NgModule, Optional, SkipSelf } from '@angular/core';

@NgModule({
  // declarations, imports, exports, etc.
})
export class CoreModule {
  // This is the constructor guard
  constructor(@Optional() @SkipSelf() parentModule?: CoreModule) {
    if (parentModule) {
      throw new Error(
        'CoreModule has already been loaded. Import it only in the AppModule.'
      );
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Core-guard

Type: lib (folder)

Tag: type: core

  • Guards we apply across our application, such as role-guard, auth-guard

Core-interceptor

Type: lib (folder)

Tag: type: core

  • Interceptors we apply in our application, such as response-interceptor, error-interceptor

Core-api

Tag: type: core

Type: lib (folder)

  • Api service which interacts with our back-end system, such as product-api,order-api, often @injectable({provideIn:’root’})

Core-layout

Tag: type: core

Type: lib (folder)

  • Including all layouts which is loaded at the initial page load and loading only one, such as app-header, app-layout, app-footer

Shared-ui-[componet-name]

Tag: type: ui

Type: lib (folder)

  • Components that we intend to share across our application. Keep it dump as much as possible, such as shared-ui-button, shared-ui-dropdown.

Shared-directive-[directive-name]

Tag: type: directive

Type: lib (folder)

  • Directives which we intend to share across our application, such as shared-directive-hover, shared-directive-double-click, keep it separate, avoid putting it together

Shared-pipe-[pipe-name]

Tag: type: pipe

Type: lib (folder)

  • Pipes which we intend to share across our application, such as shared-pipe-percent, shared-pipe-upper-case, keep it separate, avoid putting it together

Feature-[feature-name]

Tag: type: feature

Type: lib (folder)

I strictly follow the DDD (Domain Driven Development) approach. There are 2 dimensions:

  • Horizontal: Each domain business has sub-domains/features. For instance, a customer domain has sub-domains/features: customer-profile, customer-list, customer-order. We have the structure below:

    ├── apps
       └── my-app
    └── libs
        ├── customer
           ├── feature-detail
           └── feature-list
    
  • Vertical: Inside each domain/sub-domains involves these parts:

    • pipe
    • directive
    • service
    • component
    • constant
    • helper
    feature-name/
    ├── constant/        // For feature-specific constants or enums
    ├── directive/
    ├── helper/          // For utility classes or pure functions
    ├── pipe/
    ├── service/
    └── feature-name.component.ts  // The main component
    

These ones are used internally or shared with sub-domains

Conclusion

Above, I shared my preferred Angular structure with Nx. Well-structured code is the “key” for building maintainable, scalable applications. In the next post, I will share my boundaries setup (Nx workspace). If you have another viewpoint or adjustment, feel free to drop a comment.

Top comments (0)