Cách Mình Cấu Trúc Code

Nguyên tắc tổ chức codebase sao cho scale được theo quy mô team và độ phức tạp

Cấu trúc project tốt thì developer mới vào tìm code ngay được. Cấu trúc tệ thì ai cũng phải grep cả codebase mới biết code nằm đâu.

Câu hỏi kiểm tra: "Nếu một người mới cần tìm đoạn code này, họ sẽ tìm ở đâu trước?" -- Nếu không trả lời được ngay thì cấu trúc cần sửa.

Nguyên Tắc

1. Tổ chức theo chức năng, không phải theo loại

Tổ chức theo loại file trông gọn gàng nhưng thực tế rất khó dùng:

components/    ← 200 file, trộn lẫn hết
hooks/         ← Hook nào đi với component nào?
utils/         ← Bãi rác cho mọi thứ

Developer mới muốn tìm code "checkout" phải lục tung cả ba folder. Thay vào đó:

features/
  checkout/    ← Mọi thứ cho checkout: components, hooks, utils
  auth/        ← Mọi thứ cho auth
  dashboard/   ← Mọi thứ cho dashboard

Tìm code checkout? Vào folder checkout/. Xong.

Code thực sự generic -- một hàm format ngày, một button component dùng chung -- thì đặt vào shared/ hoặc lib/.

2. Code liên quan để chung chỗ

Code hay phải sửa cùng lúc thì nên nằm gần nhau.

Để chung khi:

  • Luôn phải sửa cùng lúc
  • File này chỉ có nghĩa khi đi cùng file kia
  • Cùng phục vụ một khái niệm

Tách ra khi:

  • Được dùng bởi 3+ features khác nhau
  • Thay đổi file này không liên quan tới file kia
  • Những team khác nhau quản lý từng phần

3. Tôn trọng các lớp

Hầu hết app có ba lớp:

┌─────────────────────────────────────┐
│           PRESENTATION              │
│  Những gì user thấy và tương tác   │
│  UI components, pages, styles       │
└─────────────────────────────────────┘
                  ↓ gọi
┌─────────────────────────────────────┐
│          BUSINESS LOGIC             │
│  Ứng dụng làm gì. Validation,      │
│  tính toán, biến đổi dữ liệu       │
└─────────────────────────────────────┘
                  ↓ gọi
┌─────────────────────────────────────┐
│              DATA                   │
│  Dữ liệu đến từ đâu. Database,    │
│  APIs, file system                  │
└─────────────────────────────────────┘

Mũi tên chỉ xuống thôi. Hàm database không import React component. Utility không render HTML.

Khi các lớp bị trộn lẫn thì thay đổi lan tỏa khó đoán -- "mình chỉ sửa một database query" mà UI bể. Giữ lớp nào ra lớp nấy.

4. Thứ quan trọng phải dễ thấy

  • Entry point ở root hoặc đầu folder
  • File cấu hình ở project root
  • Trang chính của feature đặt tên rõ ràng (page.tsx, index.tsx)

Chôn file cấu hình database vào src/lib/utils/helpers/db/config.ts là tự hại mình.

File Mới Nên Nằm Ở Đâu?

Hỏi theo thứ tự:

  1. Thuộc feature cụ thể? → Vào folder của feature đó
  2. Dùng bởi nhiều features? → Vào shared/ hoặc lib/
  3. Là cấu hình hoặc setup? → Project root
  4. Script chạy một lần?scripts/ hoặc tools/

Có nên tạo folder mới?

Tạo khi: có 3+ file liên quan, khái niệm đó xứng đáng có tên riêng, ai đó sẽ tìm nó như một "thứ" độc lập.

Không tạo khi: chỉ 1-2 file, tạo cho có, hoặc tên folder sẽ rất chung chung (misc/, other/).

Có nên tách file?

Tách khi: file vượt ~200-300 dòng, có nhiều trách nhiệm không liên quan, cần test riêng, hoặc nhiều người cần sửa phần khác nhau.

Giữ nguyên khi: code liên kết chặt, tách ra tạo circular dependency, hoặc code đang kể một "câu chuyện" hoàn chỉnh mà tách ra thì mất mạch.

Những Lỗi Hay Gặp

Bãi rác "utils"

utils.ts có 50 hàm không liên quan -- cấu trúc đã thất bại. Ai cũng quăng code vào đây vì không biết đặt đâu, file cứ phình ra mãi. Tách theo domain: date-utils.ts, validation.ts, formatting.ts.

Tạo folder quá sớm

Chỉ có một button mà tạo components/buttons/PrimaryButton/index.tsx -- ba cấp thư mục cho một file. Bắt đầu phẳng, thêm cấu trúc khi có đủ file. Đừng tổ chức "phòng xa" cho thứ chưa tồn tại.

Naming lung tung

UserProfile.tsx, order-list.tsx, Dashboard/index.tsx trong cùng folder. Mỗi file một kiểu đặt tên -- codebase nhìn vào chỉ thấy loạn. Chọn một convention (kebab-case hay PascalCase) rồi áp dụng khắp nơi. Convention nào ít quan trọng hơn việc nhất quán.

Lồng quá sâu

src/modules/features/user/components/profile/ProfileCard/index.tsx

6 cấp sâu cho một component. Nếu đường dẫn sâu hơn 3-4 cấp thì nên xem lại -- phần lớn trường hợp là đang over-organize.

Checklist

Ổn nếu:

  • Người mới tìm được code mà không cần hỏi
  • Giải thích được file nên đặt đâu trong một câu
  • Thay đổi liên quan chạm vào các file gần nhau
  • Không file nào 1000+ dòng
  • Không folder nào chứa 50+ file lộn xộn

Cần sửa nếu:

  • Phải grep cả codebase mới tìm được code
  • File liên tục bị quăng vào "utils" hoặc "helpers"
  • Cùng folder chứa code hoàn toàn khác nhau
  • Mỗi người đặt file cùng loại vào chỗ khác nhau

Phát Triển Cấu Trúc

Bắt đầu đơn giản. Project mới không cần 10 folder. Bắt đầu phẳng, thêm tổ chức khi pattern xuất hiện tự nhiên.

Refactor chủ động. Folder quá lớn hoặc pattern rõ ràng thì tái cấu trúc ngay. Đừng chờ tới lúc "đau" mới sửa -- lúc đó sửa tốn gấp 5 lần.

Ghi lại convention. Viết ra file nên đặt ở đâu. Người mới vào không nên phải đoán.