4、分层架构核心理论:经典三层架构详解

说到分层架构,很多人第一反应就是「三层架构」。没错,它确实是软件工程里最经典、最基础的分层模式。我做了十几年系统设计,见过无数项目从单体直接拆成微服务,结果拆得七零八落。其实很多场景下,老老实实把三层架构做好,比盲目追新要靠谱得多。

今天我们就来把三层架构彻底聊透。我会结合自己踩过的坑,帮你理解每一层到底该干什么、不该干什么,以及层与层之间怎么通信才不「越界」。

4.1 三层架构的由来与本质

三层架构不是谁拍脑袋想出来的。它源于一个很朴素的诉求:让代码各司其职。

早期开发经常是 JSP 里写 SQL,HTML 里混业务逻辑。改一个需求,要翻遍所有文件。后来大家发现,如果把界面、业务、数据这三件事分开,维护成本会大幅下降。这就是三层架构的初衷。

核心原则:每一层只关心自己的事,不越俎代庖。

三层架构的典型结构是这样的:

  • 表现层(Presentation Layer):负责与用户交互,展示数据、接收输入。
  • 业务层(Business Layer):负责核心业务逻辑、规则校验、流程编排。
  • 数据层(Data Layer):负责数据持久化、访问数据库或外部存储。

你想想看,这三层就像一家公司的三个部门:前台接待(表现层)、业务经理(业务层)、仓库管理员(数据层)。前台不能直接去仓库搬货,业务经理也不能自己去前台站岗。各司其职,才能高效运转。

4.2 各层职责详解

4.2.1 表现层:只做「翻译」和「传递」

表现层的职责其实很单纯:把用户的操作翻译成业务层能理解的请求,再把业务层返回的结果翻译成用户能看懂的界面。

我在项目中遇到过不少团队,喜欢在表现层写业务逻辑。比如在 Controller 里直接做金额计算、状态判断。嗯,这里要提醒你:表现层一旦开始「思考」,代码就离失控不远了。

表现层应该包含的内容:

  • 视图渲染(HTML、JSON、XML 等)
  • 输入校验(格式校验,如邮箱格式、必填项)
  • 会话管理(登录状态、Token 校验)
  • 异常处理(统一错误页面或错误码返回)

表现层不应该包含的内容:

  • 业务规则判断(如「用户余额是否足够」)
  • 数据访问逻辑(如直接写 SQL)
  • 复杂的状态转换
避坑指南:我曾经接手过一个项目,Controller 里直接调了 DAO 层,跳过了 Service。结果业务逻辑散落在各个 Controller 里,改一个需求要改 5 个文件。后来重构花了整整两周。记住:表现层永远不要绕过业务层。

4.2.2 业务层:系统的「大脑」

业务层是整个架构的核心。说白了,业务层就是把你脑子里的业务规则翻译成代码。

我个人习惯把业务层分成两部分:

  • 业务逻辑:核心算法、规则引擎、状态机
  • 业务编排:调用多个数据层方法,组合成完整的业务流程

举个例子,下单流程:

  1. 校验库存(调用数据层)
  2. 扣减库存(调用数据层)
  3. 生成订单(调用数据层)
  4. 发送通知(调用外部服务)

这些步骤的编排,就是业务层的职责。表现层只需要说「用户要下单」,剩下的全交给业务层。

个人经验:业务层最容易犯的错误是「过度膨胀」。我见过一个 Service 类写了 3000 行,什么逻辑都往里塞。我的建议是:一个业务方法只做一件事。如果方法超过 50 行,就该考虑拆分了。

4.2.3 数据层:只做「存取」

数据层的职责非常明确:封装对数据源的访问,提供 CRUD 操作。

数据层不应该包含任何业务逻辑。比如「查询用户订单」和「查询用户订单并计算总价」——前者是数据层的事,后者是业务层的事。

数据层的典型实现:

  • DAO(Data Access Object)模式
  • Repository 模式
  • ORM 框架(MyBatis、Hibernate、Entity Framework)
层 核心职责 常见错误
表现层 用户交互、数据展示、输入校验 写业务逻辑、直接访问数据库
业务层 业务规则、流程编排、事务管理 过度膨胀、与表现层耦合
数据层 数据存取、ORM 映射、连接管理 包含业务判断、暴露内部实现

4.3 层间通信规则

三层架构的通信规则其实就一句话:上层依赖下层,下层不依赖上层。

具体来说:

  • 表现层调用业务层
  • 业务层调用数据层
  • 数据层不反向调用

为什么这么设计?因为一旦下层依赖上层,就会出现循环依赖。你想想看,如果数据层调用了业务层,那业务层又调用了数据层,代码就成了一团乱麻。

3.1 通信方式

层间通信通常有两种方式:

  • 接口调用:上层通过接口调用下层的方法。这是最常用的方式。
  • 事件驱动:上层发布事件,下层订阅事件。这种方式适合解耦,但会增加复杂度。

我个人更推荐接口调用。原因很简单:简单、直观、容易调试。事件驱动虽然时髦,但如果你不是处理异步场景,没必要给自己找麻烦。

3.2 数据传输对象(DTO)

层间通信时,数据怎么传?我的建议是:使用 DTO(Data Transfer Object)。

举个例子:

// 表现层接收的请求
public class CreateOrderRequest {
    private String userId;
    private List<OrderItemDTO> items;
    // getter/setter
}

// 业务层返回的结果
public class OrderResult {
    private String orderId;
    private BigDecimal totalAmount;
    private String status;
    // getter/setter
}

为什么不用实体类直接传?因为实体类通常包含数据库字段、关联关系等内部细节。暴露给表现层,等于把家底都亮出来了。DTO 可以只包含需要的字段,既安全又清晰。

避坑指南:我曾经见过一个项目,表现层直接用了 Hibernate 的实体类。结果前端不小心传了一个关联对象的 ID,直接触发了懒加载异常。后来我们花了三天把所有接口改成 DTO 传参。记住:层间通信永远不要暴露内部实现。

4.4 三层架构的变体与扩展

实际项目中,三层架构往往会有一些变体:

  • 四层架构:在业务层和数据层之间加一层「服务层」,用于处理跨业务模块的调用。
  • 领域驱动设计(DDD):把业务层拆得更细,引入领域模型、聚合、值对象等概念。
  • 微服务架构:每个微服务内部仍然是三层架构,只是服务之间通过 RPC 或消息队列通信。

但不管怎么变,核心思想不变:分层是为了解耦,解耦是为了可维护。

4.5 本章小结

三层架构不是什么高深的理论,但它是最实用的架构模式之一。我做了这么多年,见过太多项目因为分层不清晰而陷入混乱。记住三点:

  1. 各层职责要清晰:表现层只做界面,业务层只做逻辑,数据层只做存取。
  2. 通信规则要遵守:上层依赖下层,不反向调用,不越层调用。
  3. 数据传输要规范:使用 DTO,不暴露内部实现。

把这些基础打牢了,后面学更复杂的架构模式才会得心应手。

经典三层架构示意图 表现层(Presentation Layer) 用户界面 · 输入校验 · 会话管理 · 异常处理 业务层(Business Layer) 业务逻辑 · 规则校验 · 流程编排 · 事务管理 数据层(Data Layer) 数据存取 · ORM映射 · 连接管理 · 缓存 通信规则:上层依赖下层,下层不依赖上层,禁止跨层调用