Node.js + Express + MongoDB 微服务架构设计:全栈开发实战指南

星辰之舞酱
星辰之舞酱 2026-02-07T23:07:01+08:00
0 0 0

引言

在现代Web应用开发中,微服务架构已经成为构建可扩展、可维护应用的重要模式。Node.js作为高性能的JavaScript运行时环境,Express作为轻量级的Web应用框架,以及MongoDB作为流行的NoSQL数据库,三者的结合为构建现代化的微服务架构提供了完美的技术栈组合。

本文将深入探讨如何使用Node.js + Express + MongoDB构建完整的微服务架构解决方案,从基础概念到实际代码实现,涵盖服务拆分、数据模型设计、RESTful API开发等核心内容,为全栈开发者提供一份实用的技术指南。

微服务架构概述

什么是微服务架构

微服务架构是一种将单一应用程序拆分为多个小型、独立服务的软件架构模式。每个服务都运行在自己的进程中,通过轻量级通信机制(通常是HTTP API)进行交互。每个服务围绕特定的业务功能构建,并可以独立部署、扩展和维护。

微服务的核心优势

  • 独立开发与部署:每个服务可以独立开发、测试和部署
  • 技术多样性:不同服务可以使用不同的技术栈
  • 可扩展性:可以根据需求单独扩展特定服务
  • 容错性:单个服务的故障不会影响整个系统
  • 团队协作:小团队可以负责特定服务

微服务与传统架构对比

传统的单体应用架构将所有功能集成在一个单一的应用程序中,而微服务架构将应用分解为多个小型服务。这种分解使得系统更加灵活,但也带来了服务间通信、数据一致性等新的挑战。

Node.js + Express + MongoDB 技术栈选择

Node.js 的优势

Node.js是一个基于Chrome V8引擎的JavaScript运行时环境,具有以下特点:

  • 非阻塞I/O:基于事件驱动和非阻塞I/O模型,适合处理高并发场景
  • 单线程:使用单线程模型处理大量并发连接
  • 丰富的生态系统:npm包管理器提供了海量的第三方模块
  • 快速开发:JavaScript语言特性使得开发效率高

Express 框架特点

Express是Node.js最流行的Web应用框架,具有以下优势:

  • 简洁轻量:核心功能简单,易于学习和使用
  • 中间件支持:丰富的中间件生态系统
  • 灵活路由:支持复杂的路由规则
  • 高性能:基于原生HTTP模块,性能优秀

MongoDB 的适用场景

MongoDB作为文档型数据库,在微服务架构中表现出色:

  • 灵活的数据模型:JSON格式存储,适应性强
  • 水平扩展能力:支持分片和复制集
  • 丰富的查询语言:支持复杂的查询操作
  • 良好的Node.js集成:提供官方的Node.js驱动

项目结构设计

整体架构图

┌─────────────────────────────────────────────────────────┐
│                    微服务架构总览                       │
├─────────────────────────────────────────────────────────┤
│                    用户服务 (User Service)                │
│                 ┌─────────────────────────────┐           │
│                 │    User API Controller      │           │
│                 │    User Service Layer       │           │
│                 │    User Repository          │           │
│                 └─────────────────────────────┘           │
│                    订单服务 (Order Service)               │
│                 ┌─────────────────────────────┐           │
│                 │    Order API Controller     │           │
│                 │    Order Service Layer      │           │
│                 │    Order Repository         │           │
│                 └─────────────────────────────┘           │
│                    支付服务 (Payment Service)             │
│                 ┌─────────────────────────────┐           │
│                 │    Payment API Controller   │           │
│                 │    Payment Service Layer    │           │
│                 │    Payment Repository       │           │
│                 └─────────────────────────────┘           │
└─────────────────────────────────────────────────────────┘

项目目录结构

microservice-project/
├── package.json
├── README.md
├── config/
│   ├── database.js
│   └── server.js
├── src/
│   ├── services/
│   │   ├── user/
│   │   │   ├── controller/
│   │   │   ├── service/
│   │   │   ├── repository/
│   │   │   ├── model/
│   │   │   └── routes/
│   │   ├── order/
│   │   │   ├── controller/
│   │   │   ├── service/
│   │   │   ├── repository/
│   │   │   ├── model/
│   │   │   └── routes/
│   │   └── payment/
│   │       ├── controller/
│   │       ├── service/
│   │       ├── repository/
│   │       ├── model/
│   │       └── routes/
│   ├── middleware/
│   │   ├── auth.js
│   │   ├── error.js
│   │   └── validation.js
│   ├── utils/
│   │   ├── logger.js
│   │   └── helpers.js
│   └── app.js
├── tests/
└── docker-compose.yml

