Skip to main content

Áp dụng SOLID trong NestJS

NestJS cung cấp kiến trúc module cho phép các nhà phát triển có thể tái sử dụng và kiểm tra được code. NestJS cũng hỗ trợ TypeScript, cung cấp khả năng kiểm tra kiểu tĩnh và IntelliSense để các developer có năng suất tốt hơn. NestJS được xây dựng dựa trên Express, một framework web phổ biến cho Node.js. NestJS cũng cung cấp CLI mạnh mẽ giúp dễ dàng tạo project, modules, controllers và services. NestJS là framework Node.js được sử dụng để xây dựng các ứng dụng có thể mở rộng và bảo trì. Nó dựa trên các nguyên tắc lập trình hướng đối tượng và các mẫu thiết kế, bao gồm các nguyên tắc SOLID. S.O.L.I.D là từ viết tắt đại diện cho 5 nguyên tắc thiết kế trong phát triển phần mềm. Những nguyên tắc này được Robert C. Martin (còn được gọi là Bác Bob) giới thiệu như những hướng dẫn để tạo ra các hệ thống phần mềm linh hoạt và có thể bảo trì. Mỗi chữ cái trong từ viết tắt tương ứng với một nguyên tắc cụ thể: S - Single Responsibility Principle (SRP) : Nguyên tắc này nêu rõ rằng một lớp hoặc mo...

Áp dụng SOLID trong NestJS


NestJS cung cấp kiến trúc module cho phép các nhà phát triển có thể tái sử dụng và kiểm tra được code. NestJS cũng hỗ trợ TypeScript, cung cấp khả năng kiểm tra kiểu tĩnh và IntelliSense để các developer có năng suất tốt hơn. NestJS được xây dựng dựa trên Express, một framework web phổ biến cho Node.js. NestJS cũng cung cấp CLI mạnh mẽ giúp dễ dàng tạo project, modules, controllers và services.

NestJS là framework Node.js được sử dụng để xây dựng các ứng dụng có thể mở rộng và bảo trì. Nó dựa trên các nguyên tắc lập trình hướng đối tượng và các mẫu thiết kế, bao gồm các nguyên tắc SOLID.

S.O.L.I.D là từ viết tắt đại diện cho 5 nguyên tắc thiết kế trong phát triển phần mềm. Những nguyên tắc này được Robert C. Martin (còn được gọi là Bác Bob) giới thiệu như những hướng dẫn để tạo ra các hệ thống phần mềm linh hoạt và có thể bảo trì. Mỗi chữ cái trong từ viết tắt tương ứng với một nguyên tắc cụ thể:

S - Single Responsibility Principle (SRP): Nguyên tắc này nêu rõ rằng một lớp hoặc module chỉ nên có một lý do để thay đổi. Điều đó có nghĩa là một lớp nên có một trách nhiệm hoặc mục đích duy nhất và nó phải gói gọn trách nhiệm đó. Bằng cách tuân thủ SRP, bạn có thể làm cho code của mình dễ hiểu, duy trì và kiểm tra hơn.

=> VD: Chúng ta sẽ tạo CatsController 
có một trách nhiệm duy nhất: xử lý các yêu cầu HTTP liên quan đến Cats:
import {
  Controller,
  Delete,
  Get,
  Param,
  ParseIntPipe,
  Post,
  Put,
} from '@nestjs/common';
import { VERSION_NUMBER, EPath } from 'src/constant/api';
import { CatsService } from './cats.service';
import { ApiTags } from '@nestjs/swagger';

@Controller({
  path: EPath.CATS,
  version: VERSION_NUMBER,
})
@ApiTags(EPath.CATS)
export class CatsController {
  constructor(readonly catService: CatsService) {}

  @Get()
  async getCats() {
    return this.catService.getCats();
  }

  @Post()
  async createCat() {
    return this.catService.createCat();
  }

