4. 分层架构模式:经典三层架构

分层架构,说白了就是“各司其职”。

我刚开始带项目时,团队里有个新人把 SQL 查询直接写在 JSP 页面里。当时没觉得有什么,后来业务一变,改一个字段要翻十几个页面。嗯,从那以后,我强制团队必须分层。

今天聊的经典三层架构,是分层思想里最基础、也最实用的一种。它把系统切成三块:表现层、业务层、数据层。

核心原则:上层依赖下层,下层不依赖上层。每一层只关心自己的事。

4.1 三层架构长什么样?

先看一张图,帮你快速建立整体认知。

经典三层架构示意图 表现层(UI Layer) Web 页面 / 移动端 / API 网关 业务层(Business Layer) 服务编排 / 业务规则 / 校验逻辑 数据层(Data Layer) 数据库 / 缓存 / 文件存储 调用方向:表现层 → 业务层 → 数据层 依赖关系:上层依赖下层,下层对上层无感知

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 架构。
我的建议:不要为了“先进”而放弃三层架构。很多大厂的早期系统都是三层架构,后来才逐步演进。先跑起来,再优化。

三层架构是架构师的“基本功”。它简单、清晰、容易落地。但你要知道它的边界在哪里——什么时候该坚守,什么时候该放手。这才是经验所在。