数据模型设计

用户服务数据模型

// src/services/user/model/user.model.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');

const userSchema = new mongoose.Schema({
  username: {
    type: String,
    required: true,
    unique: true,
    trim: true,
    minlength: 3,
    maxlength: 30
  },
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true,
    match: [/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/, '请输入有效的邮箱地址']
  },
  password: {
    type: String,
    required: true,
    minlength: 6
  },
  firstName: {
    type: String,
    required: true,
    trim: true,
    maxlength: 50
  },
  lastName: {
    type: String,
    required: true,
    trim: true,
    maxlength: 50
  },
  phone: {
    type: String,
    trim: true,
    maxlength: 20
  },
  avatar: {
    type: String,
    default: null
  },
  isActive: {
    type: Boolean,
    default: true
  },
  role: {
    type: String,
    enum: ['user', 'admin', 'moderator'],
    default: 'user'
  }
}, {
  timestamps: true,
  toJSON: {
    transform: function(doc, ret) {
      delete ret.password;
      delete ret.__v;
      return ret;
    }
  }
});

// 密码加密中间件
userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();
  
  try {
    const salt = await bcrypt.genSalt(12);
    this.password = await bcrypt.hash(this.password, salt);
    next();
  } catch (error) {
    next(error);
  }
});

// 密码验证方法
userSchema.methods.comparePassword = async function(candidatePassword) {
  return bcrypt.compare(candidatePassword, this.password);
};

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

订单服务数据模型

// src/services/order/model/order.model.js
const mongoose = require('mongoose');

const orderItemSchema = new mongoose.Schema({
  productId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Product',
    required: true
  },
  productName: {
    type: String,
    required: true
  },
  quantity: {
    type: Number,
    required: true,
    min: 1
  },
  price: {
    type: Number,
    required: true,
    min: 0
  }
});

const orderSchema = new mongoose.Schema({
  userId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  items: [orderItemSchema],
  totalAmount: {
    type: Number,
    required: true,
    min: 0
  },
  status: {
    type: String,
    enum: ['pending', 'processing', 'shipped', 'delivered', 'cancelled'],
    default: 'pending'
  },
  shippingAddress: {
    street: String,
    city: String,
    state: String,
    zipCode: String,
    country: String
  },
  paymentMethod: {
    type: String,
    enum: ['credit_card', 'debit_card', 'paypal', 'bank_transfer'],
    required: true
  },
  paymentStatus: {
    type: String,
    enum: ['pending', 'completed', 'failed', 'refunded'],
    default: 'pending'
  }
}, {
  timestamps: true,
  toJSON: {
    transform: function(doc, ret) {
      delete ret.__v;
      return ret;
    }
  }
});

module.exports = mongoose.model('Order', orderSchema);

数据库连接配置

数据库配置文件

// config/database.js
const mongoose = require('mongoose');

class Database {
  constructor() {
    this.connect();
  }

  connect() {
    const dbUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/microservice_db';
    
    mongoose.connect(dbUri, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
      useCreateIndex: true,
      useFindAndModify: false
    })
    .then(() => {
      console.log('MongoDB connected successfully');
    })
    .catch((error) => {
      console.error('MongoDB connection error:', error);
      process.exit(1);
    });

    // 连接事件监听
    mongoose.connection.on('connected', () => {
      console.log('Mongoose connected to MongoDB');
    });

    mongoose.connection.on('error', (err) => {
      console.error('Mongoose connection error:', err);
    });

    mongoose.connection.on('disconnected', () => {
      console.log('Mongoose disconnected from MongoDB');
    });

    // 应用退出时关闭数据库连接
    process.on('SIGINT', async () => {
      await mongoose.connection.close();
      console.log('Mongoose connection closed due to app termination');
      process.exit(0);
    });
  }
}

module.exports = new Database();

RESTful API 设计规范

用户服务API设计

