Tổ chức dự án NestJS theo mô hình Mono Repo: Khi nào, tại sao và cách triển khai?

0 0 0

Người đăng: Nguyễn Văn Huy

Theo Viblo Asia

NestJS đã và đang chứng tỏ sức mạnh của mình trong việc xây dựng các ứng dụng backend hiệu quả nhờ vào kiến trúc mô-đun rõ ràng. Tuy nhiên, khi dự án của bạn không còn chỉ là một API đơn thuần mà bắt đầu phình to với nhiều thành phần phức tạp, việc quản lý tất cả trong một cấu trúc project đơn lẻ sẽ dần bộc lộ những giới hạn.

Đây chính là lúc mô hình Mono Repo tỏa sáng.

Bài viết này sẽ cùng bạn đi sâu vào mô hình Mono Repo trong NestJS, giúp bạn trả lời các câu hỏi:

  • Mono Repo thực chất là gì và khác gì so với Multi Repo?
  • Khi nào là thời điểm vàng để áp dụng Mono Repo cho dự án NestJS của bạn?
  • Cách triển khai và cấu trúc một dự án NestJS theo mô hình Mono Repo như thế nào?
  • Những lợi ích, thách thức và công cụ hỗ trợ nào cần biết khi áp dụng?

I. Mono Repo là gì?

1. Định nghĩa

Mono Repo (Monolithic Repository) là một chiến lược quản lý mã nguồn, trong đó nhiều dự án, package, hoặc module khác nhau cùng được lưu trữ và quản lý trong một repository duy nhất.

Điều quan trọng là mặc dù nằm chung một "nhà", mỗi module trong Mono Repo có thể được build, test, và lint một cách độc lập. Đồng thời, chúng vẫn có khả năng chia sẻ mã nguồn chung (ví dụ: utils, DTOs, configs) một cách cực kỳ dễ dàng.

2. So sánh với Multi Repo

Để hiểu rõ hơn, hãy xem bảng so sánh nhanh giữa Mono Repo và Multi Repo (mỗi dự án/module một repository riêng).

Tiêu chí Mono Repo Multi Repo
Chia sẻ code Rất dễ dàng và trực tiếp. Khó khăn, thường phải qua package manager (npm, yarn) và quản lý version phức tạp.
CI/CD Dễ dàng đồng bộ, có thể deploy nhiều thành phần cùng lúc. Khó đồng bộ hóa phiên bản và quy trình deploy giữa các repo.
Quản lý Dependencies Dùng chung node_modules, dễ quản lý và nhất quán. Mỗi repo có node_modules riêng, dễ gây ra xung đột phiên bản.
Quy mô nhỏ Có thể hơi cồng kềnh và phức tạp nếu không cần thiết. Đơn giản, dễ tiếp cận và quản lý ban đầu.
Refactor xuyên dự án Dễ dàng vì mọi thứ đều nằm trong tầm tay của IDE. Cực kỳ phức tạp, phải thay đổi và cập nhật ở nhiều repo.

II. Khi nào nên dùng Mono Repo trong NestJS?

Không phải dự án nào cũng cần đến Mono Repo. Nhưng nếu bạn thấy dự án của mình có những dấu hiệu dưới đây, đây là lúc nên nghiêm túc cân nhắc:

1. Dự án có nhiều thành phần logic độc lập

Khi ứng dụng của bạn không chỉ là một REST API, mà là một hệ thống bao gồm nhiều dịch vụ con:

  • API chính (REST, GraphQL) cho client.
  • Background Workers / Jobs để xử lý các tác vụ nền (gửi email, xử lý video...).
  • Websocket Server cho giao tiếp real-time.
  • Cronjobs để chạy các tác vụ định kỳ.
  • Các Microservice giao tiếp với nhau qua gRPC, Kafka, Redis...

2. Có nhu cầu cao về chia sẻ và tách biệt

  • Chia sẻ code: Bạn muốn tái sử dụng code một cách triệt để giữa các thành phần trên (ví dụ: các file DTO, entities/models, utils, guards, interceptors, services chung).
  • Tách biệt trách nhiệm: Bạn muốn cấu trúc của dự án phải phản ánh rõ ràng domain nghiệp vụ. Mỗi service có một "lãnh địa" riêng, dễ dàng cho các team khác nhau phát triển song song.
  • Chạy độc lập: Mỗi service/module có khả năng chạy riêng biệt (standalone), giúp cho việc phát triển và gỡ lỗi trở nên nhanh chóng hơn.

