1、架构分层初探:为什么需要分层?分层架构的演进历史(单体→分层→微服务)
说实话,我做了十几年软件架构,被问得最多的问题就是:「架构到底怎么分?分几层才合适?」
每次听到这个问题,我都会想起自己刚入行时踩过的坑。那时候写代码,一个 .php 文件搞定所有逻辑——数据库查询、HTML 渲染、业务判断全揉在一起。嗯,现在想想,那叫一个酸爽。
今天咱们就来聊聊,为什么非得分层不可?以及这个分层思想是怎么一步步演变过来的。
1.1 不分层的痛:我亲身经历过的「屎山」
先讲个真实故事。2012年我接手过一个电商后台系统,代码量大概 15 万行。你猜怎么着?所有逻辑都在一个模块里。
- 用户点击「下单」,同一个函数里既查数据库、又算价格、还发邮件通知
- 改一个支付逻辑,结果把商品展示页搞崩了
- 新同事入职第一周,光看懂代码流程就花了三天
说白了,这就是典型的「大泥球」架构。所有组件互相依赖,牵一发而动全身。
- 耦合度高:改一处,崩一片
- 可测试性差:没法单独测试某个逻辑
- 维护成本爆炸:新人上手慢,老人改代码提心吊胆
- 扩展性为零:想加个新功能,得动整个系统
为什么会这样?因为没有「边界」。代码之间没有清晰的职责划分,就像一群人挤在一个房间里干活,谁碰了谁都不知道。
3.2 分层架构的诞生:给代码划地盘
为了解决这个问题,前辈们想出了一个办法——分层。
分层架构的核心思想就一句话:各司其职,单向依赖。
我记得最早接触的分层模型是经典的三层架构:
| 层次 | 职责 | 典型技术 |
|---|---|---|
| 表现层(UI) | 用户交互、数据展示 | HTML、JSP、Vue |
| 业务逻辑层(BLL) | 核心业务规则、流程编排 | Java Service、C# 类库 |
| 数据访问层(DAL) | 数据库读写、持久化 | JDBC、MyBatis、EF |
每一层只依赖它的下一层。表现层调业务层,业务层调数据层。反过来不行——数据层绝对不能直接调用表现层的方法。
3.3 从单体到分层:一次质的飞跃
分层架构出现之前,大家写的是「单体应用」。一个 war 包部署到 Tomcat,所有代码都在里面。
分层之后,代码结构变成了这样:
project/
├── web/ # 表现层
│ └── UserController.java
├── service/ # 业务层
│ └── UserService.java
├── dao/ # 数据层
│ └── UserDao.java
└── common/ # 公共层
└── LogUtil.java
你看,目录结构本身就说明了职责。新同事一看就知道:改页面去 web 目录,改业务逻辑去 service 目录。
我曾经在一个项目里,把三层架构用到了极致。每个层都定义了清晰的接口,层与层之间通过接口通信。结果呢?
- 测试可以 mock 掉下层,单独测业务逻辑
- 换数据库?只需要改 dao 层,业务层完全不动
- 团队分工明确:前端写 web 层,后端写 service 层
3.4 从分层到微服务:当分层不够用了
分层架构解决了大泥球的问题,但它也有自己的天花板。
随着业务越来越复杂,一个应用可能包含几十个业务模块。所有模块还在同一个进程里运行,部署时还是得整个应用一起发布。
这时候,微服务架构出现了。
微服务说白了就是:把分层架构中的「层」拆成独立的「服务」。
举个例子:
- 原来一个电商系统,分表现层、订单层、支付层、库存层
- 微服务化之后,订单服务、支付服务、库存服务各自独立部署
每个服务内部仍然保持分层结构,但服务之间通过 API 通信(REST 或 RPC)。
| 维度 | 单体分层 | 微服务 |
|---|---|---|
| 部署粒度 | 整个应用一起部署 | 每个服务独立部署 |
| 技术栈 | 统一技术栈 | 每个服务可选不同技术 |
| 团队规模 | 一个团队维护整个应用 | 每个服务由小团队负责 |
| 通信方式 | 进程内方法调用 | 网络调用(HTTP/RPC) |
你想想看,微服务其实是对分层思想的延续——只不过把「逻辑层」变成了「物理服务」。每一层(服务)仍然各司其职,只不过现在它们跑在不同的进程里。
3.5 架构演进的本质:控制复杂度
回顾一下这条演进路线:
- 单体:所有代码在一起,简单但混乱
- 分层:按职责切分,清晰但耦合仍在
- 微服务:按业务切分,独立但复杂
你会发现,每一次演进都是在解决上一阶段的问题,但同时也引入了新的复杂度。
说白了,架构设计就是一场「权衡」的游戏。没有银弹,只有最适合当前阶段的方案。
好了,这一章我们聊了为什么需要分层,以及分层架构的演进历史。下一章我会带你深入每一层,看看具体怎么设计接口、怎么处理层间依赖、怎么避免常见的分层陷阱。
记住一句话:分层的目的是控制复杂度,而不是增加复杂度。