2、分层架构总览:表现层、业务层、数据层、基础设施层的职责划分

聊到分层架构,我脑子里第一个蹦出来的画面,是十年前我刚带团队时做的一个「大泥球」项目。所有代码揉在一起,表现层里塞着SQL,业务逻辑里混着HTML标签。改一个按钮颜色,能引发数据库连接池崩溃。嗯,那滋味,谁尝谁知道。

后来我痛定思痛,开始认真搞分层。说白了,分层架构就是把一个复杂的系统,按照职责切分成几个独立的「房间」。每个房间只干自己的事,房间之间通过明确的「门」(接口)来沟通。这样做的好处,你想想看——改一个房间的装修,不会影响到隔壁的承重墙。

今天我们就来拆解这四个核心层:表现层、业务层、数据层、基础设施层。我会结合我在AP平台(自适应平台)项目中的实际经验,把每一层的职责、边界、以及最容易踩的坑,给你讲透。

2.1 表现层:用户的「脸面」

表现层,也叫UI层或展示层。它的职责只有一个:跟用户打交道

用户看到的页面、填写的表单、点击的按钮、弹出的提示,全归它管。但请注意——它只管「展示」和「交互」,不管「业务逻辑」。

核心原则:表现层只做三件事——接收用户输入、展示数据、把请求转发给业务层。

我在项目中遇到过一种典型错误:前端工程师为了省事,直接在JavaScript里写「如果用户余额大于100,显示VIP标识」。这看起来方便,但一旦业务规则变了(比如VIP门槛改成200),你就得改前端代码,还得重新发版。更可怕的是,如果多个前端(Web、App、小程序)都写了同样的逻辑,改起来就是一场噩梦。

正确的做法是什么?表现层只负责调用接口,拿到数据后原样展示。判断逻辑交给业务层。

// ❌ 错误:表现层处理业务逻辑
if (user.balance > 100) {
  showVIPBadge();
}

// ✅ 正确:表现层只展示结果
// 调用业务层接口,拿到 isVIP 字段
if (response.isVIP) {
  showVIPBadge();
}
我的习惯:表现层里不要出现任何「if-else」判断业务状态。所有判断都封装在业务层,返回给前端的是一个「已经算好的结果」。

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

业务层是整个架构的核心。所有的业务规则、流程编排、状态流转,都在这里完成。

我个人习惯把业务层再细分成两块:

  • 领域服务:处理核心业务逻辑,比如「计算订单金额」「校验用户权限」。
  • 应用服务:负责流程编排,比如「下单流程」需要先校验库存、再扣减余额、最后生成订单。

为什么要分这么细?我曾经在一个项目中,把所有业务逻辑都写在一个叫 OrderService 的类里。结果这个类膨胀到8000行,改一个支付逻辑,要担心会不会影响退款逻辑。后来我把它拆成 OrderDomainService(领域)和 OrderApplicationService(应用),问题迎刃而解。

避坑指南:我曾经见过团队把业务层和数据层混在一起,在Service里直接写SQL。这样做短期内开发快,但后期维护成本极高。业务层应该只依赖接口,不依赖具体实现。

业务层还有一个容易被忽视的职责:事务管理。一个业务操作往往涉及多个数据变更,要么全部成功,要么全部回滚。这个「原子性」的保证,应该在业务层控制,而不是交给数据层。

2.3 数据层:信息的「仓库」

数据层负责数据的持久化、查询和缓存。它不关心数据怎么用,只关心数据怎么存、怎么取。

这里要特别注意:数据层不是「数据库层」。它包含但不限于:

组件 职责 举例
Repository 数据存取接口 UserRepository.findById()
ORM映射 对象与数据库表的转换 JPA Entity、MyBatis Mapper
缓存层 提升读取性能 Redis、本地缓存
数据源 连接管理 MySQL、MongoDB、Elasticsearch

数据层有一个铁律:只提供CRUD,不包含业务逻辑。比如,你不能在SQL里写 WHERE age > 18 来表示「成年人」——这个「18」是业务规则,应该放在业务层。

核心原则:数据层返回的是「原始数据」,业务层拿到后做加工。数据层永远不要「替业务层做决定」。

我记得有一次,团队里一位同事在Repository里写了一个 findActiveUsers() 方法,里面直接过滤了状态为「已注销」的用户。后来业务变了,需要展示已注销用户的历史记录,结果不得不改数据层代码。如果当初只提供 findAll(),过滤逻辑交给业务层,就不会有这个问题。

2.4 基础设施层:系统的「地基」

基础设施层是最容易被忽视的一层。它不直接处理业务,但所有层都依赖它。说白了,它就是「让系统能跑起来」的那些东西。

包括但不限于:

  • 通信机制:RPC框架、消息队列、HTTP客户端
  • 配置管理:配置中心、环境变量
  • 日志与监控:日志收集、链路追踪、指标采集
  • 安全认证:鉴权、加密、防攻击
  • 通用工具:日期处理、序列化、异常处理

你可能会问:「这些不都是框架和中间件干的事吗?为什么要单独划一层?」

原因很简单:解耦。如果你的业务代码里直接调用了某个消息队列的API,哪天你想把RabbitMQ换成Kafka,就得改所有调用点。但如果通过基础设施层封装一层接口,业务层只依赖接口,底层随便换。

我的习惯:基础设施层的所有组件,都通过「依赖注入」的方式提供给上层。业务层永远不知道它用的是Redis还是本地缓存,是MySQL还是PostgreSQL。

2.5 层与层之间的「交通规则」

分层架构最核心的约束是:上层依赖下层,下层不依赖上层

具体来说:

  • 表现层 → 依赖 → 业务层
  • 业务层 → 依赖 → 数据层
  • 数据层 → 依赖 → 基础设施层
  • 基础设施层 → 不依赖任何上层

但这里有一个常见的「越级调用」问题。比如表现层直接调用数据层,跳过业务层。为什么不能这样?因为一旦跳过业务层,业务规则就没人执行了。你想想看,如果用户可以直接从页面调用「删除订单」的数据接口,那「只有管理员才能删除」这个规则谁来校验?

避坑指南:我曾经在一个项目中,为了「性能优化」,让表现层直接读取缓存数据。结果缓存里存的是旧数据,业务层根本不知道数据被改了,导致了一连串的数据不一致问题。从那以后,我规定:所有数据访问,必须经过业务层。

2.6 分层架构的「代价」

说了这么多好处,我也得泼点冷水。分层架构不是银弹,它也有代价:

  • 代码量增加:每层都要写接口、实现类、DTO转换,代码量至少翻倍。
  • 性能损耗:每多一层,就多一次方法调用和对象拷贝。对于极高并发场景,需要权衡。
  • 学习成本:新成员需要理解每一层的职责和边界,上手周期变长。

但我的经验是:对于中大型项目,分层架构带来的维护收益,远大于它的代价。对于几十行代码的小工具,你当然可以不用分层。但一旦项目超过10万行代码,没有分层,你就是在给自己挖坑。

总结一句话:表现层管「脸面」,业务层管「脑子」,数据层管「仓库」,基础设施层管「地基」。各司其职,互不越界。

下一章,我会带你手把手搭建一个AP平台的分层架构原型,从项目结构到代码实现,咱们一步步来。到时候你会发现,分层架构一旦用顺手了,写代码就像搭积木一样清晰。