// src/services/user/routes/user.routes.js
const express = require('express');
const router = express.Router();
const userController = require('../controller/user.controller');

// GET /api/users - 获取所有用户
router.get('/', userController.getAllUsers);

// GET /api/users/:id - 根据ID获取用户
router.get('/:id', userController.getUserById);

// POST /api/users - 创建新用户
router.post('/', userController.createUser);

// PUT /api/users/:id - 更新用户信息
router.put('/:id', userController.updateUser);

// DELETE /api/users/:id - 删除用户
router.delete('/:id', userController.deleteUser);

// POST /api/users/login - 用户登录
router.post('/login', userController.loginUser);

// GET /api/users/profile - 获取当前用户信息
router.get('/profile', userController.getProfile);

module.exports = router;

用户控制器实现

// src/services/user/controller/user.controller.js
const User = require('../model/user.model');
const { validationResult } = require('express-validator');
const jwt = require('jsonwebtoken');

class UserController {
  // 获取所有用户
  async getAllUsers(req, res) {
    try {
      const page = parseInt(req.query.page) || 1;
      const limit = parseInt(req.query.limit) || 10;
      const skip = (page - 1) * limit;

      const users = await User.find()
        .select('-password')
        .skip(skip)
        .limit(limit)
        .sort({ createdAt: -1 });

      const total = await User.countDocuments();
      
      res.json({
        success: true,
        data: users,
        pagination: {
          page,
          limit,
          total,
          pages: Math.ceil(total / limit)
        }
      });
    } catch (error) {
      console.error('Error fetching users:', error);
      res.status(500).json({
        success: false,
        message: '获取用户列表失败',
        error: error.message
      });
    }
  }

  // 根据ID获取用户
  async getUserById(req, res) {
    try {
      const user = await User.findById(req.params.id)
        .select('-password');

      if (!user) {
        return res.status(404).json({
          success: false,
          message: '用户不存在'
        });
      }

      res.json({
        success: true,
        data: user
      });
    } catch (error) {
      console.error('Error fetching user:', error);
      res.status(500).json({
        success: false,
        message: '获取用户信息失败',
        error: error.message
      });
    }
  }

  // 创建新用户
  async createUser(req, res) {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        return res.status(400).json({
          success: false,
          message: '验证失败',
          errors: errors.array()
        });
      }

      const { username, email, password, firstName, lastName } = req.body;

      // 检查用户是否已存在
      const existingUser = await User.findOne({
        $or: [{ email }, { username }]
      });

      if (existingUser) {
        return res.status(409).json({
          success: false,
          message: '用户名或邮箱已存在'
        });
      }

      // 创建新用户
      const user = new User({
        username,
        email,
        password,
        firstName,
        lastName
      });

      await user.save();

      // 生成JWT token
      const token = jwt.sign(
        { userId: user._id, username: user.username },
        process.env.JWT_SECRET || 'your-secret-key',
        { expiresIn: '24h' }
      );

