4. 分层架构模式:经典三层架构
分层架构,说白了就是“各司其职”。
我刚开始带项目时,团队里有个新人把 SQL 查询直接写在 JSP 页面里。当时没觉得有什么,后来业务一变,改一个字段要翻十几个页面。嗯,从那以后,我强制团队必须分层。
今天聊的经典三层架构,是分层思想里最基础、也最实用的一种。它把系统切成三块:表现层、业务层、数据层。
核心原则:上层依赖下层,下层不依赖上层。每一层只关心自己的事。
4.1 三层架构长什么样?
先看一张图,帮你快速建立整体认知。
4.2 每一层到底干什么?
我习惯用“职责边界”来理解分层。边界清晰,代码才不会乱。
| 层级 | 核心职责 | 常见技术 | 常见反例 |
|---|---|---|---|
| 表现层 | 用户交互、数据展示、请求接收 | Vue / React / Spring MVC Controller | 在 Controller 里写业务逻辑 |
| 业务层 | 业务规则、流程编排、数据校验 | Spring Service / 领域模型 | Service 里直接操作数据库 |
| 数据层 | 数据持久化、查询、缓存 | MyBatis / JPA / Redis | SQL 散落在业务代码中 |
我的习惯:表现层只做“翻译”工作——把 HTTP 请求翻译成业务参数,把业务结果翻译成 JSON 响应。多一行逻辑都不写。
4.3 优点:为什么大家都在用?
三层架构能火这么多年,自然有它的道理。
- 职责单一:每层只做一件事。改界面不影响业务逻辑,换数据库不碰前端代码。
- 易于分工:前端团队写表现层,后端团队写业务层,DBA 管数据层。各干各的,互不干扰。
- 可测试性强:业务层可以脱离 UI 和数据层单独做单元测试。我在项目中经常用 Mock 框架模拟数据层,跑业务逻辑的测试用例。
- 演进成本低:从单体到分布式,三层架构可以平滑过渡。表现层拆成微服务网关,业务层拆成领域服务,数据层拆成分库分表——架构思想不变。
4.4 缺点:别把它当万能药
你想想看,如果三层架构完美无缺,那微服务和 DDD 就不会出现了。
- 层与层之间耦合仍然存在:业务层强依赖数据层的接口。一旦数据层接口变化,业务层必须跟着改。
- 性能损耗:每次请求都要经过三层调用。对于高频、低延迟的场景(比如秒杀),这种穿透式调用会成为瓶颈。
- 业务逻辑容易“贫血”:很多团队把业务层写成“数据中转站”——从数据层拿数据,简单处理一下,再扔给表现层。业务逻辑反而散落在各处。
- 不适合复杂业务:当业务规则超过 50 个、实体关系像蜘蛛网一样时,三层架构会变得臃肿。我曾经接手过一个老项目,一个 Service 类写了 8000 行……
避坑指南:我曾经在一个项目中,为了“分层”而分层,硬生生把简单的 CRUD 拆成三个包。结果代码量翻倍,可读性反而下降了。记住:分层是手段,不是目的。
4.5 实战案例:用户注册功能
光说不练假把式。我们用一个用户注册的例子,看看三层架构怎么落地。
4.5.1 表现层(Controller)
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public Result<Long> register(@RequestBody @Valid RegisterRequest request) {
// 只做参数校验和结果返回
Long userId = userService.register(request);
return Result.success(userId);
}
}
看到了吗?Controller 里没有业务逻辑,没有 SQL。它只做三件事:收参数、调服务、返结果。
4.5.2 业务层(Service)
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public Long register(RegisterRequest request) {
// 1. 校验业务规则
if (userRepository.existsByEmail(request.getEmail())) {
throw new BusinessException("邮箱已被注册");
}
// 2. 构建领域对象
User user = User.builder()
.email(request.getEmail())
.password(PasswordEncoder.encode(request.getPassword()))
.status(UserStatus.ACTIVE)
.build();
// 3. 持久化
userRepository.save(user);
// 4. 返回结果
return user.getId();
}
}
业务层是核心。它负责所有业务规则:邮箱唯一性校验、密码加密、状态初始化。数据层怎么存、存到哪里,业务层不关心。
4.5.3 数据层(Repository)
@Repository
public class UserRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
public boolean existsByEmail(String email) {
String sql = "SELECT COUNT(1) FROM users WHERE email = ?";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class, email);
return count != null && count > 0;
}
public void save(User user) {
String sql = "INSERT INTO users (email, password, status) VALUES (?, ?, ?)";
jdbcTemplate.update(sql, user.getEmail(), user.getPassword(), user.getStatus().name());
}
}
数据层只做一件事:和数据库打交道。SQL 写在这里,业务层永远看不到 SQL。
关键点:三层之间通过接口通信。表现层依赖业务层的接口,业务层依赖数据层的接口。接口定义要稳定,实现可以随时换。
4.6 什么时候该用?什么时候该换?
我个人经验是:
- 适合用三层架构的场景:业务逻辑相对稳定、团队规模 10 人以内、系统复杂度中等。比如企业内部管理系统、电商后台、内容管理平台。
- 不适合的场景:高并发实时系统、业务规则频繁变化、需要事件驱动或 CQRS 模式的系统。这时候可以考虑六边形架构或 CQRS 架构。
我的建议:不要为了“先进”而放弃三层架构。很多大厂的早期系统都是三层架构,后来才逐步演进。先跑起来,再优化。
三层架构是架构师的“基本功”。它简单、清晰、容易落地。但你要知道它的边界在哪里——什么时候该坚守,什么时候该放手。这才是经验所在。