  @Put(':id')
  async updateCat(@Param('id', ParseIntPipe) id: number) {
    return this.catService.updateCat(id);
  }

  @Delete(':id')
  async deleteCat(@Param('id', ParseIntPipe) id: number) {
    return this.catService.deleteCat(id);
  }
}

 

O — Open/Closed Principle (OCP): Nguyên tắc này gợi ý rằng các thực thể phần mềm (lớp, module, chức năng, v.v.) phải mở để mở rộng nhưng đóng để sửa đổi. Nói cách khác, bạn sẽ có thể mở rộng hoạt động của hệ thống mà không cần sửa đổi mã hiện có của nó. Điều này thường đạt được thông qua việc sử dụng các tính chất trừu tượng (abstract), giao diện (interface) và tính đa hình (polymorphism).

=> VD: Chúng ta sẽ tạo một CatsService để xử lý logic liên quan đến các chú mèo. Service này sẽ có interface là ICatService sẽ xác định hợp đồng mà service phải tuân thủ. Điều này sẽ cho phép chúng ta dễ dàng hoán đổi việc triển khai CatsService bằng một triển khai khác đáp ứng cùng một hợp đồng (contract).
ICat.interface.ts
export interface ICat {
  id: number;
  name: string;
  height: number;
  weight: number;
  color: string;
  deleted_at?: Date;
  created_at: Date;
  updated_at: Date;
  created_by?: number;
  updated_by?: number;
}

ICatService.interface.ts
import { ICat } from './ICat.interface';

export interface ICatService {
  getCats(): Promise<ICat[]>;
  createCat(): Promise<ICat>;
  updateCat(id: number): Promise<ICat>;
  deleteCat(id: number): Promise<void>;
}

cats.service.ts
import { Injectable } from '@nestjs/common';
import { ICatService } from './interfaces/ICatService.interface';
import { ICat } from './interfaces/ICat.interface';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class CatsService implements ICatService {
  constructor(readonly prisma: PrismaService) {}

  async getCats(): Promise<ICat[]> {
    const cats = await this.prisma.cats.findMany();
    return cats;
  }

  createCat(): Promise<ICat> {
    throw new Error('Method not implemented.');
  }

  updateCat(id: number): Promise<ICat> {
    throw new Error('Method not implemented.');
  }

  deleteCat(id: number): Promise<void> {
    throw new Error('Method not implemented.');
  }
}


L - Liskov Subs
titution Principle (LSP): Nguyên tắc này nêu rõ rằng các đối tượng của lớp cha (superclass) phải được thay thế bằng các đối tượng của các lớp con (subclasses) của nó mà không ảnh hưởng đến tính chính xác của chương trình. Vi phạm LSP có thể dẫn đến hành vi không mong muốn và lỗi trong code.

=> VD: Vì CatsService triển khai (implement) interface ICatService nên chúng ta có thể dễ dàng hoán đổi nó bằng một triển khai khác đáp ứng cùng một hợp đồng. Ví dụ: chúng ta có thể tạo MockCatService cho việc thử nghiệm triển khai cùng giao diện ICatService.

mock-cats.service.ts
import { Injectable } from '@nestjs/common';
import { ICatService } from './interfaces/ICatService.interface';
import { ICat } from './interfaces/ICat.interface';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class MockCatsService implements ICatService {
  constructor(readonly prisma: PrismaService) {}

  async getCats(): Promise<ICat[]> {
    const cats = await this.prisma.cats.findMany();
    return cats;
  }

  createCat(): Promise<ICat> {
    throw new Error('Method not implemented.');
  }

  updateCat(id: number): Promise<ICat> {
    throw new Error('Method not implemented.');
  }

  deleteCat(id: number): Promise<void> {
    throw new Error('Method not implemented.');
  }
}