      res.status(201).json({
        success: true,
        message: '用户创建成功',
        data: {
          user: {
            id: user._id,
            username: user.username,
            email: user.email,
            firstName: user.firstName,
            lastName: user.lastName
          },
          token
        }
      });
    } catch (error) {
      console.error('Error creating user:', error);
      res.status(500).json({
        success: false,
        message: '创建用户失败',
        error: error.message
      });
    }
  }

  // 更新用户信息
  async updateUser(req, res) {
    try {
      const { username, email, firstName, lastName, phone } = req.body;

      const user = await User.findByIdAndUpdate(
        req.params.id,
        {
          username,
          email,
          firstName,
          lastName,
          phone
        },
        { new: true, runValidators: true }
      ).select('-password');

      if (!user) {
        return res.status(404).json({
          success: false,
          message: '用户不存在'
        });
      }

      res.json({
        success: true,
        message: '用户信息更新成功',
        data: user
      });
    } catch (error) {
      console.error('Error updating user:', error);
      res.status(500).json({
        success: false,
        message: '更新用户信息失败',
        error: error.message
      });
    }
  }

  // 删除用户
  async deleteUser(req, res) {
    try {
      const user = await User.findByIdAndDelete(req.params.id);

      if (!user) {
        return res.status(404).json({
          success: false,
          message: '用户不存在'
        });
      }

      res.json({
        success: true,
        message: '用户删除成功'
      });
    } catch (error) {
      console.error('Error deleting user:', error);
      res.status(500).json({
        success: false,
        message: '删除用户失败',
        error: error.message
      });
    }
  }

  // 用户登录
  async loginUser(req, res) {
    try {
      const { email, password } = req.body;

      // 查找用户
      const user = await User.findOne({ email }).select('+password');
      
      if (!user) {
        return res.status(401).json({
          success: false,
          message: '邮箱或密码错误'
        });
      }

      // 验证密码
      const isPasswordValid = await user.comparePassword(password);
      
      if (!isPasswordValid) {
        return res.status(401).json({
          success: false,
          message: '邮箱或密码错误'
        });
      }

      // 生成JWT token
      const token = jwt.sign(
        { userId: user._id, username: user.username, role: user.role },
        process.env.JWT_SECRET || 'your-secret-key',
        { expiresIn: '24h' }
      );

      res.json({
        success: true,
        message: '登录成功',
        data: {
          user: {
            id: user._id,
            username: user.username,
            email: user.email,
            firstName: user.firstName,
            lastName: user.lastName,
            role: user.role
          },
          token
        }
      });
    } catch (error) {
      console.error('Error logging in:', error);
      res.status(500).json({
        success: false,
        message: '登录失败',
        error: error.message
      });
    }
  }

  // 获取当前用户信息
  async getProfile(req, res) {
    try {
      const user = await User.findById(req.user.userId)
        .select('-password');

      if (!user) {
        return res.status(404).json({
          success: false,
          message: '用户不存在'
        });
      }

      res.json({
        success: true,
        data: user
      });
    } catch (error) {
      console.error('Error fetching profile:', error);
      res.status(500).json({
        success: false,
        message: '获取用户信息失败',
        error: error.message
      });
    }
  }
}

module.exports = new UserController();

中间件开发

验证中间件

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

const validateUserCreate = [
  body('username')
    .isLength({ min: 3, max: 30 })
    .withMessage('用户名长度必须在3-30个字符之间')
    .matches(/^[a-zA-Z0-9_]+$/)
    .withMessage('用户名只能包含字母、数字和下划线'),
  
  body('email')
    .isEmail()
    .normalizeEmail()
    .withMessage('请输入有效的邮箱地址'),
  
  body('password')
    .isLength({ min: 6 })
    .withMessage('密码长度至少6个字符')
    .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
    .withMessage('密码必须包含大小写字母和数字'),
  
  body('firstName')
    .isLength({ min: 1, max: 50 })
    .withMessage('名字长度必须在1-50个字符之间'),
  
  body('lastName')
    .isLength({ min: 1, max: 50 })
    .withMessage('姓氏长度必须在1-50个字符之间')
];

const validateUserUpdate = [
  body('username')
    .optional()
    .isLength({ min: 3, max: 30 })
    .withMessage('用户名长度必须在3-30个字符之间')
    .matches(/^[a-zA-Z0-9_]+$/)
    .withMessage('用户名只能包含字母、数字和下划线'),
  
  body('email')
    .optional()
    .isEmail()
    .normalizeEmail()
    .withMessage('请输入有效的邮箱地址'),
  
  body('firstName')
    .optional()
    .isLength({ min: 1, max: 50 })
    .withMessage('名字长度必须在1-50个字符之间'),
  
  body('lastName')
    .optional()
    .isLength({ min: 1, max: 50 })
    .withMessage('姓氏长度必须在1-50个字符之间')
];

const handleValidationErrors = (req, res, next) => {
  const errors = validationResult(req);
  
  if (!errors.isEmpty()) {
    return res.status(400).json({
      success: false,
      message: '验证失败',
      errors: errors.array()
    });
  }
  
  next();
};

module.exports = {
  validateUserCreate,
  validateUserUpdate,
  handleValidationErrors
};

认证中间件

// src/middleware/auth.js
const jwt = require('jsonwebtoken');

const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];

  if (!token) {
    return res.status(401).json({
      success: false,
      message: '访问令牌缺失'
    });
  }

  jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key', (err, user) => {
    if (err) {
      return res.status(403).json({
        success: false,
        message: '令牌无效或已过期'
      });
    }
    
    req.user = user;
    next();
  });
};

