領(lǐng)域驅(qū)動設(shè)計(jì)詳解:如何用它來簡化復(fù)雜項(xiàng)目開發(fā)
DDD 是一种软件设计方法,它将业务核心置于开发的中心位置。领域驱动设计(详情见 https://en.wikipedia.org/wiki/Domain-driven_design)。
由埃里克·埃文斯首次在其著作《领域驱动设计(DDD):软件核心复杂性的应对模式》中提出,DDD帮助团队组织代码结构,基于真实的业务概念,从而使应用程序更容易理解和发展。
DDD的关键目标
- 使软件与实际业务问题保持一致
- 改善开发人员和领域专家之间的交流
- 通过围绕业务概念来组织代码,从而降低复杂性
- 提高软件的可维护性和可扩展性
DDD的核心概念"要创建好的软件,你必须先了解软件的方方面面。否则你无法开发出一个银行软件系统,如果你没有对银行业务有深入了解的话。"
——《领域驱动设计》作者 Eric Evans
DDD 提出了一些关键概念来帮助构建应用程序结构。让我们来看看这些概念:
1. 领域与子领域
一个 领域 或范畴 是你的软件所处的知识或活动范围(比如,电子商务、银行业、医疗保健)。
在一个领域内,可以有多个子系统,例如支付、库存和客户维护。
2. 第二部分:有限边界上下文域
一个有界的上下文定义了一个明确的边界,围绕着领域中的特定部分,确保这些模型在该上下文中保持独立且连贯,从而保持一致性。
例如,在一个电子商务系统中,“订单处理”和“产品目录”模块各自有自己的数据模型。
3. 实体类与值对象
- 每个实体都有独一无二的身份并且随着时间的推移而变化(例如,
User
,Order
)。 - 值对象 是不可变的,其价值取决于其属性(例如,
Money
,Address
)。
4. 聚合物及聚合根
聚合是一组领域对象,它们被视作一个整体来处理。
聚合的根是确保聚合内部一致性的主要实体。
比如说,一个 Order
实体(聚合根实体)可以包含 OrderItem
实体,但会强制执行这样的规则,例如“一个订单必须至少包含一个产品”。
5. 领域中的事件
领域事件代表业务领域中的重要事情,例如OrderPlaced
,PaymentReceived
等。
这有助于解耦业务逻辑并提高系统的可维护性。
6. 代码库、服务端及工厂模式
- 存储库管理领域对象的检索和持久化,抽象数据访问的细节。
- 领域服务封装了不适合直接放在实体或值对象中的特定领域操作。
- 生成器简化了对象的创建,确保复杂对象的正确创建。
7. 无处不在的语言
开发人员和领域专家之间有一种共同的语言,确保清晰度和一致性,避免误解。
目的创建一个系统概念性描述,既能被开发人员也能够被行业专家理解。
比如说,在一个赌博系统中,会包含以下术语,比如“比赛”、“下注”和“赔率”这样的术语。
用DDD来应对复杂性有的人觉得复杂,有的人却觉得很简单。
然而,在软件开发中,复杂性往往源于相互关联的系统、多个数据源。此外,还有不同的商业目标。
DDD 的目的是通过围绕核心业务概念来构建软件,从而解决这些问题。
相比之下,对于相对简单的应用,一种简单的设计方法就足够了。
但随着系统的发展,其复杂性自然会逐渐增加,从长远来看,采用领域驱动设计更为有利。
在现代商业环境中,精准度至关重要,因为糟糕的架构选择可能会带来严重的后果。
DDD 提供了一个框架来处理复杂的业务逻辑,确保软件与业务目标保持一致。领域驱动设计(Domain-Driven Design)
在实际项目中实现领域驱动设计(DDD)(领域驱动设计简称DDD)
现在我们已经掌握了基础知识,让我们来看看领域驱动设计在实际应用中的表现。
1. 识别领域和子领域
对于一个电子商务平台,可能的子领域有“在线购物这一主要领域”。可能的子领域还包括:
- 订单管理(核心业务)
- 客户支持服务(客户支持)
- 产品推荐(通用业务)
2. 界定上下文
- 订单上下文负责处理订单、支付和发票事宜。
- 产品上下文管理产品的目录和库存情况。
3. 设计聚合和实体
你可以这样定义订单情境:
/**
* 订单类用于管理订单信息,包括订单ID、项目列表和订单状态
*/
class Order {
constructor(
private orderId: string,
private items: OrderItem[],
private status: OrderStatus
) {}
/**
* 添加一个物品到订单中
*/
addItem(item: OrderItem) {
this.items.push(item);
}
/**
* 完成订单
*/
completeOrder() {
this.status = '已完成';
}
}
切换到全屏, 切换回退出
4. 解耦:使用领域事件(Domain Events)
我们不是直接对 Order
中的支付系统进行修改,而是触发一个领域事件(Domain Event)。
// 订单下单类,包含订单ID
class OrderPlaced {
constructor(public readonly orderId: string) {}
}
点全屏 关掉全屏
一个处理器会监听此事件来处理支付。
5. 使用仓库进行数据持久存储.
class OrderRepository {
// 订单映射
private orders: Map<string, Order> = new Map();
// 保存订单
save(order: Order) {
this.orders.set(order.orderId, order);
}
// 根据订单ID查找订单
findById(orderId: string): Order | undefined {
return this.orders.get(orderId);
}
}
全屏模式 退出全屏
何时应用领域驱动设计DDD虽然强大,但并不适用于每个项目。下面列举一些DDD适用的场景:
- 复杂的业务逻辑和不断演变的需求
- 大规模的应用,由多个团队协作完成
- 需要业务与开发团队高度协同的项目
避免使用DDD:
- 简单的 CRUD 程序
- 简单原型或 MVP,领域复杂度较低
你真的需要DDD吗?
如果你不了解DDD解决的问题,那可能就用不上它了。
DDD特别适合长期复杂的项目(通常超过6个月)。
如果你的项目没有明显的业务复杂性,采用DDD可能带来不必要的负担。
此外,在研究领域驱动设计之前,了解设计模式和企业设计原则非常重要。
对于没有面向对象设计经验的人来说,像仓库、工厂和聚合体这样的概念可能感觉很有挑战性。
最后的想法注:这里的"thoughts"已翻译为“最后的想法”,如需调整请告知。
领域驱动设计(DDD)不仅仅是遵循一些规则——它是一种思维方式,帮助开发者开发与实际业务需求紧密相关的软件。
通过专注于特定领域、明确界定边界并利用领域中的事件,你就能打造出既可扩展又易于维护的应用程序了。
如果你正在开发或构建一个复杂的系统,可以考虑使用领域驱动设计(DDD)原则,这能为你的代码库带来长期稳定性和清晰度。
接下来干什么?
- 深入了解 事件溯源(Event Sourcing) 和 CQRS,因为它们常常与 DDD 相辅相成。
- 探索像 NestJS 或 Spring Boot 这样的工具,它们支持 DDD 模式的实现。
- 加入讨论 DDD 最佳实践的社群。
有关DDD的想法?下面评论一下!
我正在开发一个超级方便好用的工具,叫做LiveAPI。
LiveAPI 在几分钟内快速整理你的后端 API 文档
通过使用 LiveAPI,您可以快速生成可以交互的 API 文档,这样允许用户直接在浏览器中执行 API。
如果你厌倦了手动为API编写文档,这个工具可能让你的工作更轻松。
共同學(xué)習(xí),寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章