+1

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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí