nestjs-boilerplate

Architecture


Table of Contents


Hexagonal Architecture

NestJS Boilerplate is based on Hexagonal Architecture. This architecture is also known as Ports and Adapters.

Hexagonal Architecture Diagram

Motivation

The main reason for using Hexagonal Architecture is to separate the business logic from the infrastructure. This separation allows us to easily change the database, the way of uploading files, or any other infrastructure without changing the business logic.

Description of the module structure

.
├── domain
│   └── [DOMAIN_ENTITY].ts
├── dto
│   ├── create.dto.ts
│   ├── find-all.dto.ts
│   └── update.dto.ts
├── infrastructure
│   └── persistence
│       ├── document
│       │   ├── document-persistence.module.ts
│       │   ├── entities
│       │   │   └── [SCHEMA].ts
│       │   ├── mappers
│       │   │   └── [MAPPER].ts
│       │   └── repositories
│       │       └── [ADAPTER].repository.ts
│       ├── relational
│       │   ├── entities
│       │   │   └── [ENTITY].ts
│       │   ├── mappers
│       │   │   └── [MAPPER].ts
│       │   ├── relational-persistence.module.ts
│       │   └── repositories
│       │       └── [ADAPTER].repository.ts
│       └── [PORT].repository.ts
├── controller.ts
├── module.ts
└── service.ts

[DOMAIN ENTITY].ts represents an entity used in the business logic. Domain entity has no dependencies on the database or any other infrastructure.

[SCHEMA].ts represents the database structure. It is used in the document-oriented database (MongoDB).

[ENTITY].ts represents the database structure. It is used in the relational database (PostgreSQL).

[MAPPER].ts is a mapper that converts database entity to domain entity and vice versa.

[PORT].repository.ts is a repository port that defines the methods for interacting with the database.

[ADAPTER].repository.ts is a repository that implements the [PORT].repository.ts. It is used to interact with the database.

infrastructure folder - contains all the infrastructure-related components such as persistence, uploader, senders, etc.

Each component has port and adapters. Port is interface that define the methods for interacting with the infrastructure. Adapters are implementations of the port.

Recommendations

Repository

Don’t try to create universal methods in the repository because they are difficult to extend during the project’s life. Instead of this create methods with a single responsibility.

// ❌
export class UsersRelationalRepository implements UserRepository {
  async find(condition: UniversalConditionInterface): Promise<User> {
    // ...
  }
}

// ✅
export class UsersRelationalRepository implements UserRepository {
  async findByEmail(email: string): Promise<User> {
    // ...
  }
  
  async findByRoles(roles: string[]): Promise<User> {
    // ...
  }
  
  async findByIds(ids: string[]): Promise<User> {
    // ...
  }
}

FAQ

Is there a way to generate a new resource (controller, service, DTOs, etc) with Hexagonal Architecture?

Yes, you can use the CLI to generate a new resource with Hexagonal Architecture.



Previous: Installing and Running

Next: Command Line Interface