III. Gợi ý cấu trúc thư mục NestJS theo Mono Repo

Đây là một cấu trúc thư mục phổ biến và hiệu quả mà bạn có thể áp dụng.

my-nest-monorepo/
├── apps/
│ ├── api/ # Ứng dụng REST API chính
│ │ ├── src/
│ │ │ ├── main.ts
│ │ │ └── app.module.ts
│ │ └── tsconfig.app.json
│ ├── jobs/ # Ứng dụng xử lý background job
│ │ ├── src/
│ │ │ ├── main.ts
│ │ │ └── jobs.module.ts
│ │ └── tsconfig.app.json
│ └── websocket/ # Ứng dụng websocket
│ ├── src/
│ │ ├── main.ts
│ │ └── websocket.module.ts
│ └── tsconfig.app.json
├── libs/
│ ├── common/ # Thư viện chung: DTOs, interceptors, decorators
│ │ └── src/
│ │ ├── index.ts
│ │ └── ...
│ ├── database/ # Thư viện quản lý database: TypeORM/Prisma, entities
│ │ └── src/
│ │ ├── index.ts
│ │ └── ...
│ └── utils/ # Thư viện chứa các hàm tiện ích
│ └── src/
│ ├── index.ts
│ └── ...
├── nest-cli.json
├── package.json
└── tsconfig.json

Giải thích:

  • apps/: Chứa các ứng dụng độc lập. Mỗi thư mục con trong apps là một project NestJS hoàn chỉnh, có main.ts riêng và có thể được khởi chạy một cách riêng biệt. Chúng được xem như là "entry point" của hệ thống.
  • libs/: Chứa các thư viện dùng chung. Đây là nơi bạn đặt những mã nguồn được chia sẻ giữa các ứng dụng trong apps. Các thư viện này không thể tự chạy mà sẽ được import vào các ứng dụng thông qua path alias (ví dụ: @myorg/common).

IV. Công cụ hỗ trợ: Nx vs Tự cấu hình

1. Nx

Nx là một bộ công cụ build thông minh và mở rộng được cho các Mono Repo. NestJS có sự tích hợp sâu sắc và khuyến nghị sử dụng Nx.

  • CLI mạnh mẽ: Cung cấp CLI để tạo workspace, app, lib một cách tự động và chuẩn hóa.
  • Dependency Graph: Nx hiểu được sự phụ thuộc giữa các app và lib trong repo của bạn.
  • Tối ưu hóa tác vụ: Hỗ trợ các lệnhaffected:* (ví dụ: nx affected:build), chỉ build/test/lint những phần bị ảnh hưởng bởi thay đổi của bạn, giúp tiết kiệm thời gian CI/CD một cách đáng kinh ngạc.
  • Tự động cấu hình: Tự động tạo và quản lý path alias, cấu hình TypeScript, Jest, ESLint cho từng app/lib.

2. Tự cấu hình thủ công

Nếu không muốn phụ thuộc vào Nx, bạn hoàn toàn có thể tự cấu hình một Mono Repo. Cách này cho bạn sự linh hoạt tối đa nhưng đòi hỏi nhiều công sức hơn.

  • Bạn sẽ phải tự tổ chức cấu trúc thư mục như trên.
  • Tự cấu hình tsconfig.json với baseUrlpaths để tạo path alias.
  • Tự quản lý các file tsconfig.build.json, webpack (nếu cần), và các script trong package.json để build từng app.

V. Hướng dẫn setup Mono Repo NestJS

1. Setup với Nx

Bước 1: Cài đặt workspace

npx create-nx-workspace@latest my-workspace --preset=apps
cd my-workspace

Bước 2: Cài plugin hỗ trợ NestJS

npm install -D @nx/nest

Bước 3: Tạo app NestJS

npx nx g @nx/nest:app api

Bước 4: Tạo thư viện chia sẻ (libs)

Lệnh này tạo một thư viện trong libs/common.

npx nx g @nx/nest:lib common

Bước 5: Import lib trong app

Nx đã tự động cấu hình path alias qua tsconfig.base.json. Trong ../api/src/app.module.ts, bạn có thể import từ lib một cách dễ dàng:

import { CommonModule } from '@my-workspace/common';

