Appearance
领域驱动设计(Domain-Driven Design)
概述
领域驱动设计(Domain-Driven Design,简称DDD)是由Eric Evans在2003年提出的一种软件设计方法论。DDD强调将复杂的业务领域作为软件设计的核心,通过深入理解业务领域,建立准确的领域模型,并将这个模型作为软件设计和实现的基础。
DDD不仅仅是一种技术方法,更是一种思维方式和协作模式。它强调领域专家与开发团队的紧密合作,通过统一的语言(Ubiquitous Language)来确保业务需求与技术实现的一致性。
架构图
DDD整体架构
┌─────────────────────────────────────────────────────────────────────────────┐
│ 领域驱动设计架构 │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ 用户界面层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Web UI │ │ Mobile UI │ │ API │ │ Console │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 应用服务层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 应用服务 │ │ 命令处理器 │ │ 查询处理器 │ │ 工作流协调 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 领域层 │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 限界上下文 │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 聚合根 │ │ 实体 │ │ 值对象 │ │ 领域服务 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 领域事件 │ │ 工厂 │ │ 规约 │ │ 策略 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 基础设施层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 仓储实现 │ │ 消息队列 │ │ 外部API │ │ 文件系统 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 数据库 │ │ 缓存 │ │ 日志 │ │ 配置 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
限界上下文映射
┌─────────────────────────────────────────────────────────────────────────────┐
│ 限界上下文映射 │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────┐ 共享内核 ┌─────────────────┐
│ 订单上下文 │◄──────────────▶│ 支付上下文 │
│ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ 订单 │ │ │ │ 支付 │ │
│ │ 订单项 │ │ │ │ 账单 │ │
│ │ 客户 │ │ │ │ 交易 │ │
│ └─────────────┘ │ │ └─────────────┘ │
└─────────────────┘ └─────────────────┘
│ │
│ 客户-供应商 │ 防腐层
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 库存上下文 │ │ 用户上下文 │
│ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ 产品 │ │ │ │ 用户 │ │
│ │ 库存 │ │ │ │ 角色 │ │
│ │ 仓库 │ │ │ │ 权限 │ │
│ └─────────────┘ │ │ └─────────────┘ │
└─────────────────┘ └─────────────────┘
│ │
│ 开放主机服务 │ 遵循者
▼ ▼
┌─────────────────┐ 发布语言 ┌─────────────────┐
│ 物流上下文 │◄──────────────▶│ 报表上下文 │
│ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ 运输 │ │ │ │ 报表 │ │
│ │ 配送 │ │ │ │ 统计 │ │
│ │ 跟踪 │ │ │ │ 分析 │ │
│ └─────────────┘ │ │ └─────────────┘ │
└─────────────────┘ └─────────────────┘
聚合设计模式
┌─────────────────────────────────────────────────────────────────────────────┐
│ 订单聚合 │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────┐
│ 订单聚合根 │ ◄─── 聚合根
│ │
│ - 订单ID │
│ - 客户ID │
│ - 订单状态 │
│ - 创建时间 │
│ - 总金额 │
│ │
│ + 添加订单项 │
│ + 移除订单项 │
│ + 确认订单 │
│ + 取消订单 │
└─────┬───────────┘
│ 包含
▼
┌─────────────────┐
│ 订单项 │ ◄─── 实体
│ │
│ - 订单项ID │
│ - 产品ID │
│ - 产品名称 │
│ - 数量 │
│ - 单价 │
│ - 小计 │
│ │
│ + 更新数量 │
│ + 计算小计 │
└─────┬───────────┘
│ 包含
▼
┌─────────────────┐
│ 金额 │ ◄─── 值对象
│ │
│ - 数值 │
│ - 货币 │
│ │
│ + 加法 │
│ + 减法 │
│ + 比较 │
└─────────────────┘
聚合边界规则:
1. 聚合根是唯一的入口点
2. 外部只能通过聚合根访问聚合内部
3. 聚合内部保证一致性
4. 聚合之间通过ID引用
核心概念
1. 统一语言(Ubiquitous Language)
定义: 统一语言是领域专家和开发团队共同使用的语言,它准确地反映了业务领域的概念和规则。
特征:
- 共同理解:所有团队成员使用相同的术语
- 业务导向:术语来源于业务领域而非技术实现
- 持续演进:随着对领域理解的深入而不断完善
- 无歧义性:每个术语都有明确的含义
实践方法:
术语词汇表
┌─────────────────────────────────────────────────────────────┐
│ 电商领域术语词汇表 │
├─────────────────────────────────────────────────────────────┤
│ 术语 │ 定义 │
├─────────────────────────────────────────────────────────────┤
│ 订单 │ 客户购买商品的正式请求 │
│ 购物车 │ 客户临时存放待购买商品的容器 │
│ 库存 │ 可供销售的商品数量 │
│ 支付 │ 客户为订单付款的过程 │
│ 发货 │ 将订单商品从仓库发送给客户的过程 │
│ 退款 │ 将已支付金额返还给客户的过程 │
└─────────────────────────────────────────────────────────────┘
领域模型对话
业务专家: "当客户下单时,我们需要检查库存是否充足"
开发人员: "你是说当Order被创建时,需要验证Inventory的可用数量?"
业务专家: "是的,如果库存不足,订单应该被拒绝"
开发人员: "明白,我们在Order.create()方法中添加库存检查逻辑"
2. 限界上下文(Bounded Context)
定义: 限界上下文是一个明确的边界,在这个边界内,特定的领域模型是有效和一致的。
特征:
- 明确边界:清晰定义上下文的范围
- 模型一致性:上下文内模型保持一致
- 语言统一:上下文内使用统一语言
- 自治性:上下文可以独立演进
识别方法:
业务能力分析
电商系统限界上下文识别:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 订单管理 │ │ 库存管理 │ │ 支付处理 │
│ │ │ │ │ │
│ - 订单创建 │ │ - 库存查询 │ │ - 支付处理 │
│ - 订单修改 │ │ - 库存预留 │ │ - 退款处理 │
│ - 订单取消 │ │ - 库存扣减 │ │ - 账单生成 │
│ - 订单查询 │ │ - 补货管理 │ │ - 对账管理 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 用户管理 │ │ 商品管理 │ │ 物流管理 │
│ │ │ │ │ │
│ - 用户注册 │ │ - 商品录入 │ │ - 配送安排 │
│ - 用户认证 │ │ - 商品分类 │ │ - 物流跟踪 │
│ - 权限管理 │ │ - 价格管理 │ │ - 签收确认 │
│ - 个人信息 │ │ - 促销活动 │ │ - 异常处理 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
数据一致性需求
强一致性需求 (同一上下文):
- 订单与订单项
- 用户与用户权限
- 商品与商品库存
最终一致性需求 (跨上下文):
- 订单与库存
- 订单与支付
- 支付与物流
3. 聚合(Aggregate)
定义: 聚合是一组相关对象的集合,作为数据修改的单元,由聚合根统一管理。
设计原则:
- 一致性边界:聚合内保证强一致性
- 事务边界:一个事务只能修改一个聚合
- 引用规则:聚合间通过ID引用,不直接引用对象
- 大小适中:聚合不宜过大,影响性能
聚合根职责:
┌─────────────────────────────────────────┐
│ 聚合根职责 │
├─────────────────────────────────────────┤
│ 1. 控制访问入口 │
│ - 外部只能通过聚合根访问聚合内部 │
│ - 封装聚合内部的复杂性 │
│ │
│ 2. 维护业务不变性 │
│ - 确保聚合内部状态的一致性 │
│ - 执行业务规则和约束 │
│ │
│ 3. 发布领域事件 │
│ - 在状态变化时发布相关事件 │
│ - 通知其他聚合或上下文 │
│ │
│ 4. 管理生命周期 │
│ - 控制聚合的创建和销毁 │
│ - 管理聚合内实体的生命周期 │
└─────────────────────────────────────────┘
4. 实体(Entity)
定义: 实体是具有唯一标识的对象,其标识在整个生命周期中保持不变。
特征:
- 唯一标识:具有全局唯一的标识符
- 可变性:属性可以改变,但标识不变
- 生命周期:有明确的创建、修改、销毁过程
- 业务意义:标识具有业务含义
实体设计:
┌─────────────────────────────────────────┐
│ 用户实体 │
├─────────────────────────────────────────┤
│ 标识: │
│ - userId: UserId (值对象) │
│ │
│ 属性: │
│ - email: Email (值对象) │
│ - name: PersonName (值对象) │
│ - registrationDate: Date │
│ - lastLoginTime: DateTime │
│ - status: UserStatus (枚举) │
│ │
│ 行为: │
│ + changeEmail(newEmail: Email) │
│ + updateProfile(name: PersonName) │
│ + activate() │
│ + deactivate() │
│ + recordLogin() │
└─────────────────────────────────────────┘
5. 值对象(Value Object)
定义: 值对象是没有标识的对象,通过其属性值来区分,具有不可变性。
特征:
- 无标识:没有唯一标识符
- 不可变:创建后不能修改
- 值相等:通过值比较相等性
- 可替换:可以用相同值的对象替换
值对象设计:
┌─────────────────────────────────────────┐
│ 金额值对象 │
├─────────────────────────────────────────┤
│ 属性: │
│ - amount: BigDecimal │
│ - currency: Currency │
│ │
│ 约束: │
│ - amount >= 0 │
│ - currency 不能为空 │
│ │
│ 行为: │
│ + add(other: Money): Money │
│ + subtract(other: Money): Money │
│ + multiply(factor: BigDecimal): Money │
│ + equals(other: Money): boolean │
│ + compareTo(other: Money): int │
│ │
│ 不变性: │
│ - 所有操作返回新的Money对象 │
│ - 不提供修改内部状态的方法 │
└─────────────────────────────────────────┘
6. 领域服务(Domain Service)
定义: 领域服务封装了不自然属于实体或值对象的业务逻辑。
使用场景:
- 跨聚合操作:需要协调多个聚合的业务逻辑
- 复杂计算:复杂的业务计算逻辑
- 外部依赖:需要访问外部系统的业务逻辑
- 策略模式:可变的业务规则
服务设计:
┌─────────────────────────────────────────┐
│ 价格计算服务 │
├─────────────────────────────────────────┤
│ 职责: │
│ - 计算订单总价 │
│ - 应用折扣规则 │
│ - 计算税费 │
│ - 处理促销活动 │
│ │
│ 方法: │
│ + calculateOrderTotal( │
│ order: Order, │
│ customer: Customer, │
│ promotions: List<Promotion> │
│ ): Money │
│ │
│ + applyDiscount( │
│ amount: Money, │
│ discount: Discount │
│ ): Money │
│ │
│ + calculateTax( │
│ amount: Money, │
│ taxRate: TaxRate │
│ ): Money │
└─────────────────────────────────────────┘
7. 领域事件(Domain Event)
定义: 领域事件表示领域中发生的重要业务事件,用于解耦聚合间的依赖。
特征:
- 业务意义:表示重要的业务事件
- 不可变:事件一旦发生不能修改
- 时序性:有明确的发生时间
- 异步处理:通常异步处理
事件设计:
┌─────────────────────────────────────────┐
│ 订单已创建事件 │
├─────────────────────────────────────────┤
│ 事件信息: │
│ - eventId: EventId │
│ - occurredOn: DateTime │
│ - version: int │
│ │
│ 业务数据: │
│ - orderId: OrderId │
│ - customerId: CustomerId │
│ - orderItems: List<OrderItem> │
│ - totalAmount: Money │
│ - orderDate: DateTime │
│ │
│ 元数据: │
│ - correlationId: String │
│ - causationId: String │
│ - userId: UserId │
└─────────────────────────────────────────┘
事件处理流程:
订单创建 ──▶ 发布事件 ──▶ 库存预留 ──▶ 发送确认邮件
│ │ │ │
▼ ▼ ▼ ▼
聚合根 事件总线 库存服务 通知服务
战略设计
1. 上下文映射(Context Mapping)
映射关系类型:
共享内核(Shared Kernel)
特点:
- 两个上下文共享部分模型
- 需要密切协调变更
- 适用于紧密相关的团队
示例:
订单上下文 ←→ 支付上下文
共享: 客户信息、金额类型
客户-供应商(Customer-Supplier)
特点:
- 上游团队为下游团队提供服务
- 下游团队依赖上游团队
- 需要协商接口变更
示例:
库存上下文 (供应商) ──▶ 订单上下文 (客户)
接口: 库存查询、库存预留
遵循者(Conformist)
特点:
- 下游完全遵循上游模型
- 无法影响上游设计
- 适用于外部系统集成
示例:
报表上下文 ──▶ 第三方分析系统
遵循: 第三方系统的数据格式
防腐层(Anti-Corruption Layer)
特点:
- 保护本地模型不受外部影响
- 转换外部模型到本地模型
- 适用于遗留系统集成
示例:
新订单系统 ←─ 防腐层 ←─ 遗留ERP系统
转换: ERP数据格式 → 领域模型
开放主机服务(Open Host Service)
特点:
- 提供标准化的服务接口
- 多个客户端可以使用
- 定义发布语言
示例:
用户服务 ──▶ 标准用户API
客户端: 订单服务、支付服务、物流服务
发布语言(Published Language)
特点:
- 定义标准的交换格式
- 文档化的接口规范
- 版本化管理
示例:
XML Schema、JSON Schema、Protocol Buffers
2. 上下文集成模式
数据库集成
┌─────────────┐ ┌─────────────┐
│ 上下文A │ │ 上下文B │
└─────┬───────┘ └─────┬───────┘
│ │
▼ ▼
┌─────────────────────────────┐
│ 共享数据库 │
└─────────────────────────────┘
优点: 实现简单、数据一致性
缺点: 紧耦合、难以独立演进
文件传输集成
┌─────────────┐ 文件 ┌─────────────┐
│ 上下文A │ ────▶ │ 上下文B │
│ │ │ │
│ 生成文件 │ │ 处理文件 │
└─────────────┘ └─────────────┘
优点: 松耦合、批量处理
缺点: 延迟高、错误处理复杂
远程过程调用
┌─────────────┐ RPC ┌─────────────┐
│ 上下文A │ ──────▶ │ 上下文B │
│ │ ◄────── │ │
│ 调用服务 │ 响应 │ 提供服务 │
└─────────────┘ └─────────────┘
优点: 实时交互、类型安全
缺点: 紧耦合、可用性依赖
消息传递
┌─────────────┐ 消息 ┌─────────────┐ 消息 ┌─────────────┐
│ 上下文A │ ────▶ │ 消息中间件 │ ────▶ │ 上下文B │
│ │ │ │ │ │
│ 发布消息 │ │ 路由转发 │ │ 订阅消息 │
└─────────────┘ └─────────────┘ └─────────────┘
优点: 松耦合、异步处理、可扩展
缺点: 复杂性增加、最终一致性
战术设计
1. 聚合设计模式
聚合大小原则
小聚合原则:
✓ 一个聚合根 + 少量实体/值对象
✓ 单一事务边界
✓ 高内聚、低耦合
✓ 性能考虑
大聚合问题:
✗ 并发冲突增加
✗ 内存占用过大
✗ 加载性能下降
✗ 事务复杂度增加
聚合引用规则
内部引用:
聚合根 ──▶ 实体 (直接引用)
实体 ──▶ 值对象 (直接引用)
外部引用:
聚合A ──▶ 聚合B (通过ID引用)
示例:
class Order {
private OrderId id; // 自身ID
private CustomerId customerId; // 外部聚合ID引用
private List<OrderItem> items; // 内部实体直接引用
private ShippingAddress address; // 值对象直接引用
}
聚合一致性保证
强一致性 (聚合内):
- 同一事务内修改
- 立即验证业务规则
- 原子性操作
最终一致性 (聚合间):
- 通过领域事件
- 异步处理
- 补偿机制
示例:
// 聚合内强一致性
order.addItem(productId, quantity); // 立即验证库存
order.calculateTotal(); // 立即计算总价
// 聚合间最终一致性
orderCreated.publish(); // 发布事件
// 异步处理: 库存扣减、发送邮件等
2. 仓储模式(Repository Pattern)
定义: 仓储提供了一个类似集合的接口来访问聚合,封装了数据访问的复杂性。
设计原则:
┌─────────────────────────────────────────┐
│ 仓储设计原则 │
├─────────────────────────────────────────┤
│ 1. 每个聚合根一个仓储 │
│ - OrderRepository for Order │
│ - CustomerRepository for Customer │
│ │
│ 2. 仓储接口在领域层定义 │
│ - 避免基础设施依赖 │
│ - 支持测试和模拟 │
│ │
│ 3. 仓储实现在基础设施层 │
│ - 具体的数据访问技术 │
│ - ORM映射和查询实现 │
│ │
│ 4. 提供集合式接口 │
│ - add(), remove(), findById() │
│ - 隐藏查询复杂性 │
└─────────────────────────────────────────┘
仓储接口设计:
┌─────────────────────────────────────────┐
│ 订单仓储接口 │
├─────────────────────────────────────────┤
│ interface OrderRepository { │
│ │
│ // 基本操作 │
│ void add(Order order); │
│ void remove(Order order); │
│ Order findById(OrderId id); │
│ │
│ // 业务查询 │
│ List<Order> findByCustomerId( │
│ CustomerId customerId); │
│ List<Order> findByStatus( │
│ OrderStatus status); │
│ List<Order> findByDateRange( │
│ Date from, Date to); │
│ │
│ // 规约模式 │
│ List<Order> findBySpecification( │
│ Specification<Order> spec); │
│ } │
└─────────────────────────────────────────┘
3. 工厂模式(Factory Pattern)
使用场景:
- 复杂的对象创建逻辑
- 需要验证创建参数
- 多种创建方式
- 封装创建细节
工厂设计:
┌─────────────────────────────────────────┐
│ 订单工厂 │
├─────────────────────────────────────────┤
│ class OrderFactory { │
│ │
│ public Order createOrder( │
│ CustomerId customerId, │
│ List<OrderItemData> items, │
│ ShippingAddress address │
│ ) { │
│ // 验证参数 │
│ validateCustomer(customerId); │
│ validateItems(items); │
│ validateAddress(address); │
│ │
│ // 创建订单 │
│ OrderId orderId = generateId(); │
│ Order order = new Order( │
│ orderId, customerId, address); │
│ │
│ // 添加订单项 │
│ for (OrderItemData item : items) { │
│ order.addItem( │
│ item.productId, │
│ item.quantity, │
│ item.price); │
│ } │
│ │
│ return order; │
│ } │
│ } │
└─────────────────────────────────────────┘
4. 规约模式(Specification Pattern)
定义: 规约模式将业务规则封装为可组合的对象,支持复杂的查询和验证逻辑。
规约设计:
┌─────────────────────────────────────────┐
│ 规约接口 │
├─────────────────────────────────────────┤
│ interface Specification<T> { │
│ boolean isSatisfiedBy(T candidate); │
│ │
│ Specification<T> and( │
│ Specification<T> other); │
│ Specification<T> or( │
│ Specification<T> other); │
│ Specification<T> not(); │
│ } │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 具体规约实现 │
├─────────────────────────────────────────┤
│ class HighValueOrderSpec │
│ implements Specification<Order> { │
│ │
│ private Money threshold; │
│ │
│ public boolean isSatisfiedBy( │
│ Order order) { │
│ return order.getTotal() │
│ .isGreaterThan(threshold); │
│ } │
│ } │
│ │
│ class RecentOrderSpec │
│ implements Specification<Order> { │
│ │
│ private int days; │
│ │
│ public boolean isSatisfiedBy( │
│ Order order) { │
│ return order.getOrderDate() │
│ .isAfter( │
│ LocalDate.now().minusDays(days)│
│ ); │
│ } │
│ } │
└─────────────────────────────────────────┘
使用示例:
Specification<Order> highValueRecent =
new HighValueOrderSpec(Money.of(1000))
.and(new RecentOrderSpec(30));
List<Order> orders = orderRepository
.findBySpecification(highValueRecent);
分层架构
1. 用户界面层(User Interface Layer)
职责:
- 处理用户交互
- 展示数据
- 输入验证
- 会话管理
组件:
┌─────────────────────────────────────────┐
│ 用户界面层组件 │
├─────────────────────────────────────────┤
│ Controllers/Handlers: │
│ - 处理HTTP请求 │
│ - 参数验证和转换 │
│ - 调用应用服务 │
│ - 返回响应 │
│ │
│ View Models/DTOs: │
│ - 数据传输对象 │
│ - 视图模型 │
│ - 序列化/反序列化 │
│ │
│ Validators: │
│ - 输入验证 │
│ - 格式检查 │
│ - 安全验证 │
└─────────────────────────────────────────┘
2. 应用服务层(Application Service Layer)
职责:
- 协调领域对象
- 事务管理
- 安全检查
- 工作流控制
应用服务设计:
┌─────────────────────────────────────────┐
│ 订单应用服务 │
├─────────────────────────────────────────┤
│ @Service │
│ class OrderApplicationService { │
│ │
│ @Transactional │
│ public OrderId createOrder( │
│ CreateOrderCommand command) { │
│ │
│ // 1. 验证权限 │
│ securityService.checkPermission( │
│ command.getUserId(), │
│ "CREATE_ORDER"); │
│ │
│ // 2. 获取领域对象 │
│ Customer customer = customerRepo │
│ .findById(command.getCustomerId())│
│ Product product = productRepo │
│ .findById(command.getProductId())│
│ │
│ // 3. 执行领域逻辑 │
│ Order order = orderFactory │
│ .createOrder(customer, product, │
│ command.getQuantity()); │
│ │
│ // 4. 持久化 │
│ orderRepository.add(order); │
│ │
│ // 5. 发布事件 │
│ eventPublisher.publish( │
│ new OrderCreatedEvent(order)); │
│ │
│ return order.getId(); │
│ } │
│ } │
└─────────────────────────────────────────┘
3. 领域层(Domain Layer)
职责:
- 业务逻辑实现
- 业务规则验证
- 领域模型定义
- 领域事件发布
层次结构:
领域层
├── 聚合
│ ├── 聚合根
│ ├── 实体
│ └── 值对象
├── 领域服务
├── 领域事件
├── 仓储接口
├── 工厂
└── 规约
4. 基础设施层(Infrastructure Layer)
职责:
- 数据持久化
- 外部系统集成
- 技术服务提供
- 横切关注点
组件:
┌─────────────────────────────────────────┐
│ 基础设施层组件 │
├─────────────────────────────────────────┤
│ 持久化: │
│ - 仓储实现 │
│ - ORM映射 │
│ - 数据库访问 │
│ │
│ 消息: │
│ - 事件发布器 │
│ - 消息队列 │
│ - 事件处理器 │
│ │
│ 外部集成: │
│ - 外部API客户端 │
│ - 适配器 │
│ - 防腐层 │
│ │
│ 技术服务: │
│ - 日志 │
│ - 缓存 │
│ - 配置 │
│ - 安全 │
└─────────────────────────────────────────┘
实施策略
1. 团队组织
康威定律应用
"设计系统的组织,其产生的设计等同于组织之间的沟通结构"
组织结构 ──▶ 系统架构
示例:
┌─────────────┐ ┌─────────────┐
│ 订单团队 │ │ 订单上下文 │
│ │───▶│ │
│ - 产品经理 │ │ - 订单聚合 │
│ - 开发人员 │ │ - 订单服务 │
│ - 测试人员 │ │ - 订单API │
└─────────────┘ └─────────────┘
┌─────────────┐ ┌─────────────┐
│ 支付团队 │ │ 支付上下文 │
│ │───▶│ │
│ - 产品经理 │ │ - 支付聚合 │
│ - 开发人员 │ │ - 支付服务 │
│ - 测试人员 │ │ - 支付API │
└─────────────┘ └─────────────┘
团队自治原则
每个团队负责一个或多个限界上下文:
- 独立的开发周期
- 独立的技术选型
- 独立的部署流程
- 独立的数据存储
团队间协作:
- 明确的接口契约
- 定期的集成测试
- 统一的监控标准
- 共享的基础设施
2. 实施步骤
第一阶段:领域建模
1. 事件风暴 (Event Storming)
- 识别领域事件
- 发现聚合边界
- 确定限界上下文
2. 统一语言建立
- 术语词汇表
- 概念模型图
- 业务流程图
3. 上下文映射
- 识别上下文关系
- 定义集成策略
- 设计防腐层
第二阶段:架构设计
1. 分层架构设计
- 定义层次职责
- 设计依赖关系
- 确定技术选型
2. 聚合设计
- 识别聚合根
- 定义聚合边界
- 设计聚合关系
3. 服务设计
- 应用服务设计
- 领域服务设计
- 基础设施服务设计
第三阶段:实现开发
1. 核心领域实现
- 聚合实现
- 领域服务实现
- 仓储接口定义
2. 应用层实现
- 应用服务实现
- 命令处理器实现
- 查询处理器实现
3. 基础设施实现
- 仓储实现
- 事件发布器实现
- 外部集成实现
第四阶段:集成测试
1. 单元测试
- 聚合测试
- 领域服务测试
- 应用服务测试
2. 集成测试
- 上下文内集成
- 跨上下文集成
- 端到端测试
3. 性能测试
- 负载测试
- 压力测试
- 容量规划
3. 迁移策略
绞杀者模式(Strangler Pattern)
遗留系统迁移:
阶段1: 新功能用DDD实现
┌─────────────┐ ┌─────────────┐
│ 遗留系统 │ │ 新DDD系统 │
│ │ │ │
│ 现有功能 │ │ 新功能 │
└─────────────┘ └─────────────┘
阶段2: 逐步迁移现有功能
┌─────────────┐ ┌─────────────┐
│ 遗留系统 │ │ 新DDD系统 │
│ │ │ │
│ 部分功能 │ │ 大部分功能 │
└─────────────┘ └─────────────┘
阶段3: 完全替换
┌─────────────┐
│ 新DDD系统 │
│ │
│ 全部功能 │
└─────────────┘
分支抽象(Branch by Abstraction)
代码级别的渐进迁移:
1. 创建抽象层
interface OrderService {
Order createOrder(...);
}
2. 实现新旧两套逻辑
class LegacyOrderService implements OrderService
class DddOrderService implements OrderService
3. 通过配置切换
@ConditionalOnProperty("order.service.type")
OrderService orderService;
4. 逐步切换到新实现
order.service.type=ddd
5. 移除旧实现
删除LegacyOrderService
优势与挑战
优势
1. 业务对齐
统一语言:
- 减少沟通误解
- 提高需求理解准确性
- 加速开发进度
- 降低维护成本
领域模型:
- 准确反映业务逻辑
- 易于业务人员理解
- 支持业务变化
- 提高代码可读性
2. 代码质量
高内聚低耦合:
- 清晰的职责分离
- 减少代码依赖
- 提高代码复用性
- 易于单元测试
业务逻辑集中:
- 避免贫血模型
- 业务规则集中管理
- 减少重复代码
- 提高一致性
3. 可维护性
模块化设计:
- 独立的上下文
- 清晰的边界
- 局部化变更影响
- 支持并行开发
演进友好:
- 支持渐进式重构
- 新功能易于添加
- 遗留系统易于替换
- 技术栈易于升级
挑战
1. 学习成本
概念复杂:
- 大量新概念和模式
- 需要深入理解业务
- 设计技能要求高
- 团队培训成本
实践经验:
- 缺乏最佳实践
- 容易过度设计
- 边界划分困难
- 性能优化复杂
2. 实施复杂性
初期投入:
- 建模时间长
- 开发周期延长
- 架构设计复杂
- 基础设施要求高
团队协作:
- 需要领域专家参与
- 跨团队协调复杂
- 统一语言建立困难
- 上下文集成挑战
3. 性能考虑
对象创建开销:
- 大量小对象
- 内存占用增加
- GC压力增大
- 序列化成本
查询复杂性:
- 跨聚合查询困难
- 需要额外的查询模型
- 数据一致性挑战
- 缓存策略复杂
最佳实践
1. 建模实践
事件风暴
参与者:
- 领域专家
- 产品经理
- 架构师
- 开发人员
步骤:
1. 识别领域事件 (橙色便签)
2. 找出命令 (蓝色便签)
3. 发现聚合 (黄色便签)
4. 识别限界上下文 (大纸张)
5. 定义上下文关系 (箭头连线)
产出:
- 事件列表
- 聚合清单
- 上下文地图
- 统一语言词汇表
领域建模
建模原则:
1. 从业务出发,不是技术
2. 关注核心领域
3. 保持模型简单
4. 持续重构完善
建模工具:
- UML类图
- 领域模型图
- 事件流图
- 上下文地图
2. 代码实践
聚合设计
设计原则:
1. 小聚合优于大聚合
2. 通过ID引用其他聚合
3. 一个事务修改一个聚合
4. 使用最终一致性
实现技巧:
- 延迟加载
- 快照优化
- 事件溯源
- CQRS分离
测试策略
测试金字塔:
单元测试 (70%):
- 聚合测试
- 值对象测试
- 领域服务测试
集成测试 (20%):
- 仓储测试
- 应用服务测试
- 事件处理测试
端到端测试 (10%):
- 用户场景测试
- 性能测试
- 安全测试
3. 架构实践
依赖管理
依赖方向:
用户界面层 ──▶ 应用服务层 ──▶ 领域层 ◄── 基础设施层
依赖倒置:
- 领域层定义接口
- 基础设施层实现接口
- 通过依赖注入连接
事件驱动
事件设计:
- 业务事件优于技术事件
- 事件不可变
- 包含足够的上下文信息
- 版本化管理
事件处理:
- 幂等性设计
- 错误重试机制
- 死信队列处理
- 监控和告警
工具和框架
1. 建模工具
txt
可视化工具:
- Miro/Mural (事件风暴)
- Lucidchart (架构图)
- txt (UML图)
- Context Mapper (上下文映射)
协作工具:
- Confluence (文档)
- Notion (知识库)
- GitHub Wiki (版本控制)
- Slack/Teams (沟通)
2. 开发框架
Java生态:
- Spring Boot (应用框架)
- Axon Framework (CQRS/ES)
- JPA/Hibernate (持久化)
- Spring Data (仓储)
.NET生态:
- ASP.NET Core (应用框架)
- MediatR (CQRS)
- Entity Framework (持久化)
- EventStore (事件存储)
Node.js生态:
- NestJS (应用框架)
- TypeORM (持久化)
- EventStore (事件存储)
- Bull (消息队列)
3. 基础设施
消息中间件:
- Apache Kafka
- RabbitMQ
- Apache Pulsar
- Redis Streams
数据存储:
- PostgreSQL (关系型)
- MongoDB (文档型)
- EventStore (事件存储)
- Redis (缓存)
监控工具:
- Prometheus (指标)
- Grafana (可视化)
- ELK Stack (日志)
- Jaeger (链路追踪)
总结
领域驱动设计是一种以业务领域为核心的软件设计方法论,它通过深入理解业务领域,建立准确的领域模型,并将这个模型作为软件设计和实现的基础。
核心价值:
- 业务对齐:确保软件准确反映业务需求
- 代码质量:提高代码的可读性和可维护性
- 团队协作:建立统一的沟通语言
- 架构清晰:明确的职责分离和边界定义
适用场景:
- 复杂的业务领域
- 长期维护的系统
- 大型团队协作
- 业务规则频繁变化
实施建议:
- 从小开始:选择核心子域进行试点
- 持续学习:团队需要不断学习和实践
- 业务参与:确保领域专家深度参与
- 渐进演进:采用渐进式的实施策略
成功关键因素:
- 领导支持:管理层的理解和支持
- 团队能力:具备DDD实践能力的团队
- 业务配合:业务专家的积极参与
- 技术基础:合适的技术栈和基础设施
DDD不仅仅是一种技术方法,更是一种思维方式。它要求我们从业务的角度思考软件设计,将复杂的业务领域转化为清晰的软件模型。虽然学习和实施DDD需要投入大量的时间和精力,但对于复杂的业务系统来说,这种投入是值得的。
通过DDD,我们可以构建出真正反映业务需求、易于维护和扩展的软件系统,为企业的数字化转型提供坚实的技术基础。