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...
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.
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).
L - Liskov Substitution 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.
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.');
}
}
=> 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
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.
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.');
}
}
=> 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
Post a Comment