4、软件分层架构概述:经典三层架构
聊到软件架构,有个话题绕不开——分层。
我刚开始写代码那会儿,喜欢把所有逻辑塞在一个文件里。数据库查询、业务判断、页面渲染,全搅在一起。当时觉得挺爽,写完就跑。直到项目越来越大,改一个需求要翻半天代码,改完还经常出bug。嗯,那时候我才意识到:不分层,就是在给自己挖坑。
4.1 经典三层架构长什么样?
最经典的分层方式,就是三层架构。说白了,就是把软件切成三块:
- 表现层(UI Layer)——负责跟用户打交道
- 业务逻辑层(BLL Layer)——负责处理核心业务
- 数据访问层(DAL Layer)——负责跟数据库沟通
这三层的关系,就像餐厅的运作模式:
| 层次 | 类比 | 职责 |
|---|---|---|
| 表现层 | 服务员 | 接待顾客、点菜、上菜 |
| 业务逻辑层 | 厨师长 | 决定怎么做菜、把控品质 |
| 数据访问层 | 仓库管理员 | 管理食材、记录库存 |
服务员不会自己跑进厨房炒菜,厨师长也不会亲自去仓库搬货。各司其职,这就是分层的核心思想。
4.2 为什么需要分层?
你可能想问:不分层也能跑,为什么非要折腾?
我在项目中遇到过这样一个场景:一个电商系统,刚开始只有网页版。后来要加手机App,再后来要对接第三方平台。如果代码不分层,表现层和业务逻辑全混在一起,那每加一个客户端,就得重写一遍业务代码。想想就头大。
分层带来的好处,我总结了几点:
- 解耦——改表现层不影响业务逻辑,改数据库不影响上层代码
- 复用——同一套业务逻辑,可以同时服务Web、App、API
- 可维护——出问题能快速定位,是前端问题还是后端问题?
- 可测试——每一层都可以单独写单元测试
核心观点:分层不是为了炫技,而是为了应对变化。你想想看,一个系统上线后,唯一不变的就是「一直在变」。
4.3 分层的核心原则
分层不是随便切几刀就完事。我见过不少团队,分了层但比不分还乱。为什么?因为没遵守基本原则。
原则一:上层依赖下层,下层不依赖上层
这是最基础的一条。表现层可以调用业务逻辑层,业务逻辑层可以调用数据访问层。但反过来不行——数据访问层不能直接调用表现层的东西。
我曾经在一个项目里看到,数据访问层里居然引用了前端的UI组件库。问原因,说是为了方便在数据库查询结果里直接弹窗。嗯,这种写法,后期维护起来简直是噩梦。
原则二:每一层只做自己的事
表现层只负责展示和交互,别在里面写SQL。业务逻辑层只处理业务规则,别在里面拼HTML。数据访问层只做数据存取,别在里面做权限判断。
我习惯用一个简单的检查方法:看一个方法里,是不是混了不同层次的代码。比如:
// ❌ 坏味道:表现层里写SQL
public void showUserInfo() {
String sql = "SELECT * FROM users WHERE id = 1";
// ... 执行查询
// ... 渲染页面
}
// ✅ 正确做法:分层调用
public void showUserInfo() {
User user = userService.getUserById(1);
renderUserPage(user);
}
原则三:层与层之间通过接口通信
不要直接new一个下层对象。用接口或者抽象类来定义契约。这样,下层实现变了,上层不用改代码。
个人经验:我习惯在每一层都定义接口。比如IUserRepository、IUserService。刚开始可能觉得多此一举,但一旦需要切换数据库或者替换实现,你就知道接口的好处了。
原则四:避免循环依赖
A依赖B,B依赖C,C又依赖A——这种循环依赖一旦出现,代码就变成了死结。我见过一个项目,因为循环依赖,改一个地方要重新编译整个项目,编译一次半小时。
避坑指南:我曾经接手过一个遗留系统,表现层和业务逻辑层互相引用。每次改需求,两个团队都要协调半天。后来花了两个月重构,才把循环依赖拆干净。所以,从一开始就要避免。
4.4 三层架构的常见变体
实际项目中,三层架构往往会有一些调整:
- 加一层DTO——在层与层之间传递数据对象,避免暴露内部实体
- 加一层Service——把公共的业务逻辑抽出来,避免重复
- 加一层Cache——在业务逻辑层和数据访问层之间加缓存,提升性能
但不管怎么变,核心思想不变:高内聚、低耦合。
4.5 小结
分层架构不是什么高深的理论。说白了,就是让代码各司其职,互不干扰。我做了十几年开发,见过太多因为不分层而翻车的项目。也见过分层分得过度,导致代码臃肿的项目。
记住一句话:分层是为了解决问题,不是为了制造问题。把握好度,你的代码会越来越清爽。