Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
November 18, 2021 10:52 am GMT

Implementing Onion architecture in NestJS

Whats Onion architecture?

As shown in the picture, onion architecture is a way to structure the code by dividing it into domain-driven design layers. each layer can only access the layer below it throw its interfaces, and then using the dependency inversion principle each interface will be replaced with its class.

onion architecture

Onion Architecture leads to more maintainable applications since it emphasizes separation of concerns throughout the system. Jeffery Palermo

Why apply Onion Architecture in NestJs projects?

NestJs is a service-side framework based on NodeJs. NestJs has many built-in features but most importantly for us now is the Dependency Injection and the possibility to add Dependency inversion which is what we need to apply the Onion Architecture.

Building simple server-side Blog:

In the rest of the article, Ill try to explain the NestJs implementation using a simple blog project.

Ill assume most of the article readers already know Nestjs so Ill focus on the architecture in the code example.

Project layers in NestJs

project layers in nestjs

  • Domain entities: in the core of our application we have the domain, according to the Domain-driven design(DDD), we should focus our implementation around our Domain and all other layers are built around it.

In our case, the Domain Entity is just the Article, so lets do its interface:

export interface IArticle {  id: number;  title: "string;"  body: string;}
  • Repository: is the layer that participates in the Domain Entity, like getting or deleting entity object but it has to abstract away database and infrastructure details, so itll work with any kind of database.

so lets build ArticleRepository:

export interface IArticleRepository {  get(id: number): Promise<IArticle>;  delete(id: number): Promise<void>;  save(input: IArticle): Promise<void>;  update(input: IArticle): Promise<IArticle>;}

As the Repository interface most likely will have the same functions for all Entities as it should mainly perform these abstract functions, I recommend using Generics that takes the Entity type as parameter to have general Repository

export interface IRepository<T> {  get(id: number): Promise<T>;  delete(id: number): Promise<void>;  save(input: T): Promise<void>;  update(input: T): Promise<T>;}
  • Service: here we implement the use cases, it participates in the Repository to get the data it needs

in our case we need a service to get an article by id and count article characters:

export interface IArticleService {  getArticle(id: number): Promise<IArticle>;  getArticleLength(id: number): Promise<number>;}
  • Controller: for the sake of simplification I made the controller the first layer, hence NestJs is a server-side framework. But normally in literature first layer suppose to be the UI or test.

As Controller is our first layer and we wont use it as dependencies somewhere else so no need to write an interface for it and we can implement it directly.

import { Controller, Get, Param } from '@nestjs/common';import { IArticle } from './article.interface';import { IArticleService } from './articleService.interface';@Controller({ path: 'article' })export class ArticleController {  constructor(private readonly service: IArticleService) {}  @Get(':id')  async article(@Param() params): Promise<IArticle> {    return this.service.getArticle(params.id);  }}

So are we done?

ofc not yet, we just wrote the interfaces but still didnt write the actual implementation of them, and then replace the interface with the class in the run time.
Ill implement one of the Classes(ArticleService) and the rest will be the same.

Implementing ArticleService

@Injectable()export class ArticleService implements IArticleService {  constructor(private readonly repository: IRepository<IArticle>) {}  getArticleLength(id: number) {    return this.repository.get(id).then((article) => article.body.length);  }  getArticle(id: number) {    return this.repository.get(id);  }}
  • must add @Injectable() so NestJs can inject it later in a class as dependency.
  • the class implements the interface to make sure the class has the same functions
  • as we see here ArticleService has dependencies like the Controller but this time its dependency is IArticleRepository

Dependency inversion( replacing the Interface by the Class):

In the ArticleModule we can specify a string token for each class and use this token when we use the class as a dependency.

so lets apply that with ArticleService, giving it a token in ArticleModule:

@Module({  controllers: [ArticleController],  providers: [    {      provide: 'ARTICLE_SERVICE_TOKEN',      useClass: ArticleService,    },  ],})export class ArticleModule {}

and using this token in the Controller to get the Class in run it:

@Controller({ path: 'article' })export class ArticleController {  constructor(    @Inject('ARTICLE_SERVICE_TOKEN')    private readonly service: IArticleService,  ) {}  ...}

Note: as strings are bound to errors, its best practice to assign the token string to a const variable and export it from the IArticleService file and then use it instead of the string directly:

export const ARTICLE_SERVICE_TOKEN = 'ARTICLE_SERVICE_TOKEN';export interface IArticleService {  getArticle(id: number): Promise<IArticle>;  getArticleLength(id: number): Promise<number>;}

thats it now imagine if we needed to change any class, well just add the new class to the useClass parameter in the Module without having to change the controller implementation.
depenciey inversion spongbob

Dont be so radical about it:

In the end, Onion Archecturie was made to make the development process easier, so dont try to force it everywhere where it does not make much sense due to some libraries limitation or other reasons.

Thanks for writing till the end and wish we meet in another article. take care!


Original Link: https://dev.to/amroabdalla00/implementing-onion-architecture-in-nestjs-1k2f

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To