2. Setup thủ công

Bước 1: Cấu trúc thư mục

Tạo các thư mục appslibs bằng tay.

Bước 2: Cấu hình TypeScript Path Alias

Chỉnh sửa file tsconfig.json ở thư mục gốc:

{ "compilerOptions": { "baseUrl": ".", "paths": { "@common/*": ["libs/common/src/*"], "@database/*": ["libs/database/src/*"], "@utils/*": ["libs/utils/src/*"] } }
}

Bước 3: Cấu hình build

Bạn sẽ cần cấu hình nest-cli.json để NestJS CLI hiểu được cấu trúc mono repo và build các app một cách chính xác. Mỗi app sẽ có một file main.ts riêng và script build riêng trong package.json.

VI. Lợi ích và Thách thức

Lợi ích:

Quản lý đồng bộ: Dễ dàng quản lý phiên bản và dependencies cho toàn bộ hệ thống.

Tái sử dụng code tối đa: Chia sẻ logic, DTOs, và configurations một cách liền mạch.

CI/CD hiệu quả: Dễ dàng tối ưu hóa pipeline, đặc biệt khi dùng các công cụ như Nx.

Dễ dàng mở rộng: Thêm một app/service mới vào hệ thống rất nhanh chóng và nhất quán.

Phát triển song song: Các team có thể làm việc trên các app/lib khác nhau mà không ảnh hưởng nhiều đến nhau.

Thách thức:

courbe d'apprentissage (Đường cong học tập): Cần thời gian để team làm quen với quy trình và công cụ của Mono Repo.

Quy trình phức tạp hơn: CI/CD cần được cấu hình cẩn thận hơn, đặc biệt nếu không dùng Nx.

Nguy cơ "God Repo": Nếu không phân tách domain và trách nhiệm rõ ràng, repository có thể trở nên quá lớn và khó quản lý.

Thời gian build: Nếu không tối ưu, thời gian build toàn bộ repo có thể rất lâu.

VII. Kết luận

Mono Repo không phải là viên đạn bạc, nhưng nó là một chiến lược cực kỳ hợp lý và mạnh mẽ để tổ chức các dự án NestJS có quy mô lớn và phức tạp. Việc phân tách dự án thành các apps độc lập và các libs dùng chung giúp cho mã nguồn trở nên rõ ràng, dễ bảo trì, dễ kiểm thử và CI/CD mượt mà hơn rất nhiều.

Dù bạn chọn sử dụng Nx để tự động hóa hay tự mình cấu hình, tư duy phân chia này sẽ đặt nền móng vững chắc cho dự án của bạn phát triển trong tương lai. Nếu bạn đang đứng trước ngưỡng cửa mở rộng một API NestJS, hãy cân nhắc Mono Repo trước khi quá muộn.

Tài liệu tham khảo

NestJS Mono Repo Docs

Nx.dev - Get Started with NestJS

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tìm hiểu về NestJS (Phần 2)

Trong bài viết trước, mình đã giới thiệu về NestJS và các thành phần cơ bản của framework này cũng như xây dựng demo một api bằng NestJS. Như mình đã giới thiệu, NestJS có một hệ sinh thái hỗ trợ cho chúng ta trong quá trình phát triển mà các framework khác như Express, Fastify,... phải tự build hoặ

0 0 176

- vừa được xem lúc

NestJS - tìm hiểu và sử dụng Pipes

I. Giới thiệu.

0 0 46

- vừa được xem lúc

Authentication Với NestJS và Passport (Phần 1)

Authentication, hay xác thực thông tin người dùng, là một trong những tính năng cơ bản nhất của phần lớn ứng dụng Web. Trong bài viết này, mình xin chia sẻ phương pháp sử dụng passportjs để xây dựng t

0 0 101

- vừa được xem lúc

Authentication Với NestJS và Passport (Phần 2)

I. Giới thiệu.

0 0 181

- vừa được xem lúc

Middleware, Interceptor and Pipes in Nestjs

Middleware, Interceptor và Pipes củng không quá xa lạ với những anh em code Nestjs.Nhưng ai trong.

0 0 179

- vừa được xem lúc

NestJS - framework thần thánh cho Nodejs

Đọc thì có vẻ giật tít nhưng khoan, mọi chuyện không như bạn nghĩ, hãy nghe mình giải thích . 1.

0 0 66