4、服务拆分方法论:领域驱动设计(DDD)入门、限界上下文与子域、拆分粒度的把控
说实话,很多团队做微服务拆分,最后都拆成了一团乱麻。我见过最夸张的项目,一个订单服务拆了三十多个,结果每次上线要协调十几个团队,比没拆之前还痛苦。
为什么会这样?
说白了,就是没找到正确的拆分依据。今天这节,我就跟你聊聊我这些年用下来最靠谱的方法——领域驱动设计(DDD)。
4.1 DDD 到底是什么?
DDD 不是一套框架,也不是一个工具。它是一种思维方式。
我个人的理解:DDD 就是让软件模型跟业务模型对齐。业务怎么运作,代码就怎么组织。
举个例子。你想想看,一个电商系统里,「订单」这个词,在销售眼里、在仓库眼里、在财务眼里,含义完全不一样。
- 销售:订单是交易凭证,关注金额、客户信息
- 仓库:订单是拣货单,关注商品、地址、物流
- 财务:订单是应收款,关注支付状态、发票
如果你用一个「订单」对象去满足所有人,那这个类迟早变成上帝类,几百个字段,谁都不敢动。
核心观点:DDD 的核心就是让每个模型只服务于一个明确的业务场景,别想着一个模型打天下。
4.2 限界上下文:划清模型的边界
限界上下文(Bounded Context)是 DDD 里最重要的概念,没有之一。
它解决什么问题?就是上面说的——同一个词在不同场景下含义不同。限界上下文给每个模型划了一个「地盘」,在这个地盘里,模型的定义是唯一的、清晰的。
我在项目中遇到过最典型的案例:一个 CRM 系统里,「客户」这个实体,在售前阶段叫「线索」,在售后阶段叫「联系人」,在财务系统里叫「账户」。如果硬要把它们合并成一个表,那查询逻辑会复杂到让人崩溃。
| 限界上下文 | 模型名称 | 核心关注点 |
|---|---|---|
| 销售上下文 | 线索(Lead) | 潜在客户、跟进状态、转化率 |
| 售后上下文 | 联系人(Contact) | 服务记录、工单、满意度 |
| 财务上下文 | 账户(Account) | 余额、账单、信用额度 |
每个上下文内部,模型可以有自己的生命周期、自己的规则。上下文之间通过明确的接口通信,谁也不干涉谁内部的数据结构。
我的经验:刚开始做 DDD 时,别纠结于「这个字段该放哪个上下文」。先画出业务活动的大致边界,把明显冲突的概念拆开,剩下的可以在迭代中慢慢调整。
4.3 子域:从业务视角看问题
子域(Subdomain)是 DDD 里另一个关键概念。它跟限界上下文有点像,但视角不同。
- 限界上下文:技术视角,是代码层面的边界
- 子域:业务视角,是问题域的划分
子域分为三类:
- 核心子域:业务的核心竞争力,必须自己做。比如外卖平台的「调度算法」。
- 支撑子域:业务需要,但不是核心。比如「用户管理」,可以买现成的。
- 通用子域:所有行业都有的。比如「邮件通知」,直接集成第三方服务。
我记得有一次帮一个物流公司做架构评审,他们想把「路径规划」和「车辆调度」都外包出去。我说,你们的核心竞争力就是「怎么用最少的车送最多的货」,这两块必须自己掌控。后来他们听了建议,自己组建了算法团队,现在已经是行业前三了。
避坑指南:我曾经见过一个团队,把所有子域都当成核心子域来开发,结果每个模块都做得不深,上线后处处是坑。记住:只有 20% 的子域是核心,剩下的 80% 能复用就复用,能买就买。
4.4 拆分粒度的把控:到底拆多细?
这是最实际的问题。DDD 给了你方法论,但没告诉你「拆到多细才算好」。
我个人的经验是三个原则:
原则一:一个限界上下文一个服务
这是最基础的粒度。每个限界上下文内部是高内聚的,对外是低耦合的,天然适合作为一个微服务。
原则二:看团队规模
一个服务应该由一个团队(2-5人)独立维护。如果团队只有 10 个人,你拆出 20 个服务,那每个人要维护 2 个服务,沟通成本会爆炸。
原则三:看变更频率
如果两个模块的变更频率差异很大,就应该拆开。比如「商品展示」每天改,但「价格计算」一个月改一次,那它们就不该在同一个服务里。
下面这张图是我常用的拆分决策流程:
4.5 实战中的常见误区
说了这么多方法论,我再聊聊实际落地时容易踩的坑。
误区一:过度追求完美边界
有些人花几个月画领域图,画完发现业务已经变了。我的建议是:先画个 80% 准确的边界,然后快速落地,在迭代中调整。
误区二:忽略数据一致性
拆成微服务后,跨服务的事务怎么处理?我见过一个团队,把订单和库存拆成两个服务,结果下单时库存扣了但订单没创建成功,用户付了钱却没货。
嗯,这里要注意:跨服务的事务,优先考虑最终一致性,别死磕强一致性。用事件驱动 + 补偿机制,比分布式事务靠谱得多。
误区三:服务间通信太随意
有些团队拆完服务后,服务之间直接调用对方的数据库,或者用 REST API 搞出网状调用。这其实又回到了单体架构,只是把函数调用换成了网络调用。
我的建议:服务间通信尽量用异步消息。同步调用只在查询场景用,而且要做好熔断和降级。我曾经在一个项目里用 RabbitMQ 做事件总线,把服务间的耦合降到了最低,后来业务量翻了三倍,架构基本没动。
4.6 小结
DDD 不是银弹,但它给了我们一套系统的方法论来指导服务拆分。记住三个关键点:
- 限界上下文:给模型划地盘,别让一个模型管所有事
- 子域:分清核心和通用,别什么都自己造
- 粒度把控:看团队、看变更、看业务,别为了拆而拆