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 小结

分层架构不是什么高深的理论。说白了,就是让代码各司其职,互不干扰。我做了十几年开发,见过太多因为不分层而翻车的项目。也见过分层分得过度,导致代码臃肿的项目。

记住一句话:分层是为了解决问题,不是为了制造问题。把握好度,你的代码会越来越清爽。