Chuyển tới nội dung chính

Cấu trúc mã nguồn & quy ước làm việc

Tài liệu kỹ thuật

Trang này dành cho đội phát triển. Đi cùng Chuẩn lập trình.

Cấu trúc kho mã

adang_mvp/
├── docs/ tài liệu (trang bạn đang đọc)
├── docs-site/ bộ dựng tài liệu
├── connect/ công cụ nối Trello
├── scripts/ kịch bản triển khai, cấu hình máy chủ web
├── server/ máy chủ ứng dụng — NestJS
└── web/ giao diện — React + Vite

Hai thư mục server/web/ nằm chung một kho. Với đội nhỏ, tách kho chỉ làm việc đồng bộ thay đổi khó hơn.

Máy chủ ứng dụng

server/src/
├── main.ts
├── app.module.ts
├── prisma/
│ ├── schema.prisma
│ └── migrations/ tệp SQL, gồm phần ràng buộc viết tay
├── common/
│ ├── guards/ JwtAuthGuard, RolesGuard, ScopeGuard
│ ├── decorators/ @CurrentUser, @Roles, @Scope
│ ├── scope/ tính phạm vi dữ liệu theo vai trò
│ ├── errors/ AppError, danh sách mã lỗi
│ ├── filters/ bộ lọc lỗi toàn cục
│ ├── i18n/messages.vi.ts toàn bộ chuỗi tiếng Việt
│ └── time/vn-time.ts xử lý múi giờ, cách tính trễ hạn
├── auth/
├── users/ tài khoản, phòng ban, vai trò
├── tasks/
│ ├── tasks.controller.ts
│ ├── tasks.service.ts
│ ├── tasks.repository.ts
│ ├── task-transition.rules.ts
│ ├── comments/
│ ├── attachments/
│ └── dto/
├── dashboard/
├── notifications/
│ ├── channels/ mỗi kênh gửi một tệp
│ └── notifications.scheduler.ts
└── reports/

Bảy mô-đun khớp với năm nhóm chức năng, chỉ tách thêm auth khỏi users và tách dashboard khỏi tasks vì hai phần này chỉ đọc.

Giao diện

web/src/
├── main.tsx
├── routes/ khai báo đường dẫn và phân quyền hiển thị
├── pages/ mỗi màn hình trong wireframe một thư mục
├── components/
│ ├── ui/ lấy từ mẫu shadcn-admin
│ └── task/ thành phần riêng của nghiệp vụ
├── api/ lớp gọi máy chủ, một tệp một nhóm
├── types/ kiểu dùng chung với máy chủ
├── lib/
│ ├── permissions.ts quyết định nút nào hiện, nút nào ẩn
│ ├── format.ts định dạng ngày giờ, số
│ └── labels.ts ánh xạ mã sang chữ tiếng Việt
└── hooks/

Quy tắc phụ thuộc

Mũi tên chỉ chiều được phép gọi. Không có mũi tên ngược.

Controller ──→ Service ──→ Repository ──→ Prisma
│ │
└──→ DTO └──→ common/ (errors, i18n, time, scope)

Bốn điều cấm:

CấmVì sao
Service gọi ControllerNgược chiều, sinh phụ thuộc vòng
Controller gọi thẳng RepositoryBỏ qua tầng nghiệp vụ và tầng kiểm quyền
Repository chứa quy tắc nghiệp vụQuy tắc nằm rải hai chỗ, sửa sót một chỗ
Mô-đun này gọi Repository của mô-đun khácGọi qua Service của mô-đun đó

Ràng buộc này kiểm tự động bằng luật import/no-restricted-paths của bộ rà mã, không dựa vào trí nhớ.


Quy trình làm việc

Từ thẻ Trello đến khi gộp mã

Thẻ ở Backlog
└─ Đủ điều kiện bắt đầu → chuyển In Progress, tạo nhánh
└─ Code + kiểm thử → mở yêu cầu gộp mã, chuyển In Review
└─ Người khác duyệt → chuyển Testing
└─ Chạy thử theo điều kiện nghiệm thu, đính bằng chứng → Done

Điều kiện để bắt đầu một thẻ

Thẻ chỉ được chuyển sang In Progress khi đã có đủ:

  • Mã chức năng liên quan (FR-...) hoặc mã mô tả người dùng (US-...)
  • Điều kiện nghiệm thu đo được, không mơ hồ
  • Biết rõ đụng vào bảng nào, đường dẫn nào
  • Không bị thẻ khác chặn

Thiếu một mục thì chuyển thẻ sang ⏸ Chờ quyết định thay vì đoán.

Điều kiện để đóng một thẻ

  • Biên dịch không lỗi cả máy chủ lẫn giao diện
  • Kiểm thử của phần vừa làm chạy qua
  • Điều kiện nghiệm thu đạt hết
  • Bằng chứng đính vào thẻ — ảnh chụp màn hình hoặc kết quả chạy lệnh
  • Đã có người thứ hai duyệt mã
  • Tài liệu liên quan đã cập nhật nếu có thay đổi

Mục bằng chứng là bắt buộc. Ghi "đã xong" trong bình luận không tính.

Nhánh

main luôn ở trạng thái phát hành được
└── <loại>/<mã-thẻ>-<mô-tả-ngắn>
LoạiDùng khi
featThêm chức năng
fixSửa lỗi
refactorĐổi cấu trúc, không đổi hành vi
docsChỉ sửa tài liệu
choreCấu hình, phụ thuộc, công cụ

