3、服务拆分原则与策略
聊到微服务拆分,很多人第一反应就是「按功能拆呗」。嗯,这话没错,但太笼统了。我在实际项目中见过太多拆分失败的案例——要么拆得太碎,一个订单服务拆成十几个,结果改个需求要协调七八个团队;要么拆得太粗,本质上还是单体,只不过换了个马甲。
这一章,我就把服务拆分的核心原则和策略掰开揉碎讲清楚。尤其是库存系统,它天生就是个「高耦合」的业务模块,怎么拆才合理?我们一步步来看。
3.1 微服务拆分的两大原则
3.1.1 高内聚低耦合
说白了,就是把关系紧密的功能放在同一个服务里,服务之间尽量少通信。你想想看,如果两个功能频繁互相调用,那它们就不该被拆开。
高内聚:一个服务内部的各个模块,应该围绕同一个业务目标。比如库存服务里的「入库」、「出库」、「库存查询」,它们都围绕「库存数量」这个核心概念。
低耦合:服务之间的依赖要轻。接口要稳定,数据模型要独立。我见过一个项目,订单服务直接读取库存服务的数据库表——这哪是微服务?这是分布式单体。
3.1.2 单一职责
这个原则来自面向对象设计,但在微服务里同样适用。一个服务只做一件事,并且把它做好。
举个例子:库存服务只负责库存数量的增减和查询。它不应该管订单的支付状态,也不应该管商品的定价。一旦你让库存服务去处理「订单支付成功后才扣减库存」的逻辑,那这个服务就变脏了。
我个人习惯用「业务动词」来检验单一职责。一个服务对外暴露的接口,动词应该高度一致。比如库存服务:reserve()、release()、queryStock()。如果出现 createOrder() 或 calculatePrice(),那就要警惕了。
3.2 拆分策略:两种主流方法
3.2.1 按业务能力拆分
这是最直观的方式。看业务系统有哪些核心能力,每个能力对应一个服务。
拿电商系统举例:
- 商品服务:商品信息、分类、属性
- 订单服务:订单创建、状态流转
- 库存服务:库存管理、出入库
- 支付服务:支付、退款
- 用户服务:用户信息、地址
这种拆分方式的好处是直观,业务人员也能理解。但问题在于,业务能力之间的边界有时候很模糊。比如「预售」功能,它既涉及商品,又涉及库存,还涉及订单。你把它放哪?
3.2.2 按子域拆分(DDD 方式)
这是更精细的做法。先做领域建模,识别出核心域、支撑域、通用域,然后按限界上下文来拆分。
库存系统在 DDD 里通常属于支撑域。它不直接产生订单,但没有它订单也跑不起来。它的限界上下文很清晰:库存聚合根、库存流水、库存预警。
我建议的做法是:先用业务能力拆分做一个粗粒度的划分,再用 DDD 的子域分析来验证和调整。不要一上来就搞 DDD,容易陷入过度建模。
3.3 库存系统服务粒度评估
好,理论讲完了。我们拿库存系统来实战一下。假设我们要设计一个库存微服务,怎么判断粒度是否合理?
我总结了四个评估维度:
| 评估维度 | 说明 | 库存系统示例 |
|---|---|---|
| 业务完整性 | 服务是否覆盖一个完整的业务场景 | 库存服务应包含:库存查询、预占、释放、实际扣减 |
| 数据独立性 | 服务是否拥有独立的数据库 | 库存数据不应与订单数据混在一个库 |
| 变更频率 | 服务内部模块的变更频率是否一致 | 库存预警逻辑经常变,但基础库存查询很少变——是否要拆开? |
| 团队规模 | 一个服务是否只需要一个小团队维护 | 库存服务通常 2-3 人维护即可,拆太细反而增加沟通成本 |
这里有个常见的纠结:库存预占和库存实际扣减,要不要拆成两个服务?
我的答案是:不要拆。为什么呢?
- 预占和扣减操作的是同一批数据(库存数量)
- 它们的事务一致性要求极高——预占了没扣减,库存就虚高了;扣减了没预占,库存就负数了
- 拆成两个服务,你就得引入分布式事务,复杂度飙升
你看,这就是用「数据独立性」和「业务完整性」两个维度交叉验证的结果。
最后,我画了一张图,把服务拆分的核心逻辑串起来。你可以把它当作一个决策流程:
嗯,这一章的内容就到这里。服务拆分没有银弹,但掌握了原则和策略,至少不会走偏。下一章我们会深入库存系统的数据模型设计,看看怎么在微服务架构下保证数据一致性。
公众号:蓝海资料掘金营,微信deep3321