Hệ thống Website Kinh doanh Khóa học Trực tuyến toàn diện, hỗ trợ kết nối đa chiều giữa Học viên - Giảng viên - Tư vấn viên - Quản trị viên. Dự án được xây dựng với mục tiêu học tập kiến trúc hệ thống và các công nghệ hiện đại.
Dự án nhằm xây dựng một nền tảng ổn định, bảo mật để kinh doanh các khóa học video (Lập trình, Tiếng Anh, Môn đại cương...) tích hợp thanh toán và quản lý tiến trình học tập theo mô hình B2C. Hệ thống đã được chuyển đổi sang kiến trúc tự quản lý với PostgreSQL, Prisma và Socket.io.
- Ngôn ngữ: TypeScript (Bắt buộc để đảm bảo an toàn dữ liệu).
- Frontend: Next.js 15 (App Router), Tailwind CSS, Shadcn/UI, Socket.io-client.
- Backend: Node.js, Express.js.
- Database: PostgreSQL (Lưu trữ quan hệ).
- ORM: Prisma (Type-safe query & Migration).
- Authentication: JWT (JSON Web Token) + Bcrypt (Mã hóa mật khẩu).
- Real-time: Socket.io (Chat Tư vấn).
- Quản lý Monorepo: Turborepo.
| Thành phần | Thư viện chính |
|---|---|
| Backend (BE) | express, @prisma/client, jsonwebtoken, bcrypt, socket.io, zod, helmet, morgan, cors |
| Frontend (FE) | next, react, socket.io-client, lucide-react, tailwind-merge, clsx, class-variance-authority |
| Thành phần | Thư viện | Công dụng |
|---|---|---|
| Backend | express |
Framework web tạo API endpoints. |
@prisma/client |
ORM tương tác với DB bằng code TS, tự động gợi ý. | |
jsonwebtoken |
Tạo và xác thực "vé thông hành" (Token) sau khi đăng nhập. | |
bcrypt |
Mã hóa mật khẩu (không lưu mật khẩu thô). | |
socket.io |
Kết nối Real-time cho Chat. | |
zod |
Kiểm tra dữ liệu đầu vào (Validation). | |
helmet / morgan |
Bảo mật HTTP header và Ghi log request. | |
| Frontend | next |
Framework React tối ưu SEO và tốc độ. |
socket.io-client |
Client kết nối chat với server. | |
lucide-react |
Bộ icon vector đẹp và nhẹ. | |
tailwind-merge / clsx |
Xử lý class CSS linh hoạt theo điều kiện. |
Dự án này sử dụng Clean Architecture và nhiều Role người dùng — TypeScript là lựa chọn bắt buộc để giảm thiểu sai sót:
| Tình huống thực tế | JavaScript | TypeScript |
|---|---|---|
Gọi req.user.id khi chưa qua middleware |
💥 Crash lúc runtime | 🛑 Báo lỗi ngay khi viết code |
| Đổi tên field trong database | Phải tìm kiếm thủ công | Compiler chỉ thẳng tất cả chỗ cần sửa |
| Thành viên mới đọc code | Phải đọc hết logic mới hiểu | Đọc kiểu dữ liệu (Type) là đủ |
| FE và BE dùng chung dữ liệu | Dễ bị lệch nhau | Dùng chung một định nghĩa (Interface) |
E-Course-Sales-System/
├── apps/
│ ├── server/ # BACKEND (Express.js + Prisma)
│ │ ├── prisma/
│ │ │ └── schema.prisma # Định nghĩa Database Model & Relationships
│ │ ├── src/
│ │ │ ├── application/ # Business Logic (Use Cases - vd: Mua khóa học)
│ │ │ ├── domain/ # Core Logic (Entities & Repository Interfaces)
│ │ │ ├── infrastructure/ # Triển khai kỹ thuật (Infrastructure Layer)
│ │ │ │ ├── auth/ # JWT Service (Tạo & xác thực Token)
│ │ │ │ ├── database/ # Prisma Service & Repositories (Truy vấn DB)
│ │ │ │ ├── socket/ # Socket.io Server (Xử lý Real-time Chat)
│ │ │ │ └── storage/ # Xử lý Upload Media
│ │ │ ├── presentation/ # Giao diện lập trình (Presentation Layer)
│ │ │ │ ├── controllers/ # Xử lý Request/Response
│ │ │ │ ├── middleware/ # Auth Middleware, RBAC (Phân quyền)
│ │ │ │ └── routes/ # Định nghĩa các API Endpoints
│ │ │ ├── app.ts # Khởi tạo Express & Socket.io
│ │ │ └── index.ts # Entry Point của Server
│ │
│ └── web/ # FRONTEND (Next.js 15)
│ ├── src/
│ │ ├── app/ # App Router (Pages & Layouts)
│ │ ├── components/ # UI Components (Shadcn/UI)
│ │ ├── hooks/ # Custom Hooks (useRealtimeChat...)
│ │ ├── lib/ # Thư viện dùng chung (Utils, API Client)
│ │ └── middleware.ts # Route Guard (Bảo vệ các trang Dashboard)
│
├── turbo.json # Cấu hình chạy song song FE & BE
└── README.md # Tài liệu hướng dẫn
Để làm việc như một Team thực thụ, các thành viên cần tuân thủ:
- Thư mục & File: Dùng
kebab-case(vd:user-profile.tsx,auth-middleware.ts). - Component & Class: Dùng
PascalCase(vd:CourseCard,PrismaService). - Biến & Hàm: Dùng
camelCase(vd:isApproved,getUserById). - Interface/Type: Dùng
PascalCase(vd:IUserRepository).
- Không code trực tiếp trên
main. - Tạo nhánh mới:
git checkout -b feat/tên-tính-nănghoặcfix/tên-lỗi. - Commit message chuẩn:
feat: thêm chức năng đăng nhập,fix: sửa lỗi hiển thị khóa học. - Tạo Pull Request (PR): Nhờ đồng đội review trước khi gộp vào
main.
chmod +x setup.sh(Cấp quyền)../setup.sh(Chạy script).
npm install(Cài thư viện gốc).copy apps\server\.env.example apps\server\.env.copy apps\web\.env.example apps\web\.env.- Điền thông tin: Mở
.envđiềnDATABASE_URLvàJWT_SECRET. cd apps/server && npx prisma generate.- Quay lại gốc:
npm run dev.
- Sửa Schema: Cập nhật bảng tại
apps/server/prisma/schema.prisma. - Migration:
cd apps/server && npx prisma migrate dev --name <tên>. - Xem dữ liệu:
npx prisma studio.
- Pull trước khi làm: Luôn
git pullđể lấy code mới nhất. - Restart Server: Cần khởi động lại (Ctrl+C rồi
npm run dev) khi:- Sửa file
.env. - Cài thư viện mới.
- Sửa cấu hình
tsconfig.json,next.config.js,turbo.json.
- Sửa file
- Bảo mật: Tuyệt đối không đẩy file
.envlên GitHub.
- Đọc
schema.prismađể hiểu cấu trúc dữ liệu. - Xem
auth.middleware.tsđể hiểu cách bảo mật bằng JWT. - Thử tạo một
Use Casemới trongapplication/use-casesđể làm quen với Clean Architecture.
Để dự án chạy được, bạn cần một instance PostgreSQL đang hoạt động. Bạn có 2 cách chính:
Nếu máy bạn đã cài Docker Desktop, bạn chỉ cần tạo file docker-compose.yml tại thư mục gốc với nội dung:
version: '3.8'
services:
db:
image: postgres:15
restart: always
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: ecourse_db
ports:
- "5432:5432"Sau đó gõ lệnh: docker-compose up -d. Database của bạn sẽ sẵn sàng tại:
DATABASE_URL="postgresql://myuser:mypassword@localhost:5432/ecourse_db?schema=public"
- Tải về: Vào postgresql.org chọn phiên bản phù hợp (vd: v15 hoặc v16).
- Cài đặt: Trong quá trình cài, hãy nhớ Mật khẩu của user
postgres. - Tạo Database: Mở công cụ pgAdmin 4 (đi kèm khi cài Postgres), chuột phải vào "Databases" -> "Create" -> đặt tên là
ecourse_db. - Cấu hình .env:
DATABASE_URL="postgresql://postgres:mật_khẩu_của_bạn@localhost:5432/ecourse_db?schema=public"