const authorizeRole = (...roles) => {
  return (req, res, next) => {
    if (!req.user || !roles.includes(req.user.role)) {
      return res.status(403).json({
        success: false,
        message: '权限不足'
      });
    }
    next();
  };
};

module.exports = {
  authenticateToken,
  authorizeRole
};

错误处理机制

全局错误处理中间件

// src/middleware/error.js
const errorHandler = (err, req, res, next) => {
  console.error('Error:', err);

  // 自定义错误处理
  if (err.name === 'ValidationError') {
    return res.status(400).json({
      success: false,
      message: '数据验证失败',
      errors: Object.values(err.errors).map(e => e.message)
    });
  }

  if (err.name === 'CastError') {
    return res.status(400).json({
      success: false,
      message: '无效的ID格式'
    });
  }

  if (err.code === 11000) {
    // MongoDB重复键错误
    const field = Object.keys(err.keyValue)[0];
    return res.status(409).json({
      success: false,
      message: `${field} 已存在`
    });
  }

  // 默认错误处理
  res.status(err.statusCode || 500).json({
    success: false,
    message: err.message || '服务器内部错误',
    ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
  });
};

module.exports = errorHandler;

应用启动配置

主应用文件

// src/app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const database = require('../config/database');
const errorHandler = require('./middleware/error');

class App {
  constructor() {
    this.app = express();
    this.setupMiddleware();
    this.setupRoutes();
    this.setupErrorHandling();
  }

  setupMiddleware() {
    // 安全中间件
    this.app.use(helmet());
    
    // CORS配置
    this.app.use(cors({
      origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'],
      credentials: true
    }));

    // 日志中间件
    this.app.use(morgan('combined'));

    // 解析JSON请求体
    this.app.use(express.json({ limit: '10mb' }));
    this.app.use(express.urlencoded({ extended: true, limit: '10mb' }));

    // 静态文件服务
    this.app.use(express.static('public'));
  }

  setupRoutes() {
    // 健康检查端点
    this.app.get('/health', (req, res) => {
      res.json({
        success: true,
        message: 'Service is running',
        timestamp: new Date().toISOString()
      });
    });

    // API路由
    const userRoutes = require('./services/user/routes/user.routes');
    const orderRoutes = require('./services/order/routes/order.routes');
    const paymentRoutes = require('./services/payment/routes/payment.routes');

    this.app.use('/api/users', userRoutes);
    this.app.use('/api/orders', orderRoutes);
    this.app.use('/api/payments', paymentRoutes);

    // 404处理
    this.app.use('*', (req, res) => {
      res.status(404).json({
        success: false,
        message: '请求的资源不存在'
      });
    });
  }

  setupErrorHandling() {
    // 全局错误处理
    this.app.use(errorHandler);
  }

  listen(port = process.env.PORT || 3000) {
    return this.app.listen(port, () => {
      console.log(`Server running on port ${port}`);
    });
  }
}

module.exports = App;

启动文件

// src/server.js
const App = require('./app');

const app = new App();

const PORT = process.env.PORT || 3000;

app.listen(PORT);

process.on('unhandledRejection', (reason, promise) => {
  console.log('Unhandled Rejection at:', promise, 'reason:', reason);
  process.exit(1);
});

process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  process.exit(1);
});

测试策略

单元测试示例

// tests/user.service.test.js
const request = require('supertest');
const app = require('../src/app');
const User = require('../src/services/user/model/user.model');

describe('User Service', () => {
  beforeEach(async () => {
    await User.deleteMany({});
  });

  afterEach(async () => {
    await User.deleteMany({});
  });

  describe('POST /api/users', () => {
    it('should create a new user', async () => {
      const userData = {
        username: 'testuser',
        email: 'test@example.com',
        password: 'Password123',
        firstName: 'Test',
        lastName: 'User'
      };

      const response = await request(app)
        .post('/api/users')
        .send(userData)
        .expect(201);

      expect(response.body.success).toBe(true);
      expect(response.body.data.user.username).toBe(userData.username);
      expect(response.body.data.user.email).toBe(userData.email);
      expect(response.body.data.token).toBeDefined();
    });

    it('should return validation error for invalid data', async () => {
      const userData = {
        username: 'ab',
        email: 'invalid-email',
        password: '123'
      };

      const response
相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000