架构模式与分层
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/res | Service 只处理业务,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 都只是适配器)。
相关链接: