Chapter 13. Architecture

As you now know, Nest.js is based on the same principles as Angular, so it is a good idea to have a similar structure as Angular’s.

Before going into the file structure, we will see some guidelines about the naming and about how to structure our different directories and files in order to have an easy and more readable project.

We will take a look at the architecture for two different type of projects:

  • A server application
  • A more complete app using Angular universal with Nest.js and Angular 6

By the end of the chapter, you should know how to structure your app either for a server application or a complete app with a client front-end.

Style guide of naming conventions

In this part, we will see the naming conventions that we can use in order to have better maintainability and readability. For each decorator, you should use the name with a hyphen for a composed name, followed by a dot and the name of the decorator or object to which it corresponds.

Controller

The naming of the controller should respect the following principle:

user.controller.ts

@Controller()
export class UserController { /* ... */ }

Service

The naming of the service should respect the following principle:

user.service.ts

@Injectable()
export class UserService { /* ... */ }

Module

The naming of the module should respect the following principle:

user.module.ts

@Module()
export class UserModule { /* ... */ }

Middleware

The naming of the middleware should respect the following principle:

authentication.middleware.ts

@Injectable()
export class AuthenticationMiddleware { /* ... */ }

Exception filter

The naming of the exception filter should respect the following principle:

forbidden.exception.ts

export class ForbiddenException { /* ... */ }

Pipe

The naming of the pipe should respect the following principle:

validation.pipe.ts

@Injectable()
export class ValidationPipe { /* ... */ }

Guard

The naming of the guard should respect the following principle:

roles.guard.ts

@Injectable()
export class RolesGuard { /* ... */ }

Interceptor

The naming of the interceptor should respect the following principle:

logging.interceptor.ts

@Injectable()
export class LoggingInterceptor { /* ... */ }

Custom decorator

The naming of the custom decorator should respect the following principle:

comment.decorator.ts

