架构师学习-DDD
架构师学习
第2篇
示例
构建一个在线电子商务系统(E-Shop)的设计示例。这个示例将从宏观的战略设计(如何划分系统边界)到微观的战术设计(代码层面的核心元素),全面展示 DDD 的核心思想。
1. 战略设计:界定的上下文 (Bounded Contexts)
DDD 的首要原则是软件必须植根于领域,并且模型需要有清晰的边界。
场景: 假设我们要构建一个大型在线商店,不仅涉及用户下单,还需要处理库存发货和销售报表。
传统问题: 许多团队会试图创建一个包含所有属性(如用户、订单、商品、库存、报表)的单一庞大模型。这会导致模型臃肿,不同职能的团队互相干扰。
DDD 解决方案:
我们将系统划分为两个独立的界定的上下文(Bounded Contexts),:
- 在线交易上下文(E-Shop Context): 关注客户下单、购物车、结账。这里的“商品”关注价格和描述。
- 报表上下文(Reporting Context): 关注销售趋势、库存周转。这里的“商品”可能只关注销售数量和成本,不需要描述信息。
核心思想:
- 消除歧义: 同一个词(如“商品”)在不同上下文中可能有不同的含义和属性。通过划分上下文,我们可以保证模型在各自边界内的纯洁性和一致性。
- 上下文映射(Context Map): 我们定义这两个上下文的关系。例如,报表系统需要从交易系统获取数据,它们可能通过客户-供应商(Customer-Supplier)模式交互,或者通过防崩溃层(Anticorruption Layer)来转换数据,确保报表系统的模型不受交易系统模型变更的直接破坏,。
2. 战术设计:领域模型的核心要素
在“在线交易上下文”内部,我们使用战术模式来构建领域模型。
A. 通用语言 (Ubiquitous Language)
开发人员与业务专家(如销售经理)共同制定一套语言。
- 示例: 大家不再说“插入一条记录到订单表”,而是统一说“提交订单(Submit Order)”。
- 价值: 这消除了沟通障碍,代码中的类名和方法名将直接反映业务意图,。
B. 分层架构 (Layered Architecture)
为了隔离关注点,我们将系统分为四层,:
- 用户界面层: 展示商品页面,接收用户点击。
- 应用层: 协调任务(如“协调结账流程”),但不包含业务逻辑。
- 领域层(核心): 包含
Order、Customer等业务对象和规则。这是软件的心脏。 - 基础设施层: 处理数据库持久化、发送邮件等技术实现。
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. 系统运作流程示例
结合上述概念,一个“用户修改收货地址”的业务场景在代码设计中如下流转:
- 应用层接收请求,调用资源库(
CustomerRepository)。 - 资源库利用基础设施层从数据库检索数据,重建
Customer聚合(实体)。 - 应用层调用
Customer实体的业务方法(如customer.moveTo(newAddress))。 - 在方法内部,
Customer实体将旧的Address值对象替换为新的Address值对象。 - 资源库将更新后的
Customer聚合保存回数据库。
总结与比喻
这个设计示例体现了 DDD 的核心:通过将软件实现与业务领域模型紧密绑定,来应对复杂性。
为了巩固理解,我们可以用书中提到的汽车制造来类比这个系统设计:
- 领域模型就像是汽车的设计蓝图。工人在造车前必须先有精准的图纸,同样,开发软件前必须先理解并建模业务领域。
- 实体与聚合就像是汽车的发动机和底盘。它们是核心部件,有独立的标识和生命周期,必须作为一个整体来组装和维护。
- 分层架构就像是汽车的不同系统(传动系统、电子系统、内饰)。内饰(UI)的变化不应直接影响发动机(领域逻辑)的运作。
- 通用语言就像是工程师团队之间的技术术语。如果有人把“方向盘”叫成“转弯器”,制造过程就会混乱;同样,代码必须精确使用业务术语。
学习资料
https://github.com/Sairyss/domain-driven-hexagon?tab=readme-ov-file