架构模式与分层

What — 是什么

架构模式定义了软件的整体结构和组织方式。Node.js 服务端常用的架构模式包括 MVC、分层架构、六边形架构、CQRS 和整洁架构。

核心概念:

  • MVC:Model(数据)+ View(展示)+ Controller(逻辑),最经典的分层
  • 分层架构:Controller → Service → Repository → Database,每层职责单一
  • 六边形架构(端口与适配器):核心业务逻辑在中心,通过端口与外部交互,适配器连接具体技术
  • CQRS:命令(写)和查询(读)分离,各自优化
  • 整洁架构:依赖规则由外向内,核心业务不依赖任何框架
  • 领域驱动设计(DDD):按业务领域划分限界上下文,统一语言

关键特性:

  • 分层架构最直观,适合大多数 Node.js 项目
  • 六边形架构便于替换技术实现(如换 ORM)
  • CQRS 适合读写差异大的场景
  • 整洁架构测试性最好,但复杂度最高

Why — 为什么

适用场景:

  • MVC:简单 CRUD 应用
  • 分层架构:标准 API 服务
  • 六边形架构:需要替换基础设施的项目
  • CQRS:读多写少或读写模型差异大
  • 整洁架构:核心业务复杂、需要高可测试性

对比架构:

维度分层架构六边形CQRS整洁架构
复杂度
可测试性最高
替换便利
适用规模中小

How — 怎么用

代码示例

// 分层架构
// controller/user.controller.js
class UserController {
    constructor(userService) { this.userService = userService; }
    async getUser(req, res) {
        const user = await this.userService.findById(req.params.id);
        res.json(user);
    }
}

// service/user.service.js
class UserService {
    constructor(userRepo) { this.userRepo = userRepo; }
    async findById(id) {
        const user = await this.userRepo.findById(id);
        if (!user) throw new NotFoundError('User');
        return user;
    }
}

// repository/user.repository.js
class UserRepository {
    constructor(db) { this.db = db; }
    findById(id) { return this.db.users.findById(id); }
    create(data) { return this.db.users.create({ data }); }
}

// 六边形架构:端口与适配器
// port/user.port.js(接口定义)
class UserPort {
    async findById(id) { throw new Error('Not implemented'); }
}

// adapter/user.mysql.adapter.js(具体实现)
class UserMySQLAdapter extends UserPort {
    constructor(pool) { super(); this.pool = pool; }
    async findById(id) {
        const [rows] = await this.pool.query('SELECT * FROM users WHERE id = ?', [id]);
        return rows[0];
    }
}

// CQRS:命令和查询分离
// commands/create-user.command.js
class CreateUserCommand {
    constructor(name, email) { this.name = name; this.email = email; }
}

// handlers/create-user.handler.js
class CreateUserHandler {
    async execute(cmd) {
        const user = await this.userRepo.create({ name: cmd.name, email: cmd.email });
        await this.eventBus.publish('user.created', user);
        return user;
    }
}

// queries/get-user.query.js
class GetUserQuery {
    constructor(id) { this.id = id; }
}
// 读操作可走不同数据源(如 Redis 缓存/只读副本)

常见问题与踩坑

问题原因解决方案
分层不严格Service 直接操作 req/resService 只处理业务,Controller 处理 HTTP
过度设计小项目用 CQRS/整洁架构按项目规模选架构
CQRS 数据不一致读写数据源不同步最终一致性 + 事件同步

最佳实践

  • 中小项目用分层架构(Controller-Service-Repository)
  • 大型项目用六边形架构隔离核心逻辑
  • 读写差异大时考虑 CQRS
  • Service 层不依赖 HTTP 对象(req/res)

面试题

Q1: 分层架构中每层的职责?

Controller:接收 HTTP 请求、参数验证、调用 Service、格式化响应。Service:业务逻辑、事务编排、调用 Repository、不依赖 HTTP 对象。Repository:数据访问、数据库操作、查询构建、缓存。分层原则:上层依赖下层,下层不依赖上层;Service 是核心,Controller 和 Repository 是适配层。

Q2: 六边形架构的核心思想?

核心思想:业务逻辑在中心(不依赖任何外部技术),通过端口(Port/接口)定义与外部的交互,适配器(Adapter/实现)连接具体技术。好处:① 替换 ORM 只需换适配器,核心业务不动;② 替换消息队列同理;③ 核心业务可以纯单元测试(Mock 端口);④ 框架无关(Express/Fastify/NestJS 都只是适配器)。


相关链接: