领域驱动设计
-
-
- 为什么需要DDD?
- DDD的两个核心支柱
-
- [一、 战略设计 ------ 宏观划分,厘清边界](#一、 战略设计 —— 宏观划分,厘清边界)
- [二、 战术设计 ------ 微观建模,构建模型](#二、 战术设计 —— 微观建模,构建模型)
- DDD的核心流程与优势
- 一个简单示例(保险理赔)
- 何时使用DDD?
- 学习建议
-
领域驱动设计(Domain-Driven Design,DDD) 。它是一套非常强大且系统的软件设计方法论,尤其适用于处理复杂业务领域的软件开发。
简单来说,DDD的核心思想是:将软件的核心复杂性聚焦于业务领域本身,通过建立精确的业务模型来驱动软件设计,让软件成为业务领域的映射。
它不是一种具体的技术框架,而是一种思维方式、一套设计原则和模式集合。
为什么需要DDD?
在传统开发中,业务人员(领域专家)和开发人员经常"鸡同鸭讲":业务谈需求,开发想数据库和接口。这导致软件难以体现真实的业务逻辑,随着迭代会越来越臃肿、难以维护。DDD旨在解决这个问题,让所有人使用一套基于业务的"通用语言" 来沟通和设计。
DDD的两个核心支柱
DDD通常分为战略设计 和战术设计两个层面:
一、 战略设计 ------ 宏观划分,厘清边界
这部分关注如何理解、划分和组织复杂的业务领域。核心概念有:
-
领域与子域:
- 领域: 软件要解决的整个业务问题空间(例如:电商系统、保险理赔系统)。
- 子域 : 将庞大领域拆解为更小、更聚焦的部分。分为:
- 核心子域: 公司的核心竞争力,投入最多精力(例如:电商的"商品推荐"和"交易流程")。
- 支撑子域: 支持核心业务,非通用但必要(例如:电商的"物流跟踪")。
- 通用子域: 通用解决方案,可直接购买或使用现成(例如:权限管理、通知服务)。
-
限界上下文: 这是DDD中最重要的战略设计概念!
- 一个语义和业务边界的显式划分。在边界内,模型术语(通用语言)有唯一、明确的含义。
- 例如 :在电商系统中,"商品"在销售上下文 中有价格、促销信息;在仓储上下文 中有重量、尺寸、库存位置;在物流上下文中可能简化为一个ID和描述。它们是同一个"商品"在不同边界内的不同模型。
- 每个限界上下文通常对应一个独立的微服务或模块。
-
上下文映射图:
- 描述不同限界上下文之间如何通信和集成的关系图。
- 常见关系:合作伙伴关系、客户/供应商关系、防腐层、共享内核、开放主机服务等。
二、 战术设计 ------ 微观建模,构建模型
这是在单个限界上下文内部,用于构建精细领域模型的具体实现模式。核心概念有:
- 实体: 具有唯一标识和生命周期,会随时间变化的对象。关心的是谁 (例如:订单
Order、用户User,通过ID标识)。 - 值对象: 没有唯一标识,通过其属性值来定义的对象。关心的是什么 (例如:地址
Address、金额Money。两个属性完全相同的地址,可以视为同一个值对象)。 - 聚合与聚合根:
- 聚合 : 一组相关实体和值对象的集合,被视为一个数据修改的单元。
- 聚合根: 聚合的入口点,外部只能通过聚合根来访问和修改聚合内部对象,保证业务一致性。
- 例如 :
订单(Order)是聚合根,包含订单项(OrderItem)实体和收货地址(Address)值对象。修改订单项必须通过订单对象。
- 领域服务: 当某个操作或业务逻辑不适合放在实体或值对象内部时(例如:涉及多个聚合的复杂计算、调用外部系统),将其封装在无状态的领域服务中。
- 领域事件: 表示在领域中发生的、对业务有重要意义的事件(例如:
订单已付款、库存已锁定)。用于解耦不同聚合或限界上下文,实现最终一致性。 - 仓储: 负责聚合的持久化和检索,提供一个类似集合的接口,将领域模型和数据存取技术(如数据库)解耦。
- 应用服务: 协调领域对象、仓储、领域服务来完成一个具体的用例(用户故事)。它很薄,不包含业务逻辑,主要负责事务、安全和流程编排。
DDD的核心流程与优势
- 与领域专家合作: 深入理解业务。
- 建立通用语言: 团队在交流、代码、文档中使用一致的业务术语。
- 划分限界上下文: 识别出清晰的业务边界。
- 在边界内进行战术建模: 使用实体、值对象、聚合等构建高内聚的领域模型。
优势:
- 降低复杂度: 通过战略划分,将大问题分解为高内聚、低耦合的小问题。
- 提高可维护性和扩展性: 清晰的边界使修改和扩展更容易。
- 提升代码质量: 富有表达力的领域模型,使代码更贴近业务,易于理解。
- 改善团队沟通: 通用语言是沟通的基石。
一个简单示例(保险理赔)
- 战略层面 :
- 核心子域:理赔处理
- 限界上下文 :
理赔处理上下文、保单管理上下文、支付上下文
- 战术层面 (在"理赔处理上下文"内):
- 实体 :
理赔案(Claim),有唯一理赔号。 - 值对象 :
事故描述(AccidentDescription)。 - 聚合根 :
理赔案(Claim)是聚合根,内部包含理赔项(ClaimItem)实体。 - 领域事件 :
理赔已受理(ClaimSubmitted)、理赔已审核(ClaimApproved)。 - 领域服务 :
理赔计算服务(ClaimCalculationService),根据复杂规则计算赔付金额。 - 应用服务 :
提交理赔申请服务,它调用Claim实体、ClaimCalculationService,并发布ClaimSubmitted事件。
- 实体 :
何时使用DDD?
- 推荐使用:业务逻辑非常复杂、核心、频繁变化的系统(如金融、电商、ERP、供应链系统)。
- 谨慎或避免使用:简单CRUD系统、技术工具类软件、一次性原型。对于这些场景,使用DDD可能会过度设计,增加不必要的复杂度。
学习建议
- 必读经典: Eric Evans的《领域驱动设计:软件核心复杂性应对之道》(蓝皮书)。
- 实践指南: Vaughn Vernon的《实现领域驱动设计》(红皮书)。
- 从小处开始: 尝试在一个核心功能上实践战术建模,再逐步理解战略设计。
- 结合现代架构 : 理解DDD如何与微服务架构 、事件驱动架构 、CQRS(命令查询职责分离)等结合,这是目前非常主流的实践。
总结: DDD是一套"道"与"术"结合的方法论。它引导我们首先从业务视角(战略)理清边界,再从技术视角(战术)构建精良的模型。虽然学习曲线较陡,但对于应对核心业务系统的复杂性,它提供了迄今为止最系统、最深刻的解决方案。