Cách mình tổ chức code trong dự án lớn
Dự án nhỏ thì cấu trúc không quan trọng. Nhưng khi team 5+ người, 50+ files, bạn cần organize code cho dễ maintain.
Cấu trúc theo Feature (recommended)
src/
├── features/
│ ├── auth/
│ │ ├── auth.controller.js
│ │ ├── auth.service.js
│ │ ├── auth.repository.js
│ │ ├── auth.middleware.js
│ │ ├── auth.validator.js
│ │ └── auth.test.js
│ ├── orders/
│ │ ├── orders.controller.js
│ │ ├── orders.service.js
│ │ ├── orders.repository.js
│ │ └── orders.test.js
│ └── users/
│ ├── users.controller.js
│ ├── users.service.js
│ └── users.test.js
├── shared/
│ ├── database.js
│ ├── logger.js
│ ├── errors.js
│ └── middleware/
│ ├── error-handler.js
│ └── rate-limit.js
├── config/
│ └── index.js
└── app.js
Tại sao không theo Layer?
# Cấu trúc theo Layer (KHÔNG recommend)
src/
├── controllers/
│ ├── authController.js
│ ├── orderController.js
│ └── userController.js
├── services/
│ ├── authService.js
│ ├── orderService.js
│ └── userService.js
├── models/
│ ├── User.js
│ ├── Order.js
│ └── Token.js
Vấn đề: Muốn hiểu feature "orders" phải nhảy qua 4-5 thư mục. Feature-based thì mọi thứ liên quan nằm cạnh nhau.
3 Layers trong mỗi Feature
Controller → Service → Repository
(HTTP) (Logic) (Database)
Controller: Nhận request, trả response
// orders.controller.js
const router = express.Router();
router.post('/', authenticate, async (req, res, next) => {
try {
const order = await orderService.create(req.user.id, req.body);
res.status(201).json(order);
} catch (err) {
next(err);
}
});
```
### Service: Business logic
```javascript
// orders.service.js
async function create(userId, data) {
const items = await validateItems(data.items);
const total = calculateTotal(items);
if (total > 10000) {
throw new AppError('ORDER_LIMIT', 'Order exceeds maximum amount');
}
const order = await orderRepo.create({
userId,
items,
total,
status: 'pending'
});
await notificationService.send(userId, 'Order created');
return order;
}
```
### Repository: Database access
```javascript
// orders.repository.js
async function create(data) {
return db('orders').insert(data).returning('*');
}
async function findByUserId(userId, { limit = 20, offset = 0 } = {}) {
return db('orders')
.where({ user_id: userId })
.orderBy('created_at', 'desc')
.limit(limit)
.offset(offset);
}
```
## Rules
1. **Controller không chứa business logic** — chỉ parse request, gọi service, format response
2. **Service không biết HTTP** — không dùng `req`, `res`, status codes
3. **Repository không biết business rules** — chỉ CRUD
4. **Feature không import từ feature khác trực tiếp** — dùng shared hoặc events
## Khi nào cần refactor?
- File > 300 dòng → tách
- Folder > 10 files → tách sub-features
- Import cycles → sai architecture
- Test khó viết → coupling quá chặt
---
Bạn tổ chức code thế nào? Có pattern nào hay share nhé!
All Rights Reserved