很多后端项目,到一定阶段都会变成这样:

  • Controller 很薄

  • Repository 很多

  • Service 层全是:
    save()update()getById()list()

看起来分层齐全,实际上整个系统在结构上已经塌了一半

因为:
👉 Service 层如果只剩 CRUD,这个系统本质上没有“业务层”。

这篇文章,专门讲一个分水岭问题:

👉 Service 层到底存在的意义是什么?为什么它绝对不能只写 CRUD?

一、先给结论:CRUD 型 Service = 伪分层

如果一个项目里:

UserService.save(user)
UserService.update(user)
UserService.getById(id)

Service 层只是把 Repository 包了一层。

那么这个项目,本质结构是:

Controller → DAO → Database

👉 Service 层只是“形式存在”,不是“架构存在”。

这种系统通常具备几个特征:

  • 业务规则散落在 Controller / SQL / 各种 if else
  • 事务到处飞
  • 改一个规则,全链路跟着动
  • 项目越大,越不敢动

👉 这是绝大多数“中后期失控系统”的起点。

二、Service 层存在的真正原因

Service 层不是为了“多一层”。

它的真实使命只有一个:

👉 表达“业务用例”和“系统流程”。

也就是说:

  • 一个用户注册
  • 一个订单创建
  • 一次支付流程
  • 一次库存扣减
  • 一次状态流转

这些,都应该是 Service 层的语言。

Service 层的核心关键词是:

👉 编排(Orchestration)

而不是:

👉 存取(CRUD)

三、CRUD 型 Service 和业务型 Service 的本质区别

❌ CRUD 型 Service(伪业务层)

public void save(User user){
    userRepository.save(user);
}

特征:

  • 一方法一张表
  • 没有业务语义
  • 没有流程概念
  • 没有边界

👉 它本质是 DAO 的“别名”。

✅ 业务型 Service(真实业务层)

@Transactional
public void register(RegisterUserCommand cmd){
    User user = User.create(cmd);
    userRepository.save(user);

    Account account = Account.init(user.getId());
    accountRepository.save(account);

    couponService.initFor(user);
    eventPublisher.publish(new UserRegisterEvent(user));
}

特征:

  • 一个方法 = 一个业务动作
  • 多实体协作
  • 有事务边界
  • 有失败语义
  • 可演进

👉 这才是后端系统真正的“骨架”。

四、Service 层的四个核心职责

一个健康的 Service 层,至少承担四类责任:

1️⃣ 业务流程编排

Service 层关心的是:

👉 “一件事如何在系统中完成”

而不是:

👉 “一条数据怎么存”。

例如:

  • 创建订单 = 校验 → 生成订单 → 锁库存 → 写明细 → 发消息

👉 这是流程,不是 CRUD。

2️⃣ 事务边界控制

事务几乎永远应该收敛在 Service 层。

因为:

👉 事务表达的是业务一致性边界

  • 哪些操作必须一起成功
  • 哪些失败要整体回滚

这些,只有业务层知道。

3️⃣ 业务边界与用例建模

Service 层的方法名,本质是在回答:

👉 系统“能干什么”。

例如:

  • registerUser
  • createOrder
  • payOrder
  • cancelOrder
  • transferMoney

👉 它们是“业务语言”,不是“数据语言”。

4️⃣ 系统协作入口

Service 层是:

  • Controller 调用的入口
  • 定时任务调用的入口
  • MQ 消费调用的入口
  • RPC 接口调用的入口

👉 它是整个系统对外暴露的“能力边界”

五、为什么 CRUD 型 Service 一定会失控?

因为它天然会导致三件事:

❌ 1. 业务逻辑四散

  • Controller 写一点
  • SQL 写一点
  • 定时任务再写一点

👉 系统没有“业务核心”。

❌ 2. 事务边界混乱

  • 有的在 Controller
  • 有的在 DAO
  • 有的根本没有

👉 数据一致性靠运气。

❌ 3. 架构无法演进

一旦出现:

  • 分库分表
  • 缓存
  • 事件驱动
  • 微服务拆分

CRUD Service 会全盘爆炸

六、如何判断你的 Service 层是不是“真业务层”?

给你一个非常实用的自检表:

✅ 好 Service 的特征

  • 方法名是业务动词,不是数据库动词
  • 一个方法经常操作多张表
  • Service 层最容易写事务
  • Service 可以被多入口复用
  • Service 里经常出现领域对象行为

❌ 坏 Service 的特征

  • 满屏 save/update/get
  • 一个方法只调一个 DAO
  • Service 看起来像“转发器”
  • 业务规则散落各处

七、那 CRUD 应该放哪?

结论很简单:

👉 CRUD 是基础设施能力。

它的自然归宿是:

  • Repository
  • Mapper
  • DAO
  • Persistence

Service 层可以用 CRUD,但不应该“等于 CRUD”。

八、Service 层与 Domain 层的关系

很多人会再问一句:

👉 那业务逻辑到底放 Service 还是 Domain?

一句工程级判断:

  • 业务流程 → Service
  • 业务规则 → Domain

例如:

  • “注册要创建用户 + 初始化账户” → Service
  • “用户手机号必须合法” → Domain

👉 Service 负责“调度”,Domain 负责“成立”。

九、总结一句非常重要的话

👉 Repository 决定你怎么“存数据”。
👉 Domain 决定你“业务是否成立”。
👉 Service 决定你“系统能干什么”。

如果 Service 退化为 CRUD:

👉 这个系统,本质就退化成了“数据库脚本集合”。

十、阶段性总结

  • Service 层不是 DAO 包装层
  • Service 层是业务能力层
  • Service 层是事务边界层
  • Service 层是系统演进支点

当你开始自然地写出:

createOrder()
cancelOrder()
payOrder()

而不是:

saveOrder()
updateOrder()

你就已经从“写接口”,走向了“建系统”。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