title: 架构师学习-DDD author: Gamehu date: 2025-07-11 21:54:50 tags: - DDD categories: - 架构师 ---
架构师学习 第2篇
## **写在前面**
DDD 我在上一家公司的时候就专门学习过,也做了一些实践,可能因为我当时没有太深入的了解,所以总觉得好像有些重,有一种概念大于实操的感觉,不过有些概念定义对我还是很有用的,刚好当下做架构回过头再好好学习下
{%asset_img 1.png %} {%asset_img ddd-01.png %} ## 示例 构建一个**在线电子商务系统(E-Shop)**的设计示例。这个示例将从宏观的战略设计(如何划分系统边界)到微观的战术设计(代码层面的核心元素),全面展示 DDD 的核心思想。 ### 1. 战略设计:界定的上下文 (Bounded Contexts) DDD 的首要原则是软件必须植根于领域,并且模型需要有清晰的边界。 **场景:** 假设我们要构建一个大型在线商店,不仅涉及用户下单,还需要处理库存发货和销售报表。 **传统问题:** 许多团队会试图创建一个包含所有属性(如用户、订单、商品、库存、报表)的单一庞大模型。这会导致模型臃肿,不同职能的团队互相干扰。 **DDD 解决方案:** 我们将系统划分为两个独立的**界定的上下文(Bounded Contexts)**,: 1. **在线交易上下文(E-Shop Context):** 关注客户下单、购物车、结账。这里的“商品”关注价格和描述。 2. **报表上下文(Reporting Context):** 关注销售趋势、库存周转。这里的“商品”可能只关注销售数量和成本,不需要描述信息。 **核心思想:** * **消除歧义:** 同一个词(如“商品”)在不同上下文中可能有不同的含义和属性。通过划分上下文,我们可以保证模型在各自边界内的纯洁性和一致性。 * **上下文映射(Context Map):** 我们定义这两个上下文的关系。例如,报表系统需要从交易系统获取数据,它们可能通过**客户-供应商(Customer-Supplier)**模式交互,或者通过**防崩溃层(Anticorruption Layer)**来转换数据,确保报表系统的模型不受交易系统模型变更的直接破坏,。 ### 2. 战术设计:领域模型的核心要素 在“在线交易上下文”内部,我们使用战术模式来构建领域模型。 #### A. 通用语言 (Ubiquitous Language) 开发人员与业务专家(如销售经理)共同制定一套语言。 * **示例:** 大家不再说“插入一条记录到订单表”,而是统一说“**提交订单(Submit Order)**”。 * **价值:** 这消除了沟通障碍,代码中的类名和方法名将直接反映业务意图,。 #### B. 分层架构 (Layered Architecture) 为了隔离关注点,我们将系统分为四层,: 1. **用户界面层:** 展示商品页面,接收用户点击。 2. **应用层:** 协调任务(如“协调结账流程”),但不包含业务逻辑。 3. **领域层(核心):** 包含 `Order`、`Customer` 等业务对象和规则。**这是软件的心脏**。 4. **基础设施层:** 处理数据库持久化、发送邮件等技术实现。 #### C. 实体 (Entities) 与 值对象 (Value Objects) 这是领域模型的基本构建块。 * **实体(Entity):** * **示例:** `Order`(订单)。 * **设计理由:** 订单有生命周期(从创建到支付到发货),并且需要被追踪。即使两个订单的内容完全一样,只要 ID 不同,它们就是不同的对象。因此,`Order` 是一个实体,必须有唯一的标识符(Identity),。 * **值对象(Value Object):** * **示例:** `Address`(送货地址)。 * **设计理由:** 我们只关心地址的属性(街道、城市),而不关心它的唯一标识。如果两个客户住在同一地址,这在业务上是等价的。`Address` 应该是不可变的(Immutable),如果客户搬家了,我们是用一个新的 `Address` 对象替换旧的,而不是修改旧对象,。 #### D. 聚合 (Aggregates) 为了保证数据一致性,我们需要划定修改数据的边界。 * **示例:** 一个 `Order`(订单)可能包含多个 `OrderItem`(订单项)。 * **设计:** `Order` 是这个**聚合的根(Aggregate Root)**。 * **规则:** 外部对象只能引用根(`Order`),不能直接引用内部的 `OrderItem`。如果想修改某个订单项的数量,必须通过根的方法(如 `order.updateItemQuantity()`)来进行。这确保了订单总价等不变量(Invariants)在修改过程中始终保持一致,。 #### E. 服务 (Services) 有些动作不属于特定的对象。 * **示例:** `CheckoutService`(结账服务)或 `FundTransferService`(转账服务)。 * **设计理由:** 结账可能涉及订单状态更新、库存扣减、支付网关调用等。这些行为放入 `Order` 或 `Customer` 都不合适,因此我们创建一个无状态的**领域服务**来封装这些操作,。 #### F. 资源库 (Repositories) 为了解耦领域模型与数据库。 * **示例:** `OrderRepository`。 * **设计:** 领域层只定义接口 `findOrder(id)`,不关心底层是 SQL Server 还是 Oracle。资源库负责从数据库中检索数据并将其重建为领域对象(如 `Order` 聚合)。这让开发人员可以像从内存集合中获取对象一样获取领域对象,而无需在业务逻辑中编写 SQL,。 ### 3. 系统运作流程示例 结合上述概念,一个“用户修改收货地址”的业务场景在代码设计中如下流转: 1. **应用层**接收请求,调用**资源库**(`CustomerRepository`)。 2. **资源库**利用基础设施层从数据库检索数据,重建 **`Customer` 聚合**(实体)。 3. **应用层**调用 `Customer` 实体的业务方法(如 `customer.moveTo(newAddress)`)。 4. 在方法内部,`Customer` 实体将旧的 `Address` **值对象**替换为新的 `Address` **值对象**。 5. **资源库**将更新后的 `Customer` 聚合保存回数据库。 --- ### 总结与比喻 这个设计示例体现了 DDD 的核心:**通过将软件实现与业务领域模型紧密绑定,来应对复杂性。** 为了巩固理解,我们可以用书中提到的**汽车制造**来类比这个系统设计: * **领域模型**就像是汽车的**设计蓝图**。工人在造车前必须先有精准的图纸,同样,开发软件前必须先理解并建模业务领域。 * **实体与聚合**就像是汽车的**发动机和底盘**。它们是核心部件,有独立的标识和生命周期,必须作为一个整体来组装和维护。 * **分层架构**就像是汽车的**不同系统**(传动系统、电子系统、内饰)。内饰(UI)的变化不应直接影响发动机(领域逻辑)的运作。 * **通用语言**就像是工程师团队之间的**技术术语**。如果有人把“方向盘”叫成“转弯器”,制造过程就会混乱;同样,代码必须精确使用业务术语。 ### 学习资料 https://github.com/Sairyss/domain-driven-hexagon?tab=readme-ov-file