export const Comment: (data?: any, ...pipes: Array<PipeTransform<any>>) => {
    ParameterDecorator = createParamDecorator((data, req) => {
        return req.comment;
    }
};

Gateway

The naming of the gateway should respect the following principle:

comment.gateway.ts

@WebSocketGateway()
export class CommentGateway {

Adapter

The naming of the adapter should respect the following principle:

ws.adapter.ts

export class WsAdapter {

Unit test

The naming of the unit test should respect the following principle:

user.service.spec.ts

E2E test

The naming of the e2e test should respect the following principle:

user.e2e-spec.ts

Now we have overviewed the tools provided by Nest.js and have put in place some naming guidelines. We can now move onto the next part.

Directory structure

It is important to have a project with a well-structured directory file in order for it to be much more readable, understandable, and easy to work with.

So, let’s see how we can structure our directory in order for it to be more clear. You will see in the following example the directory file architecture used for the repository, which has been created for this book using the naming convention described in the previous section.

Server architecture

For the server architecture, you will see a proposed architecture used for the repository to have clean directories.

Complete overview

See the base file structure without entering into too much detail:

.
├── artillery/
├── scripts/
├── migrations/
├── src/
├── Dockerfile
├── README.md
├── docker-compose.yml
├── migrate.ts
├── nodemon.json
├── package-lock.json
├── package.json
├── tsconfig.json
├── tslint.json
└── yarn.lock

We have four folders for this server that contain all of the files that we need for a complete server:

  • artillery directory, if you need this it can contain all of the scenarios to test some end points of your API.
  • scripts directory will contain all of the scripts that you need to use in your application. In our case the script to wait for the port used by RabbitMQ to open in order that the Nest.js application waits before starting.
  • migrations A directory exists because we use sequelize and we have written some migration files that are stocked in this directory.
  • src directory, which will contain all of the code for our server application.

In the repository, we also have a client directory. In this case, however, this one is only used as a sample of web socket usage.

The src directory

The src directory will contain all of the application modules, configurations, gateways and more. Let’s take a look at this directory:

src
├── app.module.ts
├── main.cluster.ts
├── main.ts
├── gateways
│   ├── comment
│   └── user
├── modules
│   ├── authentication
│   ├── comment
│   ├── database
│   ├── entry
│   ├── keyword
│   └── user
└── shared
    ├── adapters
    ├── config
    ├── decorators
    ├── exceptions
    ├── filters
    ├── guards
    ├── interceptors
    ├── interfaces
    ├── middlewares
    ├── pipes
    └── transports

This directory will also have to be well-structured. For this, we have created three sub-directories that correspond to the web socket gateways, which have all been put in the gateways directory. The modules will contain all of the modules needed for the application. Finally, shared will contain all of the shared content as its name suggests, corresponding with all of the adapters, config files, and decorators for the custom decorators and elements that can be used everywhere without belonging to any module in particular.

Now we will dive into the modules directory.

Modules

The main part of your application will be structured as a module. This module will contain many different files. Let’s have a look at how a module can be structured:

src/modules
├── authentication
│   ├── authentication.controller.ts
│   ├── authentication.module.ts
│   ├── authentication.service.ts
│   ├── passports
│   │   └── jwt.strategy.ts
│   └── tests
│       ├── e2e
│       │   └── authentication.controller.e2e-spec.ts
│       └── unit
│           └── authentication.service.spec.ts
├── comment
│   ├── comment.controller.ts
│   ├── comment.entity.ts
│   ├── comment.module.ts
│   ├── comment.provider.ts
│   ├── comment.service.ts
│   ├── interfaces
│   │   ├── IComment.ts
│   │   ├── ICommentService.ts
│   │   └── index.ts
│   └── tests
│       ├── unit
│       │   └── comment.service.spec.ts
│       └── utilities.ts
├── database
│   ├── database-utilities.service.ts
│   ├── database.module.ts
│   └── database.provider.ts
├── entry
│   ├── commands
│   │   ├── handlers
│   │   │   ├── createEntry.handler.ts
│   │   │   ├── deleteEntry.handler.ts
│   │   │   ├── index.ts
│   │   │   └── updateEntry.handler.ts
│   │   └── impl
│   │       ├── createEntry.command.ts
│   │       ├── deleteEntry.command.ts
│   │       └── updateEntry.command.ts
│   ├── entry.controller.ts
│   ├── entry.entity.ts
│   ├── entry.model.ts
│   ├── entry.module.ts
│   ├── entry.provider.ts
│   ├── entry.service.ts
│   ├── interfaces
│   │   ├── IEntry.ts
│   │   ├── IEntryService.ts
│   │   └── index.ts
│   └── tests
│       ├── unit
│       │   └── entry.service.spec.ts
│       └── utilities.ts
├── keyword
│   ├── commands
│   │   ├── handlers
│   │   │   ├── index.ts
│   │   │   ├── linkKeywordEntry.handler.ts
│   │   │   └── unlinkKeywordEntry.handler.ts
│   │   └── impl
│   │       ├── linkKeywordEntry.command.ts
│   │       └── unlinkKeywordEntry.command.ts
│   ├── events
│   │   ├── handlers
│   │   │   ├── index.ts
│   │   │   └── updateKeywordLinks.handler.ts
│   │   └── impl
│   │       └── updateKeywordLinks.event.ts
│   ├── interfaces
│   │   ├── IKeyword.ts
│   │   ├── IKeywordService.ts
│   │   └── index.ts
│   ├── keyword.controller.ts
│   ├── keyword.entity.ts
│   ├── keyword.module.ts
│   ├── keyword.provider.ts
│   ├── keyword.sagas.ts
│   ├── keyword.service.ts
│   └── keywordEntry.entity.ts
└── user
    ├── interfaces
    │   ├── IUser.ts
    │   ├── IUserService.ts
    │   └── index.ts
    ├── requests
    │   └── create-user.request.ts
    ├── tests
    │   ├── e2e
    │   │   └── user.controller.e2e-spec.ts
    │   ├── unit
    │   │   └── user.service.spec.ts
    │   └── utilities.ts
    ├── user.controller.ts
    ├── user.entity.ts
    ├── user.module.ts
    ├── user.provider.ts
    └── user.service.ts

In our repository, we have many modules. Some of them also implement the cqrs, and it is in the same directory as the module, because it concerns the module and is part of it. The cqrs parts are separated into the commands and events directories. A module can also define some interfaces and these are put into a separate interfaces directory. Separate directories allow us to have something that is much more readable and clear without having lots of different files mixed together. Of course, all of the tests concerning the modules are also included in their own directory tests and separated into the unit and e2e.

Finally, the main files defining the module itself, including the injectables, the controllers, and the entity, are in the root of the module directory.

In this section, we have seen how to structure our server application in a way to keep it clearer and much more readable. You now know where to put all of your modules and how to structure a module, as well as where to put your gateways or your shared files if you used them.

Angular Universal architecture

The Angular Universal part of the repository is a standalone application, using the Nest.js server and Angular 6. It will be composed of just two main directories: e2e for the end-to-end tests and the src, which contains the server and the client.

Let’s start by seeing an overview of this architecture:

├── e2e/
├── src/
├── License
├── README.md
├── angular.json
├── package.json
├── tsconfig.json
├── tslint.json
├── udk.container.js
└── yarn.lock

The src directory

This directory will contain the app directory in order to put our client content with the Angular architecture using modules. Also, we will find the environments, which define if we are in production mode or not exporting constant. This environment will be replaced by the production environment config for the production mode, and then the server and shared directories. The shared directory allows us to share some files as an interface, for example, and the server directory will contain all the server applications as we have seen in the previous section.

But in this case, the server has changed a bit and now looks like this:

├── main.ts
├── app.module.ts
├── environments
│   ├── environment.common.ts
│   ├── environment.prod.ts
│   └── environment.ts
└── modules
    ├── client
    │   ├── client.constants.ts
    │   ├── client.controller.ts
    │   ├── client.module.ts
    │   ├── client.providers.ts
    │   ├── interfaces
    │   │   └── angular-universal-options.interface.ts
    │   └── utils
    │       └── setup-universal.utils.ts
    └── heroes
        ├── heroes.controller.ts
        ├── heroes.module.ts
        ├── heroes.service.ts
        └── mock-heroes.ts

The modules directory will contain all of the Nest.js modules, exactly as we have seen in the previous section. One of the modules is the client module and will serve the Universal app and all of the required assets, as well as setting up the initializer to set the engine and provide some Angular configurations.

Regarding the environments, this one will contain all of the configuration paths related to the Angular application. This configuration references the project configured into the angular.json file seen in the base of the previous section’s project.

Summary

This chapter allows you to set up the architecture of your application in a way that is much more understandable, readable and easier to work with. We have seen how to define the architecture’s directories for a server application, but also for a complete application using Angular Universal. With these two examples, you should be able to build your own project in a clearer way.

The next chapter shows how to use testing in Nest.js.