3. 服务识别与划分:业务域分析、服务粒度控制、服务分层
好,咱们进入第三章。这一章我打算聊聊服务识别与划分。说实话,这是整个SOA架构设计里最考验经验的一环。你想想看,服务划得太大,就成了单体应用的变种;划得太碎,又变成分布式泥潭。我见过不少项目,就是因为这一步没走好,后面怎么调都别扭。
3.1 业务域分析:先别急着写代码
我个人习惯,拿到一个新能源项目,第一件事不是画架构图,而是拉上业务方一起做业务域分析。说白了,就是搞清楚「这辆车/这个充电桩/这个储能系统,到底要干哪些事」。
业务域分析的核心是限界上下文(Bounded Context)。这个概念来自DDD,但在SOA里同样好用。我举个例子:
在新能源整车架构里,我们通常可以划分出这几个业务域:
- 动力域:电机控制、能量回收、扭矩分配
- 底盘域:制动、转向、悬架
- 车身域:门窗、灯光、座椅
- 座舱域:仪表、中控、语音交互
- 智驾域:感知、规划、控制
每个域内部,业务逻辑是高度内聚的。域与域之间,通过定义好的接口通信。嗯,这里要注意:不要跨域直接访问数据库。我在项目中遇到过,有人为了图方便,在座舱域里直接查动力域的表,结果后来动力域改了表结构,座舱域崩了一周。
3.2 服务粒度控制:粗了不行,细了也不行
服务粒度这个问题,我估计每个架构师都纠结过。到底一个服务该多大?
我的经验是:服务粒度 = 业务变更频率 × 团队规模。什么意思呢?
- 如果一个业务逻辑经常变,那就把它拆成独立服务。比如充电策略,不同车型、不同地区、不同季节,策略都不一样。拆出来单独迭代,不影响其他服务。
- 如果一个团队只有3个人,却维护了20个微服务,那运维成本会压垮你。我建议一个服务对应一个团队,或者至少一个服务对应一个明确的职责。
我给大家一个参考粒度:
| 服务类型 | 粒度示例 | 说明 |
|---|---|---|
| 过粗 | 整车控制服务 | 包含所有控制逻辑,改一个功能要全量发布 |
| 适中 | 电池管理服务、电机控制服务 | 每个服务聚焦一个子系统,独立迭代 |
| 过细 | 单体电芯电压采集服务 | 服务太多,通信开销大于业务逻辑本身 |
我曾经在一个项目里,把「充电桩状态上报」拆成了「电压上报」「电流上报」「温度上报」三个服务。结果呢?每次充电桩上报一次数据,要调三个接口,网络延迟翻了三倍。后来我学乖了,把高频一起变化的数据,合并到一个服务里。
3.3 服务分层:基础服务 / 业务服务 / 编排服务
服务分层,说白了就是让不同职责的服务各司其职。我习惯把服务分成三层:
3.3.1 基础服务
基础服务是底层能力,不包含任何业务逻辑。比如:
- 日志服务
- 配置管理服务
- 认证鉴权服务
- 消息推送服务
这些服务的特点是:通用、稳定、复用性高。你想想看,不管你是做充电桩还是做储能,日志总得记吧?认证总得做吧?所以基础服务一般由平台团队统一维护。
3.3.2 业务服务
业务服务是核心,承载了具体的业务逻辑。比如:
- 电池健康评估服务
- 充电策略调度服务
- 车辆远程诊断服务
每个业务服务对应一个具体的业务能力。我建议一个业务服务只做一件事,并且把它做好。比如电池健康评估服务,它只负责根据电芯数据计算SOH(健康状态),不负责存储,也不负责展示。
3.3.3 编排服务
编排服务是「指挥家」。它不干具体活,而是协调多个业务服务完成一个复杂流程。比如:
- 充电流程编排服务:调用「鉴权服务」→「充电策略服务」→「充电控制服务」→「计费服务」
- OTA升级编排服务:调用「版本管理服务」→「下载服务」→「安装服务」→「回滚服务」
编排服务的存在,是为了避免业务服务之间直接耦合。你想想看,如果充电流程里,充电控制服务直接调用了计费服务,那以后计费逻辑变了,充电控制也得跟着改。有了编排层,每个业务服务只知道自己该干什么,不需要知道别人在干什么。
- 基础服务稳定,很少改动
- 业务服务聚焦,容易维护
- 编排服务灵活,可以快速调整流程
3.4 实战案例:充电桩远程启动服务
光说理论太干,我拿一个实际场景来串一下。假设我们要设计一个「充电桩远程启动」服务。
第一步:业务域分析
这个功能属于「充电业务域」。充电业务域下还有「充电桩管理」「用户管理」「计费管理」等子域。远程启动属于「充电桩管理」子域。
第二步:服务粒度控制
我一开始想,要不要把「远程启动」「远程停止」「远程预约」都放在一个服务里?后来一想,这三个功能的变更频率不一样:
- 远程启动:基本稳定
- 远程停止:基本稳定
- 远程预约:经常变(不同活动、不同优惠策略)
所以我把「远程预约」拆成了独立服务,而「启动」和「停止」放在一起。这样预约策略变了,不影响启动和停止的稳定性。
第三步:服务分层
- 基础服务:认证鉴权服务(验证用户身份)、消息推送服务(推送启动结果到手机App)
- 业务服务:充电桩控制服务(执行启动指令)、充电桩状态服务(查询当前状态)
- 编排服务:远程启动编排服务(先鉴权→再查状态→再执行启动→最后推送结果)
你看,这样分层之后,每个服务职责清晰。如果以后要加一个「启动前检查车辆是否已插枪」的逻辑,只需要在编排服务里加一步,不需要改任何业务服务。
3.5 小结
服务识别与划分,没有标准答案。但有几个原则,我建议你记住:
- 先做业务域分析,搞清楚边界再动手
- 服务粒度要适中,高频一起变化的数据放一起
- 服务分层要清晰,基础服务、业务服务、编排服务各司其职
嗯,这一章就到这里。下一章我会聊聊服务接口设计,包括RESTful和gRPC怎么选、接口版本怎么管理。到时候见。