0

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
Let's register a Viblo Account to get more interesting posts.