I - Interface Segregation Principle (ISP): ISP nhấn mạnh rằng Client không nên bị ép buộc phải phụ thuộc vào các interface mà họ không sử dụng. Nó thúc đẩy ý tưởng tạo ra các interface cụ thể và tập trung, phù hợp với nhu cầu của Client sử dụng chúng. Nguyên tắc này giúp giảm thiểu tác động của những thay đổi trong một phần của hệ thống lên các phần không liên quan.

=> VD: Chúng ta đã định nghĩa interface ICatsService cụ thể cho nhu cầu của CatsController. Điều này cho phép chúng ta tạo ra các triển khai CatsService đáp ứng cùng một hợp đồng nhưng cũng cho phép chúng tôi tạo các triển khai ICatService khác cho các mục đích khác.

D - Dependency Inversion Principle (DIP): DIP nói rằng các module cấp cao không nên phụ thuộc vào các module cấp thấp; cả hai nên phụ thuộc vào sự trừu tượng (abstraction). Nguyên tắc này khuyến khích việc sử dụng các interface hoặc các lớp trừu tượng (abtract class) để xác định liên kết giữa các module, cho phép linh hoạt, tách rời và kiểm tra dễ dàng hơn. Bằng cách phụ thuộc vào sự trừu tượng thay vì triển khai cụ thể, bạn có thể đảo ngược luồng điều khiển truyền thống và đạt được sự kết hợp lỏng lẻo (loose coupling).


    Comments

    Popular posts from this blog

    10 điều cần biết dành cho Lập trình viên Java

    Kể từ khi JAVA được công bố chính thức từ năm 1995, nó đã thay đổi rất nhiều cái nhìn của chúng ta về Hệ Điều hành. Bill Gate đã từng nói đùa rằng, nó – Hệ Điều hành – không phải là phần cứng mà là phần mềm, là tương lai. (It was not about the hardware but the software which will be the future). Một thập kỷ sau, John Gage (thành viên thứ năm của SUN) đã nói, “Mạng là máy tính” (The Network is the Computer). Và điều này đã nhanh chóng được chứng minh trong thế kỷ 21 này. Tuy nhiên, JAVA đã được xây dựng không phụ thuộc vào Hệ Điều hành và được triển khai qua mạng. JAVA với công nghệ Applet đã khai sinh cho những Ứng dụng Mạng Giàu Tương tác (Rich Network Application hay còn gọi là Rich Internet Application – RIA). JAVA không hoàn hảo và chúng ta liên tục có những bản phát hành khác nhau, tuy nhiên JAVA đã khai sinh một ngôn ngữ lập trình cực kỳ phổ biến. Trở lại với nội dung bài viết, tác giả Armel Nene (Senior Java developer với kinh nghiệm làm việc trong lĩnh vực tài chí...

    Web application là gì?

    Trong kỹ thuật phần mềm, một Ứng dụng web hay webapp là một trình ứng dụng mà có thể tiếp cận qua web thông qua mạng như Internet hay intranet. Ứng dụng web phổ biến nhờ vào sự có mặt vào bất cứ nơi đâu của một chương trình. Khả năng cập nhật và bảo trì ứng dụng Web mà không phải phân phối và cài đặt phần mềm trên hàng ngàn máy tính là lý do chính cho sự phổ biến của nó. Ứng dụng web được dùng để hiện thực Webmail, bán hàng trực tuyến, đấu giá trực tuyến, wiki, diễn đàn thảo luận, Weblog, MMORPG, Hệ quản trị quan hệ khách hàng và nhiều chức năng khác. Lịch sử Trong dạng tính toán chủ-khách trước đây, mỗi ứng dụng có chương trình khách riêng của nó sẽ phục vụ như giao diện người dùng và phải được cài đặt riêng rẽ trên mỗi máy tính cá nhân của người dùng. Sự nâng cấp phần máy chủ của ứng dụng sẽ cần nâng cấp tất cả máy khách đã được cài trên mỗi máy trạm người dùng, thêm vào đó là chi phí hỗ trợ và giảm năng suất. Ngược lại, ứng dụng web linh hoạt tạo ra một loạt các tài l...