How to create Angular 17 project with Nx
Tags: angular, typescript
Published: 2024-01-22 09:31:39

## Summary
- [Introduction](#introduction)
- [What is DDD?](#ddd)
- [What is NX?](#nx)
- [A little bit of NX commands](#nx-command)
   - [create project] (#comand-project)
   - [create library] (#command-library)
   - [create component] (#command-component)
- [Rick and morty project](#rick-and-morty-project)
   - [Structure Folder](#structure-folder)
   - [Solid Architecture](#solid-architecture)
   - [App project](#app-project)
   - [Shell library](#shell-library)
   - [Feature library](#feature-library)
   - [Domain library](#domain-library)
   - [Util library](#util-library)
- [Conclusion](#conclusion)

## Introduction <a id="introduction"></a>
I've always wondered, after various Angular projects, how to create organized, maintainable, and testable projects. While Angular documentation provides guidelines on writing components, directives, and pipes, sometimes it's not enough. For enterprise-level Angular projects, it falls short. I started searching on GitHub for various projects and studying several articles on Angular until someone recommended reading articles from [angular architects](https://www.angulararchitects.io/en/).

In particular, the book "Microfrontend and Moduliths with Angular" can be downloaded [here](https://www.angulararchitects.io/en/ebooks/). In one of these chapters, it explains how to structure Angular projects with Domain-Driven Design, supported by NX.

## What is DDD? <a id="ddd"></a>
Domain-Driven Design is an approach to software development that centers the development on programming a domain model that has a rich understanding of the processes and rules of a domain. The name comes from a 2003 book by Eric Evans that describes the approach through a catalog of patterns.
One of the DDD disciplines I have utilized is strategic design. *The goal of strategic design is to identify so-called sub-domains that don’t need to know much about each other*.
Now, I won't delve further into the topic, but for those interested, I recommend reading the book [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://www.amazon.it/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215).

## What is NX? <a id="nx"></a>
NX is a tool that enables deploying, building, and testing scalable JavaScript applications. It can be used to create frontend/backend projects that communicate with each other, or to build microservices applications using Node.js, or a suite of frontend projects utilizing a library of components among themselves. It's a powerful tool with a wide range of capabilities! In this case, I used it to create Domain-Driven Design projects.

## A little bit of NX commands <a id="nx-command"></a>
Here are some commands that I've used and that might be useful for you to create a monorepo project with NX.
#### Create project <a id="command-project"></a>
```bash
npx create-nx-workspace@latest <monorepo-project> --preset=angular-monorepo
```
#### Create library <a id="command-library"></a>
```bash
nx g @nx/angular:library <library-name> --tags=ui
```

#### Create component <a id="command-component"></a>
```bash
nx g @nx/angular:component <component-name> --directory=path/to/library
```
Create an Angular component with the "directory" argument that allows specifying where to create the component.

## Rick and morty project <a id="rick-and-morty-project"></a>
![Image](https://media-assets.wired.it/photos/615f231327b5f17490c59a42/master/w_1600,c_limit/wired_placeholder_dummy.png)

Who likes Rick and Morty? Some love it, and some are not telling the truth :smile:. I've created an Angular project with NX where you can see how I've structured the project. The application is not complete yet, but it demonstrates the structure an enterprise application should have. In this [repository](https://github.com/black-arm/rick-and-morty-monorepo), you'll find the project. I used [The Rick and Morty API] (https://rickandmortyapi.com/) to retrieve all the data about Rick and Morty characters.

### Structure Folder <a id="structure-folder"></a>
From the image below, you can see that there is a project named rick-and-morty-monorepo in the apps folder, and in the libs folder, I have divided them by subdomains, following the principles of Domain-Driven Design with strategic design.
![structure folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/48pubvnp0z9b3ie5qgxb.png)

the subdomains I identified are: 
- character
- location
- episode

In each subdomain, the following libraries have been created:

- **api**: Provides functionalities exposed to other domains (service classes, store).
- **ui**: Library where dumb components are located.
- **util**: Utility library containing helper functions or models available to ui/util/features libraries.
- **domain**: Implements the entire business logic, including HTTP services and data management. For data management, I used @ngrx/signals.
- **features**: Implements use cases using smart components.
- **shell**: Provides an entry point for the domain. It contains all the routes to be assigned to the rick-and-morty-monorepo application.

![structure character folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zi1gky6k7uaf779r8id0.png)

Each subdomain can utilize shared libraries, and for this reason, I have created three libraries that can be used by all the other libraries. These are:

- **shared-ui**: Shareable components across all subdomains.
- **mock**: Mock classes, objects that can be used in tests.
- **util**: Helper functions.

### Solid Architecture <a id=solid-architecture></a>
This structuring of libraries allows for the separation of responsibilities between classes/components and brings a certain order and cleanliness to the project. To maintain this robust architecture, it is necessary to limit the interaction between libraries. Rules can be established through **ESLint**:
```json
       "@nx/enforce-module-boundaries": [
          "error",
          {

            "allow": [],
            "depConstraints": [
              {
                "sourceTag": "scope:app",
                "onlyDependOnLibsWithTags": ["type:shell"]
              },
              {
                "sourceTag": "type:api",
                "onlyDependOnLibsWithTags": ["type:domain"]
              },
              {
                "sourceTag": "type:shell",
                "onlyDependOnLibsWithTags": ["type:feature", "type:util"]
              },
              {
                "sourceTag": "type:feature",
                "onlyDependOnLibsWithTags": ["type:domain", "type:util", "type:ui", "type:mock"]
              },
              {
                "sourceTag": "type:ui",
                "onlyDependOnLibsWithTags": ["type:util", "type:mock"]
              },
              {
                "sourceTag": "type:domain",
                "onlyDependOnLibsWithTags": ["type:util", "type:mock"]
              },
              {
                "sourceTag": "type:mock",
                "onlyDependOnLibsWithTags": ["type:util"]
              },
              {
                "sourceTag": "type:shared",
                "onlyDependOnLibsWithTags": []
              }
            ]
          }
        ],
```

These rules define the interaction scope between different libraries:

- **scope:app**: Applications can only communicate with shell and shared-ui libraries.
- **type:domain**: Domain libraries can use util and mock libraries for testing.
- **type:shell**: Shell libraries can use features and util libraries.
- **type:feature**: Feature libraries can use domain, util, ui, shared-ui, and mock libraries.
- **type:ui**: UI libraries can use util and mock libraries.
- **type:domain**: Domain libraries can use util and mock libraries.
- **type:mock**: Mock libraries can use the util library.

### App project <a id="app-project"></a>
In the main App project, my focus was primarily on creating basic templates. In this instance, I had to develop only one base template, featuring a drawer and footer component with the overall management of HTTP API calls.

Within the main template, `<router-outlet></router-outler>` is present, enabling the insertion of content from the shells.

```typescript
export const TEMPLATE_ROUTES: Routes = [
  {
    path: '',
    component: RickAndMortyTemplateComponent,
    children: [
      {
        path: '',
        loadChildren: () => import('@rick-and-morty-monorepo/characters/shell').then(m => m.routes)
      },
      {
        path: 'episodes',
        loadChildren: () => import('@rick-and-morty-monorepo/episodes-shell').then(m => m.EPISODES_ROUTES)
      },
      {
        path: 'locations',
        loadChildren: () => import('@rick-and-morty-monorepo/locations-shell').then(m => m.LOCATION_ROUTES)
      }
    ]
  }
]
```
As you can see, the template, based on the path, will accept three shell entry points: characters, episodes, and location.

### The shell library <a id="shell-library"></a>
The shell library contains nothing but the routes to the features within that domain.
```typescript
import { Routes } from '@angular/router';

export const routes: Routes = [
    {
        path: '',
        loadComponent: () =>
            import('./pages/characters-page/characters-page.component').then(
                (c) => c.CharactersPageComponent
            ),
    },
];
```
Of course, in this case, I only had one feature. However, in a shell, I can have many features.

### Features Library <a id="feature-library"></a>
Here We find the pages

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h4221h233xaktlilkzh4.png)

For these libraries, I've opted to use the **container-presentational pattern**. In this pattern, the container component is responsible for fetching information through the domain library and passing the result to the dumb component. The page components, on the other hand, handle the skeleton of the page.

```typescript
//card-list-container.component.ts
@Component({
    selector: 'rick-and-morty-monorepo-card-list-container',
    standalone: true,
    imports: [CardListComponent, CommonModule],
    providers: [],
    templateUrl: './card-list-container.component.html',
    styleUrl: './card-list-container.component.scss',
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CardListContainerComponent implements OnInit {
    readonly store = inject(CharacterStore);

    ngOnInit(): void {
        this.store.charactersRequest({});
    }

    changePage(page: number) {
        this.store.charactersRequest({ page: page });
    }
}
```
```html
<!--card-list-container.component.html-->
<app-card-list
    [characters]="store.characters()"
    [totalPages]="store.pages()"
    (pageSelected)="changePage($event)"
></app-card-list>
```
```html
<!--characters-page.component.html-->
<div class="p-3">
    <rick-and-morty-monorepo-card-list-container></rick-and-morty-monorepo-card-list-container>
</div>

```

### Domain library <a id="domain-library"></a>
In the domain library, you can find services that make HTTP calls and a store for data management. I've delved into @ngrx/signals for data management, and I must say it's quite impressive!

### Util library <a id="util-library"></a>
In the Util libraries, there are models that can be utilized by both the domain and container/presentational components.

## Conclusion <a id="conclusion"></a>
Strategic Design is a proven method for breaking down an application into autonomous domains. NX provides a structured way to implement these domains with different libraries grouped by domains. Restrictions enforced through the extension of eslint's enforce-module-boundaries help limit access to other domains and reduce dependencies.

These access restrictions contribute to ensuring easily maintainable systems with minimal impact on other parts of the system