03 · 战略设计(下):限界上下文与上下文映射

03 · 战略设计(下):限界上下文与上下文映射

划完子域,接下来回答:每块业务内部的模型边界在哪?边界之间怎么打交道?本篇讲限界上下文的判断方法、最容易犯的"大一统模型"错误,以及六种上下文映射模式。


3.1 限界上下文:模型的户口所在地

限界上下文(Bounded Context)是模型的适用边界------在这条边界内,通用语言的每个词都有唯一、明确的含义。

判断方法一句话:当同一个词在不同场景下含义或规则不同时,就该切上下文了。

3.2 经典案例:一个"商品",三种形态

初学者最容易犯的错:建一个巨大的 Product 类,所有模块共用。看看它的下场:
#mermaid-svg-xQWEQboHeHI9GILp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xQWEQboHeHI9GILp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xQWEQboHeHI9GILp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xQWEQboHeHI9GILp .error-icon{fill:#552222;}#mermaid-svg-xQWEQboHeHI9GILp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xQWEQboHeHI9GILp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xQWEQboHeHI9GILp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xQWEQboHeHI9GILp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xQWEQboHeHI9GILp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xQWEQboHeHI9GILp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xQWEQboHeHI9GILp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xQWEQboHeHI9GILp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xQWEQboHeHI9GILp .marker.cross{stroke:#333333;}#mermaid-svg-xQWEQboHeHI9GILp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xQWEQboHeHI9GILp p{margin:0;}#mermaid-svg-xQWEQboHeHI9GILp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xQWEQboHeHI9GILp .cluster-label text{fill:#333;}#mermaid-svg-xQWEQboHeHI9GILp .cluster-label span{color:#333;}#mermaid-svg-xQWEQboHeHI9GILp .cluster-label span p{background-color:transparent;}#mermaid-svg-xQWEQboHeHI9GILp .label text,#mermaid-svg-xQWEQboHeHI9GILp span{fill:#333;color:#333;}#mermaid-svg-xQWEQboHeHI9GILp .node rect,#mermaid-svg-xQWEQboHeHI9GILp .node circle,#mermaid-svg-xQWEQboHeHI9GILp .node ellipse,#mermaid-svg-xQWEQboHeHI9GILp .node polygon,#mermaid-svg-xQWEQboHeHI9GILp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xQWEQboHeHI9GILp .rough-node .label text,#mermaid-svg-xQWEQboHeHI9GILp .node .label text,#mermaid-svg-xQWEQboHeHI9GILp .image-shape .label,#mermaid-svg-xQWEQboHeHI9GILp .icon-shape .label{text-anchor:middle;}#mermaid-svg-xQWEQboHeHI9GILp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xQWEQboHeHI9GILp .rough-node .label,#mermaid-svg-xQWEQboHeHI9GILp .node .label,#mermaid-svg-xQWEQboHeHI9GILp .image-shape .label,#mermaid-svg-xQWEQboHeHI9GILp .icon-shape .label{text-align:center;}#mermaid-svg-xQWEQboHeHI9GILp .node.clickable{cursor:pointer;}#mermaid-svg-xQWEQboHeHI9GILp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xQWEQboHeHI9GILp .arrowheadPath{fill:#333333;}#mermaid-svg-xQWEQboHeHI9GILp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xQWEQboHeHI9GILp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xQWEQboHeHI9GILp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xQWEQboHeHI9GILp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xQWEQboHeHI9GILp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xQWEQboHeHI9GILp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xQWEQboHeHI9GILp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xQWEQboHeHI9GILp .cluster text{fill:#333;}#mermaid-svg-xQWEQboHeHI9GILp .cluster span{color:#333;}#mermaid-svg-xQWEQboHeHI9GILp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xQWEQboHeHI9GILp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xQWEQboHeHI9GILp rect.text{fill:none;stroke-width:0;}#mermaid-svg-xQWEQboHeHI9GILp .icon-shape,#mermaid-svg-xQWEQboHeHI9GILp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xQWEQboHeHI9GILp .icon-shape p,#mermaid-svg-xQWEQboHeHI9GILp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xQWEQboHeHI9GILp .icon-shape .label rect,#mermaid-svg-xQWEQboHeHI9GILp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xQWEQboHeHI9GILp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xQWEQboHeHI9GILp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xQWEQboHeHI9GILp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 上帝类 Product(300+ 字段)

标题 · 详情 · 图片 · SKU · 库存数量

成交价快照 · 仓库位置 · 营销文案......
商品目录页:只需要标题图片详情

但被迫加载和维护全部字段
库存模块:只关心 SKU 和数量

但改库存要动整个 Product 类
订单模块:只需要成交快照

但商品改价,历史订单语义被污染

DDD 的做法是拆成三个模型,各归各的上下文:
#mermaid-svg-FAWpKQEN5d2Uc75A{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FAWpKQEN5d2Uc75A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FAWpKQEN5d2Uc75A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FAWpKQEN5d2Uc75A .error-icon{fill:#552222;}#mermaid-svg-FAWpKQEN5d2Uc75A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FAWpKQEN5d2Uc75A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FAWpKQEN5d2Uc75A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FAWpKQEN5d2Uc75A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FAWpKQEN5d2Uc75A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FAWpKQEN5d2Uc75A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FAWpKQEN5d2Uc75A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FAWpKQEN5d2Uc75A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FAWpKQEN5d2Uc75A .marker.cross{stroke:#333333;}#mermaid-svg-FAWpKQEN5d2Uc75A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FAWpKQEN5d2Uc75A p{margin:0;}#mermaid-svg-FAWpKQEN5d2Uc75A .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FAWpKQEN5d2Uc75A .cluster-label text{fill:#333;}#mermaid-svg-FAWpKQEN5d2Uc75A .cluster-label span{color:#333;}#mermaid-svg-FAWpKQEN5d2Uc75A .cluster-label span p{background-color:transparent;}#mermaid-svg-FAWpKQEN5d2Uc75A .label text,#mermaid-svg-FAWpKQEN5d2Uc75A span{fill:#333;color:#333;}#mermaid-svg-FAWpKQEN5d2Uc75A .node rect,#mermaid-svg-FAWpKQEN5d2Uc75A .node circle,#mermaid-svg-FAWpKQEN5d2Uc75A .node ellipse,#mermaid-svg-FAWpKQEN5d2Uc75A .node polygon,#mermaid-svg-FAWpKQEN5d2Uc75A .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FAWpKQEN5d2Uc75A .rough-node .label text,#mermaid-svg-FAWpKQEN5d2Uc75A .node .label text,#mermaid-svg-FAWpKQEN5d2Uc75A .image-shape .label,#mermaid-svg-FAWpKQEN5d2Uc75A .icon-shape .label{text-anchor:middle;}#mermaid-svg-FAWpKQEN5d2Uc75A .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FAWpKQEN5d2Uc75A .rough-node .label,#mermaid-svg-FAWpKQEN5d2Uc75A .node .label,#mermaid-svg-FAWpKQEN5d2Uc75A .image-shape .label,#mermaid-svg-FAWpKQEN5d2Uc75A .icon-shape .label{text-align:center;}#mermaid-svg-FAWpKQEN5d2Uc75A .node.clickable{cursor:pointer;}#mermaid-svg-FAWpKQEN5d2Uc75A .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FAWpKQEN5d2Uc75A .arrowheadPath{fill:#333333;}#mermaid-svg-FAWpKQEN5d2Uc75A .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FAWpKQEN5d2Uc75A .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FAWpKQEN5d2Uc75A .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FAWpKQEN5d2Uc75A .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FAWpKQEN5d2Uc75A .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FAWpKQEN5d2Uc75A .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FAWpKQEN5d2Uc75A .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FAWpKQEN5d2Uc75A .cluster text{fill:#333;}#mermaid-svg-FAWpKQEN5d2Uc75A .cluster span{color:#333;}#mermaid-svg-FAWpKQEN5d2Uc75A div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FAWpKQEN5d2Uc75A .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FAWpKQEN5d2Uc75A rect.text{fill:none;stroke-width:0;}#mermaid-svg-FAWpKQEN5d2Uc75A .icon-shape,#mermaid-svg-FAWpKQEN5d2Uc75A .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FAWpKQEN5d2Uc75A .icon-shape p,#mermaid-svg-FAWpKQEN5d2Uc75A .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FAWpKQEN5d2Uc75A .icon-shape .label rect,#mermaid-svg-FAWpKQEN5d2Uc75A .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FAWpKQEN5d2Uc75A .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FAWpKQEN5d2Uc75A .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FAWpKQEN5d2Uc75A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 商品已上架/改价事件
下单时同步快照
库存已扣减事件
订单上下文
OrderItem

· 成交价快照

· 数量

· 购买时名称快照
库存上下文
StockItem

· SKU 编码

· 占用数量

· 可售数量

· 预警阈值 · 仓库位置
商品上下文
Product

· 标题 / 详情 / 图片

· 分类 / 标签

· 营销文案

· 上下架状态

关键认知:

  1. 三个类都叫"商品相关的模型",但没有一个是同一个东西,也不应该是;
  2. 订单上下文的 OrderItem 绝不引用商品上下文的 Product 对象------只存下单那一刻的快照。否则商品改价、改图,历史订单的语义就被污染;
  3. 上下文之间靠领域事件或 API 同步数据,互不侵入对方的模型。

3.3 上下文数量怎么把握

#mermaid-svg-2osZ6XqGxzgHJHFy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2osZ6XqGxzgHJHFy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2osZ6XqGxzgHJHFy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2osZ6XqGxzgHJHFy .error-icon{fill:#552222;}#mermaid-svg-2osZ6XqGxzgHJHFy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2osZ6XqGxzgHJHFy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2osZ6XqGxzgHJHFy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2osZ6XqGxzgHJHFy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2osZ6XqGxzgHJHFy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2osZ6XqGxzgHJHFy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2osZ6XqGxzgHJHFy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2osZ6XqGxzgHJHFy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2osZ6XqGxzgHJHFy .marker.cross{stroke:#333333;}#mermaid-svg-2osZ6XqGxzgHJHFy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2osZ6XqGxzgHJHFy p{margin:0;}#mermaid-svg-2osZ6XqGxzgHJHFy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2osZ6XqGxzgHJHFy .cluster-label text{fill:#333;}#mermaid-svg-2osZ6XqGxzgHJHFy .cluster-label span{color:#333;}#mermaid-svg-2osZ6XqGxzgHJHFy .cluster-label span p{background-color:transparent;}#mermaid-svg-2osZ6XqGxzgHJHFy .label text,#mermaid-svg-2osZ6XqGxzgHJHFy span{fill:#333;color:#333;}#mermaid-svg-2osZ6XqGxzgHJHFy .node rect,#mermaid-svg-2osZ6XqGxzgHJHFy .node circle,#mermaid-svg-2osZ6XqGxzgHJHFy .node ellipse,#mermaid-svg-2osZ6XqGxzgHJHFy .node polygon,#mermaid-svg-2osZ6XqGxzgHJHFy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2osZ6XqGxzgHJHFy .rough-node .label text,#mermaid-svg-2osZ6XqGxzgHJHFy .node .label text,#mermaid-svg-2osZ6XqGxzgHJHFy .image-shape .label,#mermaid-svg-2osZ6XqGxzgHJHFy .icon-shape .label{text-anchor:middle;}#mermaid-svg-2osZ6XqGxzgHJHFy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2osZ6XqGxzgHJHFy .rough-node .label,#mermaid-svg-2osZ6XqGxzgHJHFy .node .label,#mermaid-svg-2osZ6XqGxzgHJHFy .image-shape .label,#mermaid-svg-2osZ6XqGxzgHJHFy .icon-shape .label{text-align:center;}#mermaid-svg-2osZ6XqGxzgHJHFy .node.clickable{cursor:pointer;}#mermaid-svg-2osZ6XqGxzgHJHFy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2osZ6XqGxzgHJHFy .arrowheadPath{fill:#333333;}#mermaid-svg-2osZ6XqGxzgHJHFy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2osZ6XqGxzgHJHFy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2osZ6XqGxzgHJHFy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2osZ6XqGxzgHJHFy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2osZ6XqGxzgHJHFy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2osZ6XqGxzgHJHFy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2osZ6XqGxzgHJHFy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2osZ6XqGxzgHJHFy .cluster text{fill:#333;}#mermaid-svg-2osZ6XqGxzgHJHFy .cluster span{color:#333;}#mermaid-svg-2osZ6XqGxzgHJHFy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2osZ6XqGxzgHJHFy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2osZ6XqGxzgHJHFy rect.text{fill:none;stroke-width:0;}#mermaid-svg-2osZ6XqGxzgHJHFy .icon-shape,#mermaid-svg-2osZ6XqGxzgHJHFy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2osZ6XqGxzgHJHFy .icon-shape p,#mermaid-svg-2osZ6XqGxzgHJHFy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2osZ6XqGxzgHJHFy .icon-shape .label rect,#mermaid-svg-2osZ6XqGxzgHJHFy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2osZ6XqGxzgHJHFy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2osZ6XqGxzgHJHFy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2osZ6XqGxzgHJHFy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 上下文数量怎么定?
信号:过少

· 一个上下文里模型字段大量可空

· 同一个词在代码里有两个含义

· 改一处影响多个不相关模块
信号:过多

· 一个上下文只有一个类

· 跨上下文调用占请求耗时大头

· 团队规模撑不起独立维护
合并
拆分或合并

经验值:一个限界上下文对应一个小团队(两披萨以内)能在数周内完整理解并维护的模型。宁少勿滥------上下文越少,集成的成本越低。

3.4 上下文映射:边界之间的六种契约

上下文之间必然要协作,**上下文映射(Context Mapping)**定义协作的"契约"。六种模式:

模式 一句话解释 典型场景 协作成本
防腐层(ACL) 两上下文间加一层翻译,保护自己模型不被外部污染 对接第三方支付、遗留系统 中
客户-供应商 下游定义需求,上游按需供给,双方协商排期 库存服务于订单 低
开放主机服务(OHS) 上游提供标准化协议/SDK,供所有下游接入 统一认证服务 一次性高、边际低
发布语言(PL) 双方约定公开的报文/事件格式(常与 OHS 搭配) 领域事件 Schema 低
共享内核 两上下文共享一小部分模型,需协同变更 同团队维护的紧密模块 高(慎用)
各行其道 互不集成,靠人工/文件同步 一次性数据导出 最低

最常用的是防腐层

规则:凡是调用"不受你控制的系统"(三方接口、遗留系统),中间必须垫一层翻译对象,绝不让外部报文字段直接穿透进你的领域模型。

java 复制代码
// 三方支付回调原文:字段含义由别人定义,随时会变
class ThirdPartyPayNotify { String tranStat; String amt; String merId; }

// 防腐层:翻译成自己领域的事件
class PaymentAcl {
    PaymentReceived translate(ThirdPartyPayNotify notify) {
        return new PaymentReceived(
                OrderId.of(notify.merId),
                Money.of(notify.amt),
                mapStatus(notify.tranStat));   // "01"=成功 → PAID,翻译规则集中一处
    }
}

好处:三方改字段、换服务商,只改 ACL 一个类;领域模型全程不认识三方的报文格式。

一个完整电商的上下文映射图

#mermaid-svg-2KQ9rZVRRCeGb4Yu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .error-icon{fill:#552222;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .marker.cross{stroke:#333333;}#mermaid-svg-2KQ9rZVRRCeGb4Yu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2KQ9rZVRRCeGb4Yu p{margin:0;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .cluster-label text{fill:#333;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .cluster-label span{color:#333;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .cluster-label span p{background-color:transparent;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .label text,#mermaid-svg-2KQ9rZVRRCeGb4Yu span{fill:#333;color:#333;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .node rect,#mermaid-svg-2KQ9rZVRRCeGb4Yu .node circle,#mermaid-svg-2KQ9rZVRRCeGb4Yu .node ellipse,#mermaid-svg-2KQ9rZVRRCeGb4Yu .node polygon,#mermaid-svg-2KQ9rZVRRCeGb4Yu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .rough-node .label text,#mermaid-svg-2KQ9rZVRRCeGb4Yu .node .label text,#mermaid-svg-2KQ9rZVRRCeGb4Yu .image-shape .label,#mermaid-svg-2KQ9rZVRRCeGb4Yu .icon-shape .label{text-anchor:middle;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .rough-node .label,#mermaid-svg-2KQ9rZVRRCeGb4Yu .node .label,#mermaid-svg-2KQ9rZVRRCeGb4Yu .image-shape .label,#mermaid-svg-2KQ9rZVRRCeGb4Yu .icon-shape .label{text-align:center;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .node.clickable{cursor:pointer;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .arrowheadPath{fill:#333333;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2KQ9rZVRRCeGb4Yu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2KQ9rZVRRCeGb4Yu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2KQ9rZVRRCeGb4Yu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .cluster text{fill:#333;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .cluster span{color:#333;}#mermaid-svg-2KQ9rZVRRCeGb4Yu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2KQ9rZVRRCeGb4Yu rect.text{fill:none;stroke-width:0;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .icon-shape,#mermaid-svg-2KQ9rZVRRCeGb4Yu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .icon-shape p,#mermaid-svg-2KQ9rZVRRCeGb4Yu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .icon-shape .label rect,#mermaid-svg-2KQ9rZVRRCeGb4Yu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2KQ9rZVRRCeGb4Yu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2KQ9rZVRRCeGb4Yu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2KQ9rZVRRCeGb4Yu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户-供应商