Ví dụ: feat/xcWfUD8c-tao-cong-viec

Thông điệp chuyển giao mã

Dòng đầu tiếng Việt, dưới 72 ký tự, mô tả thay đổi gì chứ không phải làm gì:

Thêm chức năng tạo công việc mới

- Kiểm tra đủ ba trường bắt buộc theo FR-TASK-01
- Trưởng phòng mặc định phòng mình, Ban Giám hiệu phải chọn
- Tạo công việc và người phụ trách trong cùng một giao dịch

Thẻ: https://trello.com/c/xcWfUD8c

Không viết "sửa lỗi", "cập nhật", "wip" — sáu tháng sau không ai hiểu.

Yêu cầu gộp mã

Kích thước: dưới 400 dòng thay đổi. Lớn hơn thì tách. Yêu cầu gộp 1.000 dòng chỉ nhận được lời duyệt hình thức.

Mô tả phải có:

## Việc gì
Một câu.

## Vì sao
Liên kết thẻ Trello và mã chức năng.

## Cách kiểm chứng
Các bước để người duyệt tự chạy lại.

## Ảnh hưởng
Có đổi cấu trúc bảng không? Có phá tương thích không?

Duyệt mã

Người duyệt kiểm theo thứ tự ưu tiên:

  1. Đúng nghiệp vụ chưa — đối chiếu mã chức năng và quy tắc nghiệp vụ liên quan
  2. Phạm vi dữ liệu có bị hở không — mọi truy vấn công việc phải lọc theo phạm vi
  3. Có rò dữ liệu ra ngoài không — có trả thẳng đối tượng Prisma không
  4. Xử lý lỗi — có nuốt lỗi không, thông điệp đã bằng tiếng Việt chưa
  5. Đọc hiểu được không — người khác đọc có hiểu ngay không
  6. Còn lại là góp ý, không chặn gộp mã

Ba mục đầu là chặn. Ba mục sau là góp ý.

Cách viết nhận xét: nói rõ mức độ.

CHẶN: hàm này lấy công việc không lọc theo phòng ban, nhân viên phòng A đọc được việc phòng B.
GÓP Ý: tách đoạn này thành hàm riêng thì dễ đọc hơn, nhưng không chặn.
HỎI: chỗ này cố ý bỏ qua trường hợp rỗng, hay sót?

Phát hành

Gộp vào main
└─ Tự động đưa lên bản thử
└─ Nghiệm thu ở bản thử
└─ Duyệt → đưa lên bản chính thức

Không bao giờ sửa thẳng trên bản chính thức. Chi tiết môi trường xem Phân chia môi trường.

Đổi cấu trúc cơ sở dữ liệu

Loại thay đổi này rủi ro nhất vì không lùi lại dễ như mã nguồn.

  1. Sinh khung bằng prisma migrate dev --create-only
  2. Mở tệp SQL và viết tay phần ràng buộc kiểm tra, chỉ mục có điều kiện, duy nhất theo hàm — xem Chuẩn lập trình
  3. Viết phần quay lui
  4. Chạy thử trên máy cá nhân với dữ liệu mẫu
  5. Chạy trên bản thử, kiểm bằng kịch bản đối chiếu ràng buộc
  6. Sao lưu cơ sở dữ liệu trước khi chạy trên bản chính thức

Không bao giờ dùng prisma db push — lệnh này bỏ qua tệp chuyển đổi và xoá mất phần viết tay.


Công cụ bắt buộc

Công cụViệc
ESLintBắt lỗi lập trình, cấm any, kiểm quy tắc phụ thuộc giữa tầng
PrettierĐịnh dạng mã, không tranh luận về dấu cách
TypeScript ở chế độ nghiêmBắt lỗi kiểu lúc biên dịch
VitestKiểm thử
Móc trước khi chuyển giao mãChạy rà mã và định dạng trên tệp vừa sửa

Quy trình tích hợp chặn gộp mã nếu: biên dịch lỗi, rà mã lỗi, kiểm thử hỏng, hoặc kịch bản đối chiếu ràng buộc báo thiếu.


Ba việc dễ sai nhất trong dự án này

Ghi riêng vì đều là loại lỗi âm thầm, không báo lỗi lúc chạy.

1. Quên lọc theo phạm vi dữ liệu

Truy vấn thiếu điều kiện phạm vi vẫn chạy đúng, chỉ là trả về cả dữ liệu phòng khác. Không ai phát hiện tới khi có người nhìn thấy việc không phải của mình.

Cách phòng: mọi hàm trong repository nhận công việc bắt buộc có tham số phạm vi. Hàm không nhận tham số đó là lỗi thiết kế, không phải tiện lợi.

2. Ràng buộc cơ sở dữ liệu bị xoá sau khi chuyển đổi

Prisma không biết tới 19 ràng buộc viết tay. Một lần chạy prisma migrate dev không cẩn thận là mất hết, và cơ sở dữ liệu vẫn hoạt động bình thường — chỉ là không còn bảo vệ gì.

Cách phòng: kịch bản đối chiếu chạy trong quy trình tích hợp sau mỗi lần chuyển đổi.

3. Múi giờ

Máy chủ chạy giờ quốc tế, người dùng ở giờ Việt Nam. Lệch bảy tiếng nghĩa là việc đến hạn hôm nay bị tính trễ từ 7 giờ sáng, hoặc ngược lại.

Cách phòng: mọi phép so sánh ngày giờ đi qua common/time/vn-time.ts. Không gọi thẳng new Date() để so với hạn hoàn thành.