2.1 限界上下文:确定切口与入路


2.1 限界上下文:确定切口与入路

摘要:限界上下文(Bounded Context)是领域驱动设计里最接近"服务边界"的概念:它定义了一个业务词真正的"上下文密封舱",你在里面怎么说、字段怎么写都不越界。本文用"订单"在不同角色眼里的差异讲透它,并给出用它定位服务边界的实操步骤。

给服务划边界最典型的一个翻车现场,"订单"是主角。买家的"订单"是一张带收货地址、金额、优惠的表单;仓库的"订单"是一件要拣货、打包、称重的出库任务;财务的"订单"是一笔待入账的收入。同一个词,三种截然不同的字段、规则和生命周期。如果你用一个"订单"实体去喂三个角色,最后只能得到一个塞满 if 的大怪物。

同一个"订单",三套上下文

限界上下文说的就是这个:每个业务角色所处的语义环境,都应该是一个独立且封闭的桶。 桶里面的"订单"就该按这个角色的规则来定义,桶之间谁也不越界去改别人的字段。

三个桶之间不是完全没有联系——它们是同一个真实事件的不同投影。但投影的转换是显式的、有规则的,而不是在同一个类里面互相改属性。这种"各有各的模型、通过契约同步"的做法,恰好就是后面我们要讲的"每个服务管自己的数据、靠 API 通信"的雏形。

用什么找到上下文:领域事件与通用语言

找上下文不以"技术分层"为标准,而以"业务上的内聚"为标准。实操靠两样东西。

第一样是通用语言(Ubiquitous Language):和业务专家对同一件事用完全一致的术语说话。如果团队内部叫"订单"、业务方叫"订单",但财务叫"单据"、仓库叫"出库单",那么这几个词很可能各自指向一个独立上下文。

第二样是领域事件:逐个把业务动作列出来——用户下单、订单支付成功、仓库已发货、物流已签收。凡是"一起发生、一起变数据"的动作,大概率属于同一个上下文;凡是"一个动作会触发另一个上下文里的动作"的,就是边界信号,要用事件或异步消息来解耦。

一个实操:给商城找边界

把商城的能力列出来,用"脸朝谁"和"数据跟谁走"两问来归类:

候选能力 它服务谁 数据跟谁一起变 归属上下文
商品列表 买家 商品、类目 商品上下文
下单 买家 购物车、订单 交易上下文
支付回调 买家/财务 支付单 支付上下文
库存扣减 仓库 库存、出入库 仓配上下文
会员积分 买家 积分、等级 用户上下文

你会发现,"商品"和"库存"明明数据很近,却分属不同上下文——因为商品的频率是"上新与定价",库存的频率是"卖出就扣",它们面对的角色和变更节奏都不同。把这两个硬切到一个上下文里,只会互相拖累。这正是服务边界"跟着语义走,不看数据近不近"的精髓。

切完之后的协同:上下文映射

边界切定后,桶之间还得协作。DDD 给了一套"上下文映射"的连接方式,常见两三种:

  • 共享内核:两个上下文共同维护一小块双方都认可的核心模型(适合紧耦合但能管住的场景)。
  • 防腐层(Anti-Corruption Layer):在边界处加一层翻译/校验,把对方的模型转成自己的模型,防止对方的坏味道渗进来。这是拆服务时最值得用的减震垫。
  • 发布事件 / 消息:一方发布领域事件,另一方订阅,各自落各自的库,靠最终一致协同——这正好引向我们第 3 章的异步通信和第 4 章的数据一致性。

陷阱:把"表拆分"当"上下文拆分"

很多团队说自己在做限界上下文,实际上只是把原来的一张订单表拆成几张表,再给每个服务分别塞一块——这切的是表,不是上下文。判断标准很简单:看能不能"一口报出这是谁的规则"。如果某个字段要同时照顾三个角色、某个状态机要兼容三条生命周期,那它就是一个还没拆干净的上下文,拆了表也只是把矛盾转移到了服务之间。

一个容易反直觉的判断:上下文越小,跨上下文协作越多

看清限界上下文的好处后,多数人容易冲向另一个极端——"那就切得越碎越好,一个上下文一个小服务"。这个方向要打住。因为在这里有个直白的负反馈:每一刀切出来一个上下文,也就同时制造出一条要靠网络才能补上的协作关系。上下文越小越干净,但不等于服务越碎越好,因为碎到一定程度,围绕同一笔"真正业务"的编排、同步、一致性开支,会反过来超过切分带来的隔离红利。

所以务实的标尺不是"多碎",而是**"这个上下文的内聚收益,够不够抵消它带来的协作成本"**。一个成熟的团队常常把几个相邻的小上下文按"变更节奏相近、团队归属一致"合并成一个服务,看起来不"最纯",但从运营角度反而更省。记住:限界上下文是用来划定"语义的桶",而服务是"实际运行的部署单元",两者有关联但不等同——中间还隔着团队编排与运维成本这一层现实。

一句能立刻上手的"划界口诀"

如果上面的方法让你一时不知从何下手,这里给一句能当场用来画草图的口诀:"它到底顺着谁的业务走?谁改它的理由最多、最单一?" 顺着这条问到底,边界通常会自己浮上来——一个能力如果"改它常常是为同一个角色、同一类变更服务",它就倾向是一个独立上下文;如果"改它的理由五花八门、要同时迁就好几个角色",那它多半还没切干净。拿这句话在团队里问一轮,往往比盯着 UML 图纠结半天更有效率。边界是用来让"谁负责、谁都改得动"变清楚的,不是为了画一张漂亮的图。

一个更常见的病灶:上下文"开裂"

比"表拆了上下文没拆"更隐蔽的,是另一种叫上下文开裂的病——同一个业务语义,在同一个上下文里被不自觉切成了两份,各管各的却不自知。举例:同样是"用户",下单服务里存了一份姓名加地址,营销服务里又存一份姓名加手机号,两边都觉得自己在管用户,等要合并促销和订单数据时才发现两个"用户"根本对不上。判断标准是那个"一口报出这是谁的规则"的反面:如果同一个词在两个地方都有人改、且各改各的,那它们其实已经是两个隐形的限界上下文,只是没人给它们画线。此时最好的动作不是把两个服务合并,而是先认清它们本来就该分开、各自定义清楚自己的通用语言——边界如果已经长出来了,去承认它、把它画出来,比硬把它塞回一个桶里省力得多。

本节要点

  • 限界上下文是"一个业务词的语义密封舱",桶内自洽、桶间靠契约同步
  • 用通用语言和领域事件定位边界:谁的话术成对、谁的数据一起变
  • 边界跟随业务语义走,不看出数据距离近不近
  • 防腐层是拆服务时的减震垫,防止坏味道互相渗透
  • 先拆上下文再谈拆表,否则只是把矛盾从表搬到服务间

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U