ویژگی تصویر

معماری MVC در Node.js

  /  Node.js   /  ساخت پروژه چندلایه (MVC) در Node.js
بنر تبلیغاتی الف
NodeJS - Node.js

معماری چندلایه یا MVC (Model-View-Controller) باعث جداسازی وظایف، تست‌پذیری و نگهداری آسان‌تر برنامه‌های وب می‌شود. در اکوسیستم Node.js که انعطاف و تنوع زیادی در انتخاب ابزارها وجود دارد، استفاده از الگوی MVC کمک می‌کند تا کد منظم، قابل فهم و قابل گسترش بماند. در این مقاله به صورت عملی ساخت یک پروژه چندلایه در Node.js با Express و Mongoose (برای MongoDB) را بررسی می‌کنیم و نکات حرفه‌ای و بهینه‌سازی‌ها را نیز مطرح می‌کنیم.

مزایا و موارد کاربرد

  • تفکیک مسئولیت‌ها و کاهش وابستگی‌ها
  • تسهیل تست واحد و یکپارچه
  • مناسب برای برنامه‌های CRUD، APIهای RESTful و پروژه‌های با رشد سریع

ساختار پیشنهادی پوشه‌ها

پوشه/فایلوظیفه
src/کد منبع پروژه
src/controllers/لایه Controller — دریافت درخواست‌ها و هماهنگی سرویس‌ها
src/models/لایه Model — اسکیمای داده و منطق مرتبط با دیتابیس
src/services/Business Logic — عملیات پیچیده و تعامل با مدل‌ها
src/routes/تعریف مسیرها و middlewareها
src/middlewares/Middlewareهای عمومی مانند احراز هویت و لاگینگ
src/utils/توابع کمکی و خطایابی

نمونه ساده: راه‌اندازی اولیه با Express

// src/app.js
const express = require('express');
const mongoose = require('mongoose');
const routes = require('./routes');

const app = express();
app.use(express.json());
app.use('/api', routes);

mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log('Mongo connected'))
  .catch(err => console.error(err));

module.exports = app;

این فایل اصلی برنامه است: ایجاد اپ اکسپرس، فعال‌سازی پارس JSON، متصل کردن router کلی و اتصال به MongoDB با Mongoose. جدا نگه داشتن اتصال دیتابیس و پیکربندی‌ها به تمیز ماندن پروژه کمک می‌کند.

نمونه مدل با Mongoose

// src/models/User.js
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  name: { type: String, required: true, trim: true },
  email: { type: String, required: true, unique: true },
  passwordHash: { type: String, required: true },
  createdAt: { type: Date, default: Date.now }
});

module.exports = mongoose.model('User', userSchema);

در این بخش اسکیمای کاربر تعریف شده است. Model مسئول تعامل مستقیم با دیتابیس است و تمام قواعد ساختاری (required، unique و غیره) در اینجا قرار می‌گیرد.

نمونه Controller و Service

// src/services/userService.js
const User = require('../models/User');
const bcrypt = require('bcrypt');

async function createUser({ name, email, password }) {
  const exists = await User.findOne({ email });
  if (exists) throw new Error('Email already in use');
  const passwordHash = await bcrypt.hash(password, 10);
  const user = new User({ name, email, passwordHash });
  return user.save();
}

module.exports = { createUser };

// src/controllers/userController.js
const userService = require('../services/userService');

async function register(req, res, next) {
  try {
    const user = await userService.createUser(req.body);
    res.status(201).json({ id: user._id, name: user.name, email: user.email });
  } catch (err) {
    next(err);
  }
}

module.exports = { register };

در این مثال، service مسئول منطق کسب‌وکار (مثل هش کردن رمز و بررسی وجود ایمیل) است و controller فقط دریافت درخواست و ارسال پاسخ را انجام می‌دهد. این جداسازی امکان تست مستقل و استفاده مجدد را می‌دهد.

تعریف مسیرها (Routes)

// src/routes/index.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');

router.post('/users', userController.register);

module.exports = router;

فایل روتر مسیرهای مربوط به منابع را به کنترلرها متصل می‌کند. در پروژه‌های بزرگ‌تر می‌توانید هر منبع را در فایل جداگانه‌ای داشته باشید و از versioning برای API استفاده کنید (/api/v1/…).

مدیریت خطا و Middleware

یک middleware مرکزی برای هندل خطاها تعریف کنید تا پاسخ‌های یکنواخت و لاگ مناسب داشته باشید. همچنین از middlewareهای اعتبارسنجی (مثلاً Joi یا express-validator) برای اعتبارسنجی درخواست‌ها استفاده کنید.

تست و CI/CD

  • برای تست واحد از Jest یا Mocha استفاده کنید و سرویس‌ها و کنترلرها را جداگانه تست کنید.
  • در CI، پیش از دیپلوی، تست‌ها و lint را اجرا کنید و از environment variables امن استفاده کنید.

بهینه‌سازی و نکات حرفه‌ای

  • از Dependency Injection و مدیریت کانتینر (مانند Awilix) برای کاهش coupling استفاده کنید.
  • لاگینگ ساخت‌یافته (پام/PM2/Logstash) و مانیتورینگ برای production ضروری است.
  • برای عملکرد بهتر، استراتژی‌های caching و pagination را از ابتدا طراحی کنید.
  • استفاده از تراکنش‌ها و کنترل همزمانی (برای دیتابیس‌هایی که پشتیبانی می‌کنند) در عملیات حساس توصیه می‌شود.

مثال کامل‌تر: اعتبارسنجی درخواست با express-validator

// src/middlewares/validators.js
const { body, validationResult } = require('express-validator');

const registerValidation = [
  body('name').isLength({ min: 2 }),
  body('email').isEmail(),
  body('password').isLength({ min: 6 }),
  (req, res, next) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });
    next();
  }
];

module.exports = { registerValidation };

// استفاده در روتر
// router.post('/users', registerValidation, userController.register);

در این کد از ابزار express-validator برای اعتبارسنجی بدنه درخواست استفاده می‌کنیم. در صورت بروز خطا یک پاسخ 400 با جزئیات ارسال می‌شود. این کار جلوی ورود داده‌های ناصحیح به لایه‌های پایین‌تر را می‌گیرد.

جمع‌بندی و بهترین روش‌ها

ساخت پروژه چندلایه در Node.js باعث بهبود نگهداری، خوانایی و توسعه تیمی می‌شود. نکات کلیدی:

  • هر لایه یک مسئولیت مشخص داشته باشد (Controller: ورودی/خروجی، Service: منطق، Model: دیتابیس)
  • از middlewareها برای cross-cutting concerns استفاده کنید (اعتبارسنجی، لاگ، خطا)
  • تست‌های واحد و یکپارچه را از ابتدا لحاظ کنید
  • پیکربندی و اسرار (env) را خارج از کد نگه دارید و در CI/CD امن کنید

با رعایت این الگوها می‌توانید برنامه‌های مقیاس‌پذیر و قابل اطمینان با Node.js بسازید. شروع ساده و افزودن لایه‌ها و الگوهای پیچیده‌تر (مانند CQRS یا Event-driven) زمانی مناسب است که نیازهای پروژه افزایش یابد.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: