Arch Day 9: DDD战略设计(高级)
DDD战略设计的核心不是画"聚合根/实体/值对象"(那是战术设计),而是识别系统中的语言边界(Bounded Context),并明确各个上下文之间的协作关系(Context Mapping),从而在组织层面建立清晰的系统拓扑。
日期: 2026-04-08 (Day 9) 阶段: 第一阶段 - 架构基础 标签: #DDD #Context-Mapping #限界上下文 #战略设计 #组织拓扑
核心概念
一句话定义
DDD战略设计的核心不是画"聚合根/实体/值对象"(那是战术设计),而是识别系统中的语言边界(Bounded Context),并明确各个上下文之间的协作关系(Context Mapping),从而在组织层面建立清晰的系统拓扑。
为什么资深架构师仍需关注
做了10年金融软件,我发现90%的架构问题不是技术问题,而是边界划分问题。DDD战略设计解决的就是这个问题:
- 边界决定一切:微服务拆分的边界、团队的职责范围、API的契约——都由Bounded Context决定
- Context Map是架构图的前置条件:没有Context Map就画架构图,等于没有地图就开车
- 组织政治的可视化工具:Context Mapping的8种关系模式直接映射了团队间的权力关系
- 演进式设计的指南针:系统演进时,Context Map告诉你哪些边界是稳定的,哪些需要调整
常见误区与反模式
| 误区 | 真相 | 后果 |
|---|---|---|
| "一个微服务=一个Bounded Context" | BC是逻辑边界,微服务是部署单元,两者可以1:1也可以1:N | 要么服务太细(运维灾难)要么太粗(耦合) |
| "边界越多越好" | 过多的边界增加集成复杂度 | 团队疲于处理跨上下文通信 |
| "先画战术模型再划边界" | 战略设计(边界)必须先于战术设计(模型) | 在错误的边界内做精细建模是浪费 |
| "Bounded Context = 数据库表的分组" | BC是语言边界和业务能力边界,不是数据分组 | 边界划分变成了DBA的工作 |
| "Context Map画完就不变了" | Context Map应该随组织和业务演进 | 架构与现实脱节 |
| "所有上下文关系都应该是Partnership" | Partnership需要极高的协调成本 | 团队被会议淹没 |
知识点详解
知识点1: Bounded Context的本质——语言边界
Eric Evans定义Bounded Context时,核心强调的是Ubiquitous Language的边界。同一个词在不同上下文中有不同含义:
经典例子:金融系统中的"Account"
| 上下文 | "Account"的含义 | 属性 |
|---|---|---|
| 身份认证(Identity) | 登录账户 | username, password, MFA |
| 银行账户(Banking) | 银行账号 | accountNumber, balance, currency |
| 会计(Accounting) | 会计科目 | ledgerCode, debit, credit |
| 客户关系(CRM) | 客户档案 | contactInfo, segmentation, riskLevel |
| 交易(Trading) | 交易账户 | positions, margin, PnL |
如果你在一个系统中用一个Account类同时表达这5种含义,代码会变成一个包含50个字段的"上帝类"(God Class),每个团队修改时都可能影响其他团队。
识别Bounded Context的三个启发式方法:
方法1: 语言边界测试
步骤:
1. 列出系统中所有核心名词(Account/Order/Product/Payment等)
2. 对每个名词,问:"在A团队和B团队的会议上,这个词是同一个意思吗?"
3. 如果不是,这两个团队可能属于不同的Bounded Context
方法2: 团队边界测试
步骤:
1. 画出组织结构图
2. 每个团队的日常工作范围通常暗示了一个Bounded Context
3. Conway定律: 系统结构往往映射组织结构
注意: 不是说组织结构决定BC,而是BC应该与组织结构对齐或者推动组织结构调整
方法3: 业务能力边界测试
步骤:
1. 列出核心业务能力(Business Capabilities)
2. 能独立完成一个完整业务功能的最小集合 = 一个BC
3. 如果拆掉这个BC,哪些业务流程会断?影响范围就是BC的边界
知识点2: Context Mapping 8种关系模式深度解析
Context Mapping定义了两个Bounded Context之间的协作关系。这8种模式不仅是技术关系,更是组织关系和权力关系的映射。
模式1: Partnership (合作关系)
定义: 两个团队成败与共,共同规划接口演进,双方地位平等。
适用场景:
- 两个团队在同一个项目组,有共同的交付目标
- 接口变更需要双方同步修改
- 例:支付团队和订单团队在同一个产品线
组织政治影响:
- 需要频繁的跨团队会议(至少周会)
- 任何一方的延期都会影响另一方
- 适合相邻的、目标高度一致的团队
代码实现:
// 双方共同维护的接口定义(共享仓库或API spec)
// order-service 和 payment-service 共同维护
interface PaymentRequest {
orderId: string;
amount: Money;
currency: Currency;
idempotencyKey: string;
}
// 变更流程: 双方代码审查 → 同时发布
代价: 协调成本高,不适合超过2-3个团队的关系。
模式2: Shared Kernel (共享内核)
定义: 两个上下文共享一小部分模型代码(通常是值对象或基础类型),双方共同维护。
适用场景:
- 有一些核心领域概念两边都需要且含义完全一致
- 例:
Money值对象、Currency枚举在多个上下文中通用
组织政治影响:
- 共享代码的变更需要双方同意(类似开源项目的PR流程)
- 共享部分必须非常小且稳定
代码实现:
// shared-kernel 包(独立仓库)
// money.ts
export class Money {
constructor(
readonly amount: bigint, // 用最小单位避免精度问题
readonly currency: Currency
) {}
add(other: Money): Money {
if (this.currency !== other.currency) {
throw new CurrencyMismatchError();
}
return new Money(this.amount + other.amount, this.currency);
}
}
// 双方作为依赖引入:
// import { Money } from '@company/shared-kernel';
代价: 共享内核必须极其稳定,否则变更波及面大。经验法则——共享内核不超过总代码的5%。
模式3: Customer-Supplier (客户-供应商)
定义: 上游(Supplier)提供服务,下游(Customer)消费服务。上游有主导权但需要考虑下游需求。
适用场景:
- 上游团队是平台/基础服务,下游是业务团队
- 上游有义务响应下游的合理需求
- 例:用户中心(上游) → 订单服务(下游)
组织政治影响:
- 关键问题:上游的产品优先级是否包含下游的需求?
- 如果上游不重视下游需求,关系会退化为Conformist
- 需要管理层确保上游对下游的服务承诺
代码实现:
// 上游(用户中心)提供API,下游(订单服务)调用
// 上游维护API契约,下游可以提出需求
// 上游通过Consumer-Driven Contract Testing保障兼容性
// 上游API
GET /api/users/{userId}
Response: { id, name, email, tier }
// 下游可以提需求: "我需要user的tier字段"
// 上游评估后添加
模式4: Conformist (跟随者)
定义: 下游无条件接受上游的模型,上游不关心下游的需求。
适用场景:
- 上游是第三方服务/外部API,你无法影响其设计
- 例:你的系统对接银行核心系统的API、对接链上智能合约
组织政治影响:
- 下游完全被动,上游的任何变更都可能破坏下游
- 在大公司中,核心系统团队对外围团队往往就是这种关系
代码实现:
// 直接使用上游的数据模型,不做转换
// 风险: 上游模型变更直接影响你的业务逻辑
// 银行API返回什么,你就用什么
interface BankAccountResponse {
acct_no: string; // 银行的命名风格
avail_bal: number; // 银行的缩写
ccy_cd: string; // 你不得不适应
}
// 你的代码直接使用 BankAccountResponse,不转换
警告: 这是最危险的模式之一。当你发现自己在Conformist模式中,应该认真考虑是否引入ACL。
模式5: Anti-Corruption Layer (防腐层,ACL)
定义: 在下游和上游之间建立一个翻译层,将上游的模型转换为下游自己的模型,防止上游的"腐化"渗透到下游。
适用场景:
- 上游模型很差(遗留系统)或与你的领域模型不一致
- 需要对接多个上游并统一模型
- 例:对接老核心系统、对接多条区块链
这是最重要的模式! 在10年金融软件经验中,80%的集成问题可以通过ACL解决。
代码实现:
// 上游(老核心系统)的丑陋模型
interface LegacyAccountDTO {
ACCT_NO: string;
AVAIL_BAL: string; // 字符串表示的金额(!)
CCY_CD: string;
ACCT_STS: string; // "A"=Active, "C"=Closed, "F"=Frozen
}
// ACL翻译层
class AccountAntiCorruptionLayer {
translate(legacy: LegacyAccountDTO): Account {
return new Account({
accountNumber: new AccountNumber(legacy.ACCT_NO),
balance: Money.fromString(legacy.AVAIL_BAL, legacy.CCY_CD),
status: this.translateStatus(legacy.ACCT_STS),
});
}
private translateStatus(code: string): AccountStatus {
const mapping: Record<string, AccountStatus> = {
'A': AccountStatus.Active,
'C': AccountStatus.Closed,
'F': AccountStatus.Frozen,
};
return mapping[code] ?? AccountStatus.Unknown;
}
}
// 你的领域模型保持干净,不受上游污染
class Account {
constructor(readonly props: {
accountNumber: AccountNumber;
balance: Money;
status: AccountStatus;
}) {}
}
模式6: Open Host Service (开放主机服务,OHS)
定义: 上游定义一套公开的、版本化的协议/API,供多个下游使用。
适用场景:
- 上游是平台服务,有多个消费者
- 需要标准化的API规范
- 例:API网关、公开的REST API
代码实现:
// OpenAPI规范定义的公开协议
// GET /api/v2/accounts/{accountId}
// 版本化 + 文档化 + 向后兼容承诺
模式7: Published Language (发布语言,PL)
定义: 使用行业标准的数据格式进行通信。
适用场景:
- 跨组织集成,使用行业标准协议
- 例:金融行业的FIX协议、ISO 20022、SWIFT报文
实例:
金融领域: FIX协议(交易)、SWIFT MT/MX(支付)、ISO 20022(报文)
Web3领域: ERC-20/721/1155标准、ABI编码、JSON-RPC
模式8: Separate Ways (各行其道)
定义: 两个上下文完全不集成,各自实现所需功能。
适用场景:
- 集成成本高于重复实现成本
- 两个上下文的需求重叠很小
- 例:每个微服务维护自己的用户缓存而非每次调用用户中心
何时选择:
集成成本 > 重复实现成本 + 数据一致性风险 → Separate Ways
反之 → 集成
知识点3: 上下文发现的系统方法
核心问题:如何在一个复杂系统中识别出所有的Bounded Context?
方法论:四步发现法
Step 1: 业务能力分解 (Business Capability Mapping)
└── 列出组织的核心业务能力
└── 每个业务能力可能是一个BC
Step 2: 语言分析 (Linguistic Analysis)
└── 收集各团队使用的术语表
└── 同一术语不同含义 → 不同BC
└── 不同术语同一含义 → 可能是同一BC
Step 3: 数据流分析 (Data Flow Analysis)
└── 追踪核心实体在系统中的流转
└── 实体的属性在哪里发生质变 → BC边界
Step 4: 变更频率分析 (Rate of Change Analysis)
└── 高频变更的模块和低频变更的模块应该分开
└── 不同的变更驱动力 → 不同的BC
对比分析
8种Context Mapping关系模式对比
| 模式 | 权力关系 | 协调成本 | 耦合度 | 适合场景 | 组织前提 |
|---|---|---|---|---|---|
| Partnership | 平等 | 最高 | 高 | 紧密协作的兄弟团队 | 共同OKR |
| Shared Kernel | 平等 | 高 | 中-高 | 少量共享概念 | 共同代码审查 |
| Customer-Supplier | 不等(上游主导) | 中 | 中 | 平台→业务 | 上游有服务意识 |
| Conformist | 不等(上游完全主导) | 低 | 高(单向) | 对接不可控的外部系统 | 无话语权 |
| ACL | 不等(下游自保) | 中 | 低 | 对接遗留系统/外部API | 下游有技术能力 |
| OHS | 不等(上游提供标准) | 低-中 | 低 | 多消费者的平台服务 | 上游有规范能力 |
| Published Language | 平等(遵循标准) | 低 | 低 | 跨组织集成 | 行业标准存在 |
| Separate Ways | 无 | 无 | 无 | 集成成本>收益 | - |
常见组合模式
| 场景 | 推荐组合 |
|---|---|
| 对接银行核心 | Conformist + ACL(翻译层保护自己) |
| 平台服务暴露API | OHS + Published Language(标准协议) |
| 新旧系统共存 | ACL(隔离遗留) + Customer-Supplier(新系统间) |
| 微服务间通信 | Customer-Supplier(大多数) + Partnership(紧耦合的少数) |
架构设计实操
实操: 为"全渠道零售银行"做完整上下文映射
业务背景: 一家零售银行,有以下业务能力:
- 个人银行账户(开户/销户/冻结)
- 存贷款(活期/定期/贷款)
- 支付转账(行内转账/跨行转账/国际汇款)
- 信用卡(申请/授信/账单/还款)
- 投资理财(基金/保险/理财产品)
- 风险管理(KYC/AML/信用评分)
- 客户关系(客户360视图/营销/投诉)
- 渠道(手机银行/网银/柜台/ATM)
Step 1: 识别Bounded Context
1. Identity & Access (身份认证)
└── 登录/认证/授权/会话管理
2. Customer (客户管理)
└── 客户基本信息/KYC/客户分层
3. Account (账户管理)
└── 开户/销户/账户状态/余额
4. Deposit (存款)
└── 活期/定期/利息计算
5. Lending (贷款)
└── 贷款申请/审批/放款/还款/逾期
6. Payment (支付)
└── 行内转账/跨行转账/国际汇款
7. Card (信用卡)
└── 申请/授信/交易/账单/还款
8. Investment (投资理财)
└── 基金/保险/理财产品销售
9. Risk (风险管理)
└── AML/反欺诈/信用评分
10. Channel (渠道层)
└── 手机银行/网银/柜台/API网关
11. Notification (通知)
└── 短信/Push/邮件/站内信
12. Ledger (总账)
└── 复式记账/对账/报表
Step 2: 绘制Context Map
Context Map:
[Channel] ──(OHS)──→ [Identity]
│ │
│(OHS) (Customer-Supplier)
│ │
├──────────→ [Customer] ←──(ACL)── [外部征信系统]
│ │
│ (Customer-Supplier)
│ │
├──→ [Account] ←─┘
│ │
│ (Shared Kernel: Money)
│ │
│ ┌───┼───┬───────┐
│ │ │ │ │
│ ▼ ▼ ▼ ▼
│ [Deposit][Lending][Card] [Investment]
│ │ │ │ │
│ │ (Customer-Supplier)│
│ │ │ │ │
│ └───┬───┘ └───┬───┘
│ │ │
│ ▼ │
│ [Payment] │
│ │ │
│ (Partnership) │
│ │ │
│ ▼ │
│ [Ledger] ←──────┘
│
└──→ [Notification] (Separate Ways - 独立的通知服务)
[Risk] ──(ACL)──→ [外部AML数据库]
│
(Customer-Supplier, 下游)
│
├── [Lending] (上游提供贷款数据给Risk)
├── [Card] (上游提供信用卡数据给Risk)
└── [Payment] (上游提供交易数据给Risk)
Step 3: 每对关系的详细说明
| 上游 | 下游 | 关系类型 | 理由 |
|---|---|---|---|
| Identity | Channel | OHS | 多渠道接入,需要标准化认证协议 |
| Customer | Account/Lending/Card | Customer-Supplier | 客户中心提供基础数据,业务线消费 |
| 外部征信 | Customer | ACL | 外部系统格式不可控,需要翻译层 |
| Account & Deposit & Lending & Card | (共享) | Shared Kernel(Money) | Money值对象是跨上下文的通用概念 |
| Payment | Ledger | Partnership | 支付和记账必须紧密协调,一致性要求极高 |
| Lending/Card/Payment | Risk | Customer-Supplier(反向) | 业务线提供数据给风控消费 |
| Notification | 所有 | Separate Ways | 通知是独立能力,不与业务逻辑耦合 |
| 外部AML数据库 | Risk | ACL | 外部数据格式和更新频率不可控 |
Step 4: 组织结构影响分析
根据Context Map,建议的团队拓扑:
平台团队(Stream-aligned):
├── Identity Team (2-3人)
├── Customer Team (3-4人)
├── Notification Team (2人)
业务团队(Stream-aligned):
├── Account & Deposit Team (4-5人)
├── Lending Team (5-6人)
├── Card Team (4-5人)
├── Payment & Ledger Team (5-6人) ← Partnership关系决定了这两个BC适合同一团队
├── Investment Team (3-4人)
支撑团队(Enabling):
├── Risk & Compliance Team (4-5人)
├── Channel/Platform Engineering Team (3-4人)
ADR
# ADR-002: Payment与Ledger的Context Mapping关系选择
## 状态: 已接受
## 上下文
支付(Payment)和总账(Ledger)之间的关系需要确定。支付的每笔交易都需要
在总账中记录借贷分录。两者的数据一致性要求是金融级(不允许差错)。
## 考虑的关系模式
1. Customer-Supplier: Payment作为上游,Ledger作为下游
2. Partnership: 双方平等协作
3. Shared Kernel: 共享交易模型
## 决策
选择Partnership。
## 理由
- 支付和记账的一致性要求极高,任何接口变更都需要双方同步
- 两个领域的变更频率相似(都与业务规则变化相关)
- Customer-Supplier模式中上游可能不够重视下游需求,金融场景不可接受
- Shared Kernel范围太大(不止Money,还有Transaction Event),维护成本高
- Partnership的高协调成本在"金融一致性"面前是值得的
## 后果
- Payment和Ledger团队需要每周联合规划会
- 接口变更采用双方代码审查制度
- 建议两个团队坐在一起(物理接近降低协调成本)
AI增强实践
AI在DDD战略设计中的应用
1. 领域术语分析
Prompt: "以下是我们系统中不同团队使用的术语列表:
- 销售团队: 客户、订单、报价、合同
- 仓储团队: 订单、发货单、库存、SKU
- 财务团队: 订单、发票、收款、账目
请分析:
1. 哪些术语在不同团队中含义不同(候选的BC边界)?
2. 建议划分哪些Bounded Context?
3. 各BC之间可能的关系模式?"
2. Context Map审查
Prompt: "我画了一个Context Map,包含以下BC和关系:
[列出你的BC和关系]
请审查:
1. 是否有遗漏的BC?
2. 关系模式是否合理? 有没有该用ACL的地方用了Conformist?
3. 是否有循环依赖?
4. 团队拓扑建议?"
3. 用AI模拟业务专家
当缺乏领域专家参与时,可以让AI扮演某个领域的业务专家,进行启发式讨论。但要注意——AI的领域知识是泛化的,不能替代真正的业务专家。AI能帮你准备问题清单和初步假设,但最终验证必须由真实的业务人员完成。
AI vs 人工边界:
| 任务 | AI能力 | 人工不可替代 |
|---|---|---|
| 术语分析和边界假设 | 优秀 | 真实的语言使用差异需要观察 |
| Context Map的逻辑检查 | 良好 | 组织政治和权力关系 |
| 生成关系模式建议 | 良好 | 团队文化和协作意愿的判断 |
| Event Storming引导 | 有限 | 现场引导和氛围管理 |
与Web3/DeFi的关联
| 传统架构概念 | Web3对应 | 关键差异 |
|---|---|---|
| Bounded Context | 独立的智能合约/协议 | Web3天然有"合约边界",但不等于好的BC |
| Context Mapping | 合约间调用关系(Composability) | DeFi乐高本质上就是Contract Map |
| ACL(防腐层) | 协议适配层(如DEX聚合器) | 1inch等聚合器就是多DEX的ACL |
| Shared Kernel | ERC标准(ERC-20/721) | 行业标准=最成功的Shared Kernel |
| Published Language | ABI/JSON-RPC | 链上的Published Language是强制的 |
| Customer-Supplier | 协议依赖(如Aave依赖Chainlink预言机) | 上游(Chainlink)出问题,下游(Aave)受影响 |
| Conformist | 无法修改的链上合约 | 已部署的合约你只能遵从其接口 |
| Separate Ways | 协议Fork | 如果上游不满足需求就Fork |
Web3中的Context Mapping实例:
[Uniswap V3]
│
├── (Conformist) → [Chainlink Price Feed]
│ 原因: Uniswap无法改变Chainlink的接口
│
├── (OHS) → [各DeFi协议/前端/聚合器]
│ 原因: Uniswap提供标准化的Router接口
│
├── (Published Language) → [ERC-20 Token标准]
│ 原因: 所有token遵循ERC-20
│
└── (Separate Ways) ← [SushiSwap]
原因: Sushi Fork了Uniswap,各行其道
今日思考
-
你经历过的系统中,最痛苦的跨团队集成是什么? 用今天学的8种模式重新审视,那次集成属于什么模式?如果重新来过,你会选择什么模式?ACL是否能解决当时的问题?
-
Conway定律是诅咒还是工具? 如果你的组织结构不合理(比如一个团队横跨了3个本应独立的BC),你会选择改变组织结构还是改变系统边界?在你的公司里,哪个更现实?
-
DeFi的Composability是Context Mapping的极致形态吗? 在DeFi中,任何协议都可以被任何其他协议调用,这种极端的开放性带来了创新(DeFi乐高),但也带来了风险(级联清算)。从DDD视角看,DeFi缺少的是什么?
面试题准备
Q1: 如何识别限界上下文边界?
30秒版本: 我用三个启发式方法:语言边界(同一个词在不同团队含义不同)、团队边界(符合Conway定律)、变更频率边界(变更原因不同的模块应该分开)。最终验证是:拆掉这个上下文后,哪些业务流程会断?
2分钟版本:
识别BC边界是DDD战略设计的核心挑战。我的方法论:
第一,语言分析。这是Eric Evans最强调的方法。我会收集各团队的领域术语表,找出那些"同一个词但含义不同"的地方。比如在金融系统中,"账户"在认证、银行、会计三个领域的含义完全不同——这就是三个BC。
第二,团队边界。Conway定律告诉我们系统结构反映组织结构。我会对照组织架构图来校验我的BC划分。如果一个BC横跨了两个独立团队,通常意味着要么BC需要拆分,要么组织需要调整。
第三,变更驱动力分析。两个模块如果由不同的原因驱动变更(一个由监管变化驱动,一个由用户需求驱动),它们应该是不同的BC。这也是单一职责原则在战略层面的体现。
第四,验证。BC的边界不是一次画对的,需要持续验证。如果发现一个需求频繁需要同时修改多个BC,说明边界可能画错了。
追问准备:
追问: "组织政治如何影响上下文关系?" 回答: 在实际工作中,Context Mapping的关系模式很大程度上反映了组织的权力结构。比如核心系统团队对外围团队往往是Conformist关系——不是技术选择而是政治现实。好的架构师会识别这种关系并通过ACL保护自己的团队,而不是试图改变权力结构。
Q2: 什么时候应该用ACL(防腐层)?
30秒版本: 当你对接的上游系统模型质量差(遗留系统)、不可控(第三方API)、或者与你的领域模型差异大时,就应该用ACL。ACL的本质是"用翻译层保护你的领域模型的纯净性"。在我的经验中,对接遗留金融核心系统时ACL几乎是必须的。
2分钟版本:
ACL是DDD中我使用频率最高的模式。三种必须用的场景:
第一,对接遗留系统。老核心系统的数据模型往往是20年前设计的,字段命名混乱、类型不规范、语义不清。如果不加ACL,这些"腐化"会渗透到你的新系统中。
第二,对接不可控的外部API。第三方API的变更你无法控制,ACL作为缓冲层,当上游变化时只需要修改翻译层。
第三,模型差异大。当上游的"Account"和你的"Account"含义完全不同时,直接映射会导致代码中到处是转换逻辑。ACL把所有转换集中在一处。
ACL的实现要点:
- 放在独立的模块/包中
- 只做翻译,不加业务逻辑
- 上游模型变化只影响ACL层,不影响下游业务代码
追问准备:
追问: "ACL的缺点是什么?" 回答: 主要是维护成本和性能开销。每次上游变更都需要同步更新ACL。对于高吞吐场景,额外的翻译层会增加延迟。所以如果上游模型和你的模型差异很小,用ACL反而是过度设计。
学习资源
- 《Domain-Driven Design》 - Eric Evans - Chapter 14: Context Map (战略设计的核心章节)
- 《Implementing Domain-Driven Design》 - Vaughn Vernon - Part 1: Strategic Design (更实操的指南)
- 《Team Topologies》 - Matthew Skelton & Manuel Pais - 组织结构与架构的关系
- Context Mapping Patterns - DDD Crew GitHub: https://github.com/ddd-crew/context-mapping
- Bounded Context Canvas - DDD Crew 的BC可视化工具
- Nick Tune的Strategic Domain-Driven Design - InfoQ演讲
明日预告
Day 10: DDD的争议与取舍 —— DDD不是银弹。我们将直面DDD的争议:聚合根边界画错了怎么办?Event Sourcing什么时候是灾难?贫血模型真的那么差吗?并分析3个真实的DDD实施失败案例,建立"何时不该用DDD"的判断框架。