分层架构:软件工程的“分而治之”之道
说起分层架构,我脑子里第一个蹦出来的词就是“分而治之”。这几乎是所有软件系统最基础、最经典的架构模式。你想想看,一个复杂的系统,如果所有代码都揉在一起,那维护起来得多痛苦?
分层架构的核心思想,就是把系统按照关注点不同,拆成几个独立的层。每一层只负责自己的那摊事,层与层之间通过定义好的接口通信。说白了,就是“各扫门前雪”。
分层架构的原则
分层架构不是随便分分就行的。我见过不少项目,层是分了,但分得乱七八糟,最后比不分还乱。这里有几个核心原则,我个人觉得特别重要:
- 单向依赖原则:上层只能依赖下层,下层绝对不能反向依赖上层。这是铁律。我曾经在一个项目里看到业务层直接调用了表现层的UI组件,结果改个按钮样式,业务逻辑也跟着崩,那叫一个酸爽。
- 封装性:每一层对外只暴露必要的接口,内部实现细节完全隐藏。下层怎么改,只要接口不变,上层就不用动。
- 职责单一:每一层只做自己该做的事。表现层别去拼SQL,持久层别去处理用户输入。
- 可替换性:理论上,每一层都可以被替换,而不影响其他层。比如你把持久层从MySQL换成MongoDB,只要接口不变,业务层代码一行都不用改。
核心要点:分层架构的本质是“关注点分离”。每一层都是一个独立的抽象级别,上层依赖下层的抽象,而不是具体实现。
各层职责详解
经典的三层架构,通常分为表现层、业务层和持久层。嗯,这里要注意,有些系统会再加一个领域层或者服务层,但核心逻辑是一样的。
1. 表现层(Presentation Layer)
表现层就是用户能看到的那部分。它负责和用户交互,接收用户的输入,展示数据给用户看。说白了,就是系统的“脸面”。
- 职责:处理HTTP请求、表单验证、数据格式化、页面渲染、会话管理等。
- 常见技术:Spring MVC的Controller、Vue/React组件、JSP页面等。
- 注意:表现层不应该包含任何业务逻辑。我见过有人把复杂的计算逻辑写在Controller里,结果想复用这个逻辑,只能复制粘贴,维护起来想哭。
// 表现层示例:只做参数校验和调用业务层
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/users")
public ResponseEntity<UserVO> createUser(@Valid @RequestBody UserCreateRequest request) {
// 表现层只做简单的参数校验,真正的业务逻辑在Service层
UserVO userVO = userService.createUser(request);
return ResponseEntity.ok(userVO);
}
}
2. 业务层(Business Layer)
业务层是系统的核心,所有业务规则、逻辑判断都在这里。它不关心数据怎么存,也不关心页面怎么展示,只关心“业务该怎么走”。
- 职责:实现业务规则、事务管理、权限校验、业务流程编排等。
- 常见技术:Spring的Service层、EJB等。
- 注意:业务层应该保持“纯逻辑”,不依赖任何框架或基础设施。我习惯把业务逻辑写成POJO类,这样单元测试特别好写。
// 业务层示例:只处理业务逻辑,不关心数据来源
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public UserVO createUser(UserCreateRequest request) {
// 业务规则:用户名不能重复
if (userRepository.existsByUsername(request.getUsername())) {
throw new BusinessException("用户名已存在");
}
// 业务规则:新用户默认状态为激活
User user = new User(request.getUsername(), request.getPassword(), UserStatus.ACTIVE);
userRepository.save(user);
return UserVO.from(user);
}
}
3. 持久层(Persistence Layer)
持久层负责和数据库打交道。它把业务层需要的对象存进去,或者从数据库里查出来。说白了,就是数据的“搬运工”。
- 职责:CRUD操作、数据映射、连接管理、缓存等。
- 常见技术:MyBatis、JPA/Hibernate、Spring Data JPA等。
- 注意:持久层只做数据访问,不要在这里写业务逻辑。我曾经看到有人把复杂的报表计算写在SQL里,结果数据库CPU飙到100%,业务层想优化都无从下手。
// 持久层示例:只做数据访问
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 方法名就表达了查询意图,不需要写SQL
boolean existsByUsername(String username);
@Query("SELECT u FROM User u WHERE u.status = :status")
List<User> findByStatus(@Param("status") UserStatus status);
}
分层架构的优缺点
任何架构模式都不是银弹。分层架构用了这么多年,自然有它的好处,但也有让人头疼的地方。
优点
- 易于维护:每一层职责清晰,改表现层不会影响业务层,改持久层也不会影响表现层。团队可以并行开发,各管各的层。
- 易于测试:每一层可以独立测试。业务层可以Mock掉持久层,只测业务逻辑。我习惯在写业务层代码时,先写单元测试,再写实现,这样代码质量高很多。
- 标准化:分层架构是一种通用模式,新成员上手快。你只要说“这是表现层,那是业务层”,大家基本都懂。
- 可扩展性:如果以后要加新的表现层(比如从Web端扩展到移动端),只需要新增一个表现层,业务层和持久层完全复用。
缺点
- 性能损耗:每一层调用都有开销。如果层数太多,或者层间通信频繁,性能会受影响。我曾经优化过一个系统,就是因为分层太死板,一个简单的查询要经过三层对象转换,最后直接合并了两层才解决问题。
- 代码冗余:有时候一个简单的功能,需要在每一层都写一个对应的类(VO、DTO、Entity),代码量会翻倍。嗯,这里可以用一些工具(如MapStruct)来减少重复劳动。
- 过度抽象:有些人为了分层而分层,明明很简单的一个功能,硬要拆成三层,结果代码变得又长又绕。记住,分层是手段,不是目的。
- 层间耦合:虽然分层降低了层内耦合,但层与层之间还是有依赖的。如果接口设计得不好,改一个接口可能影响所有上层。
我的建议:不要一开始就把所有层都建好。先从一个简单的两层结构开始(表现层+业务层),等业务复杂了,再慢慢把持久层抽出来。过度设计比不设计更可怕。
避坑指南:我曾经在一个项目里,看到有人把业务逻辑写在了持久层的Repository里,理由是“这样查询快”。结果后来业务规则变了,改一个查询条件,要翻遍所有Repository。记住,持久层只负责数据访问,业务逻辑永远留在业务层。
分层架构的核心逻辑图
下面这张图,展示了经典三层架构的调用关系和依赖方向。注意箭头方向,上层依赖下层,下层绝不反向依赖。
什么时候该用分层架构?
说实话,大部分企业级应用都适合分层架构。特别是那些业务逻辑复杂、需要长期维护的系统。但如果你只是写个简单的脚本工具,或者一个原型验证项目,那完全没必要硬套分层。
我个人判断的标准很简单:如果这个系统需要活过一年以上,或者需要多人协作开发,那就用分层架构。否则,怎么简单怎么来。
好了,关于分层架构,今天就聊这么多。记住,分层是工具,不是枷锁。灵活运用,才是王道。
公众号:蓝海资料掘金营,微信deep3321