4、服务粒度设计原则:单一职责、高内聚低耦合、服务粒度与复用性权衡

好,咱们进入第四讲。服务粒度怎么定?这是我在SOA项目里被问得最多的问题。

说白了,服务粒度就是「一个服务该管多宽的事」。管得太宽,变成大泥球;管得太窄,服务满天飞,调用链能绕地球三圈。我见过一个项目,拆了三百多个微服务,结果一个简单功能要调二十次接口——这哪是SOA,这是自找麻烦。

4.1 单一职责原则:一个服务只做一件事

这个原则来自面向对象设计,但在SOA里更关键。一个服务应该只对一类业务行为负责。

举个例子。你设计一个「车辆信号服务」,如果它既管车速信号,又管车窗信号,还管发动机诊断——嗯,这就不对了。车速和车窗的更新频率、安全等级、消费方完全不同,硬塞在一起,改一个就得动全部。

我的判断标准:

  • 这个服务的变化原因是否唯一?
  • 如果需求变了,改动的范围能否控制在这个服务内?
  • 服务的消费者是否属于同一类角色?

我在项目中遇到过这样一个坑:把「车门控制」和「车窗控制」合并成一个服务。理由是「都是车身域嘛」。结果呢?车门逻辑要升级防盗策略,车窗逻辑要优化防夹算法,两个团队改同一个代码库,合并冲突天天有。后来拆开,世界清净了。

4.2 高内聚低耦合:服务内部的「抱团」与服务间的「疏离」

高内聚,就是服务内部的功能要紧密相关。低耦合,就是服务之间依赖要少、要弱。

怎么理解?我习惯这样看:

  • 高内聚:服务里的每个方法、每个数据,都围绕同一个业务目标。比如「车速服务」,它应该包含车速采集、车速滤波、车速发布、车速异常检测。这些功能天然在一起。
  • 低耦合:服务A调用服务B,只通过接口,不共享数据库,不直接访问对方的内部状态。你想想看,如果服务A直接读了服务B的数据库表,那B一改表结构,A就崩了——这叫紧耦合,要命。

一个小技巧:

画服务依赖图的时候,如果发现某个服务被超过5个其他服务依赖,或者它依赖超过5个其他服务,就要警惕了。这往往是粒度不合适的信号。

我曾经接手过一个项目,有个「通用信号服务」,几乎所有的ECU都依赖它。结果每次升级,全车测试。这就是典型的低内聚高耦合——什么信号都往里塞,谁都离不开它,改起来谁都受影响。

4.3 服务粒度与复用性的权衡

这是最考验架构师经验的地方。粒度越细,理论上复用性越高——但代价是管理成本爆炸。粒度越粗,管理简单,但复用性差,重复代码多。

我个人的经验是:不要追求「极致复用」。复用性不是越高越好,够用就行。

粒度 优点 缺点 适用场景
细粒度(如单个信号) 复用性极高,独立部署 服务数量爆炸,调用链长,延迟高 高频率变化、多消费者场景
中粒度(如功能域) 平衡复用与管理成本 边界划分需要经验 大部分车载SOA场景
粗粒度(如整车域) 管理简单,部署方便 复用性差,变更影响大 稳定功能、硬件绑定场景

你看这个表,没有绝对的好坏。我建议你从「中粒度」起步,也就是按功能域划分。比如「车身控制域」拆成「门控服务」「窗控服务」「灯控服务」——每个服务管一类设备,但又不会细到「左前门电机控制服务」这种程度。

避坑指南:

我曾经把一个「雨刮控制服务」拆成了「雨刮速度服务」「雨刮位置服务」「雨刮诊断服务」三个。结果呢?雨刮每次动作要调三个服务,延迟从5ms飙到50ms。后来合并成一个,反而更清爽。记住:服务粒度要跟业务操作的原子性对齐,而不是跟数据字段对齐。

4.4 我常用的粒度判断方法

说了这么多理论,给点实操的。我一般用三个问题来判断粒度是否合适:

  1. 这个服务能不能独立演进而不用通知所有消费者? 如果能,粒度合适。如果不能,说明耦合太紧或者粒度太粗。
  2. 这个服务的接口数量是否在5-15个之间? 少于5个,可能太细了;多于15个,可能太粗了。当然这不是铁律,但可以作为参考。
  3. 如果把这个服务再拆一半,会不会引入分布式事务? 如果会,说明现在粒度已经够细了,别再拆了。

你想想看,分布式事务有多麻烦?两阶段提交、补偿机制、最终一致性……能不碰就别碰。所以我的原则是:宁可粒度稍粗,也不要引入分布式事务

4.5 一个实际案例

最后分享一个我经手的案例。某项目要做「空调控制」的SOA化。一开始团队设计了四个服务:

  • 温度传感器服务
  • 空调压缩机服务
  • 鼓风机服务
  • 空调面板服务

看起来挺合理对吧?但实际跑起来发现:每次用户调温度,要依次调温度传感器服务获取当前温度、调压缩机服务设定功率、调鼓风机服务调整风速——三个服务串行调用,延迟高得离谱。

后来我建议合并成两个服务:

  • 空调控制服务:负责温度设定、风速调节、模式切换
  • 空调诊断服务:负责故障检测、状态上报

为什么这么分?因为「控制」和「诊断」的变化频率不同、消费者不同。控制是高频实时操作,诊断是低频监控。拆开之后,控制路径延迟降了60%,诊断逻辑升级也不影响控制功能。

嗯,这就是粒度设计的魅力——不是越细越好,而是「刚刚好」。

核心要点回顾:

  • 单一职责:一个服务只对一类业务行为负责
  • 高内聚:服务内部功能紧密围绕同一目标
  • 低耦合:服务间只通过接口交互,不共享内部状态
  • 粒度权衡:从「中粒度」起步,避免过度拆分引入分布式事务
  • 判断方法:独立演进能力、接口数量、分布式事务风险

下一讲,咱们聊聊服务接口设计——怎么定义接口才能既灵活又稳定。到时候见。