下单扣库存
客户-供应商

支付成功发货
客户-供应商

领券用券
开放主机服务

统一发 Token
开放主机服务
防腐层 ACL

三方报文翻译
交易上下文

(核心域)
库存上下文
物流上下文
营销上下文
认证上下文
支付网关(外部系统)

读图要点:

  1. 箭头方向 = 依赖方向。交易上下文是核心域,依赖它的支撑域按它的节奏供给;
  2. 对外部系统(支付网关)一定经过 ACL,图中能看到明确的"隔离带";
  3. 认证是典型 OHS:一个上游服务所有下游,避免每个上下文各自对接登录逻辑。

3.5 上下文映射与团队协作

上下文映射同时是组织架构的镜子(康威定律):

映射模式 背后的团队关系
客户-供应商 两个团队,下游有话语权,排期可协商
防腐层 两个团队互不信任或外部无法协商,各管各的
共享内核 同一个团队或 pair 极紧密的两个团队
各行其道 没有正式协作渠道(警惕:多半是组织问题)

实用推论:如果上下文映射图上出现"共享内核",先检查是不是两个团队在互相改对方的代码------多半该拆团队或拆上下文,而不是共享模型。

3.6 常见疑问

Q:限界上下文 = 微服务吗?

不是,但关系密切。限界上下文是逻辑边界;微服务是物理部署单元。理想状态下一个上下文一个服务,但落地时可以是"多个上下文先住一个单体,各自独立模块"(09 篇详细展开)。

Q:怎么验证上下文边界划对了?

两个体检项:① 每个上下文内部能否用 5~10 句通用语言讲清自己的职责;② 跨上下文的调用/事件是否在真实业务流程中自然出现,还是为了凑数据硬拉的。后者说明边界划错了。


本篇小结:同一个词含义不同就切上下文;"商品"三种形态是最经典的判例;上下文之间用六种映射模式定契约,其中防腐层是保护模型的头号利器;映射图同时反映组织结构。战略设计到此完成,下一篇进入战术设计------边界内部的类怎么写。

关于作者

985 计算机硕士,十年头部大厂技术经历,现任金融公司高级架构师。面试近千人,培养 50+ 新人。

我维护着一个全栈技术博客(yuandianit.com),把踩过的坑整理成了实战文档,覆盖 Java 全栈、AI 应用(RAG 链路 / Agent / 多模型接入)、微信支付完整链路与对账、Wiki 知识库。

相关推荐
Thomas.Sir1 小时前
第35课:Spring Cloud 2025架构复盘、性能调优 & 面试核心总结
spring cloud·面试·架构
努力实干1 小时前
C++ 面向对象与工程架构
开发语言·c++·架构
aichitang20242 小时前
Meta Muse Spark Open:816B 巨型开放权重多模态 Agent 模型架构、部署实战与社区首测全解
大数据·人工智能·python·算法·ai·架构·spark
阡陌数智2 小时前
Anthropic Claude Fable 5.1 长周期 Agent 原生架构解析:工具调用链路稳定性与 API 聚合平台落地实战
人工智能·语言模型·自然语言处理·架构·推荐算法
艾莉丝努力练剑2 小时前
【AI大模型接入SDK】ChatServer接口实现
c++·人工智能·学习·ai·架构·大模型·sdk
孟健18 小时前
LLM API 安全防线:从 OpenCode 盗刷事件到 LiteLLM 多模型容灾实操
人工智能·后端·架构
smallyoung19 小时前
告别N+1:Spring Boot 一键数据翻译框架体系
java·后端·架构
第404号工位20 小时前
40块钱二手瘦客户端,如何跑满家庭物联网中枢?(拒绝Docker爆盘实录)
linux·架构·node.js
Thneonl20 小时前
你写的 NetworkPolicy 可能一条都没生效
后端·架构