领域驱动设计
- 前言
- 正文
- 领域驱动设计
- 基本概念
- 什么是领域模型?
- 什么是领域服务(Domain Service)?
- 什么是领域事件?
- 秒杀项目中的领域分析
- 一、秒杀活动领域设计
- 秒杀活动
- 领域模型
- 领域服务
- 领域事件
- 二、秒杀品领域设计
- 领域模型
- 领域服务
- 领域事件
- 三、订单领域设计
- 领域模型
- 领域服务
- 领域事件
- 总结
- 参考链接
前言
大家好,我是练习两年半的Java练习生,今天我们来讲一讲关于架构设计中的一种模式,领域驱动设计,也称DDD,Domain-Driven Design。还有介绍一下秒杀项目中领域模型的一些设计。
正文
领域驱动设计
基本概念
领域驱动设计里面包含的概念有:
- 领域:指特定业务领域,如银行、电商、医疗等。领域是开发的核心焦点,领域驱动设计通过深入理解和建模领域,将业务逻辑准确地映射到软件系统中。
- 领域模型:领域模型是对特定领域的抽象和建模,用于描述和表示领域中的实体、属性、关系和行为。领域模型是领域驱动设计的核心工具,它帮助开发团队更好地理解和设计软件系统,以满足业务需求。
- 实体:领域模型中的实体是领域中的具体对象,具有唯一的标识和属性。实体可以包含业务逻辑和行为,与其他实体之间可以有关联和交互。
- 值对象:值对象是没有唯一标识的对象,它的相等性是通过值的相等性来判断的,而不是通过标识。值对象通常用于表示领域中的一些属性或组合属性,如日期、地址等。
- 聚合:聚合是一组相关实体和值对象的集合,它们在领域中具有内聚性,可以作为一个整体进行管理和操作。聚合根是聚合中的一个实体,负责协调聚合内的对象之间的交互。
- 领域服务:领域服务是一些无状态的操作或行为,用于处理领域中的复杂业务逻辑或跨多个实体的操作。领域服务可以与实体和值对象进行交互,但不属于它们的一部分。
- 领域事件:领域事件是领域中发生的重要事实或状态变化的表示,它可以被其他领域对象订阅和响应,以触发相应的行为或处理。
下面是一些形象的比喻,可以帮助你更好地理解领域驱动设计中的几个概念:
- 领域:将领域比喻为一个大象,象征着特定的业务领域。领域驱动设计就是要深入了解这只大象,了解它的特点、行为和需求。
- 领域模型:将领域模型比喻为一张地图,它描述了领域中的实体、属性、关系和行为。地图可以帮助我们更好地理解和导航领域,确保软件系统与领域的契合度。
- 实体:将实体比喻为人类,每个人都有自己的唯一身份和特定属性。实体可以是领域中的具体对象,如客户、订单等,它们有自己的行为和关系。
- 值对象:将值对象比喻为装饰品,它们没有唯一标识,但可以通过它们的值来识别。值对象通常用于表示领域中的一些属性,如电话号码、日期等。
- 聚合:将聚合比喻为一个家庭,家庭成员之间有关系和交互。聚合根就像家庭的父母,负责协调家庭内的事务,并保证家庭的完整性和一致性。
- 领域服务:将领域服务比喻为一个专业的服务提供者,它可以处理领域中的复杂业务逻辑或跨多个实体的操作,类似于一个医生或律师。
- 领域事件:将领域事件比喻为一场重要的演出,演出结束后会触发观众的反应。领域事件可以被其他领域对象订阅和响应,以触发相应的行为或处理。
什么是领域模型?
以一种领域专家、设计人员、开发人员都能理解的通用语言作为相互交流的工具,在交流的过程中发现领域概念,然后将这些概念设计成一个领域模型
领域模型的特点:
- 领域模型是对具有某个边界的领域的一个抽象,反映了领域内用户业务需求的本质。
- 领域模型只反映业务,与技术实现无关
- 领域模型能让领域专家、设计人员、开发人员之间的交流变得方便。
领域建模时思考问题的角度:
《老子》讲过:有之以为利,无之以为用。
有之利,这里指建立领域模型;无之用,即包容用户需求。
- 领域模型设计的时候,不能以客户为中心作为出发点去思考问题,不能老是想着用户会对系统做什么。而是应该从一个客观的角度,根据用户需求挖掘出领域内相关的事务,思考这些事务的本质关联及变化规律。
- 领域模型是排除了人之外的客观世界模型,但领域模型包含人所扮演参与者角色,但是一般不要让参与者角色在领域模型中占据主要位置。如果以人所扮演的参与者角色在领域模型中占据主要位置,那么各个系统的领域模型将变得没有差别,因为软件系统就是一个人机交互的系统,都是以人为主的活动记录或跟踪;比如:论坛中如果以人为主导,那么领域模型就是:人发帖,人回帖,人结贴,等等;
什么是领域服务(Domain Service)?
领域中的一些概念不太适合建模为对象,即归类到实体对象或值对象,因为它们本质上就是一些操作,一些动作,而不是事物。这些操作或动作往往会涉及到多个领域对象,并且需要协调这些领域对象共同完成这个操作或动作。如果强行将这些操作职责分配给任何一个对象,则被分配的对象就是承担一些不该承担的职责,从而会导致对象的职责不明确很混乱
但是基于类的面向对象语言规定任何属性或行为都必须放在对象里面。所以我们需要寻找一种新的模式来表示这种跨多个对象的操作,DDD认为服务是一个很自然的范式用来对应这种跨多个对象的操作,所以就有了领域服务这个模式。
和领域对象不同,领域服务是以动词开头来命名的,比如资金转帐服务可以命名为MoneyTransferService。当然,你也可以把服务理解为一个对象,但这和一般意义上的对象有些区别。因为一般的领域对象都是有状态和行为的,而领域服务没有状态只有行为。
什么是领域事件?
领域事件是领域驱动设计中的一个概念,它表示领域中发生的重要事实或状态变化的表示。领域事件可以被其他领域对象订阅和响应,以触发相应的行为或处理。
在实际项目中,领域事件可以用于解耦领域对象之间的依赖关系,以及实现领域中的异步通信和业务流程的协调。通过引入领域事件,可以实现更灵活、可扩展和松耦合的系统设计。
举个例子,假设有一个电商系统,其中包含商品、订单和库存等领域对象。当用户下单购买商品时,可能会触发以下领域事件:
- 订单创建事件:表示一个新订单已被创建,包含订单的相关信息,如订单ID、用户ID、商品ID、数量等。其他领域对象(如库存管理)可以订阅这个事件,在接收到订单创建事件后,更新库存信息。
- 订单支付事件:表示一个订单已被成功支付,包含订单的支付状态和支付金额等信息。其他领域对象(如财务管理)可以订阅这个事件,在接收到订单支付事件后,更新财务信息。
- 订单取消事件:表示一个订单被取消,包含订单的取消原因和相关信息。其他领域对象(如库存管理)可以订阅这个事件,在接收到订单取消事件后,释放被占用的库存。
通过使用领域事件,订单、库存和财务等领域对象之间可以解耦,它们不需要直接依赖对方的状态变化。当一个领域对象发生状态变化时,它只需要发布相应的领域事件,其他对象可以根据自身的业务逻辑进行相应的处理。
这样的设计可以提高系统的灵活性和可扩展性,因为每个领域对象只需要关注自己感兴趣的领域事件,而不需要直接依赖其他对象的状态变化。同时,领域事件也可以用于实现业务流程的协调和跨领域的通信。
秒杀项目中的领域分析
秒杀系统包含了多个领域,包括核心子域、支撑域和通用子域等。比如,秒杀系统中的活动域、秒杀品域等属于核心子域,是我们需要重点设计并亲自实现的;
而账号子域、交易结算子域等则属于支撑子域,它们具有复用属性,在使用上并不局限于秒杀系统,可以和其他团队和业务共享,这部分可以和其他团队合作完成;最后的风控、缓存、限流等技术型的通用子域则可以通过采用开源产品或购买等引入,不需要我们自己开发。
主要涉及三个领域的设计
- 秒杀活动领域设计
- 秒杀品领域设计
- 订单领域设计
一、秒杀活动领域设计
秒杀活动
秒杀活动是秒杀品的载体,一个秒杀活动可以包含多个秒杀品,并且多个秒杀活动可以同时存在。
领域模型
- 活动名称:通用名称:activityName. 比如“圣诞节限时秒杀”;
- 活动开始时间和结束时间:通用名称:startTime与endTime. 在活动期间内,用户才可以进行秒杀,用户下单时将进行活动时间校验;
- 活动状态:通用名称:status. 分为发布、上线和下线等。只有处于上线状态的活动才对C端用户透出,也只有上线状态的活动才能进行秒杀下单;
- 活动描述:通用名称:activityDesc. 可选,可为活动添加丰富的图文描述。
领域服务
- 发布秒杀活动:当运营侧需要新的秒杀活动时,可以发布新的秒杀活动。秒杀活动发布后,将不对C端用户透出;
- 上线活动:当运营侧已确认活动可以上线时,可以对活动执行上线操作。活动上线后,将对C端透出;
- 下线活动:当活动不需要继续进行时,可以对活动执行下线操作。活动下线后,C端将不可见,同时将不再接收新的秒杀订单;
- 秒杀活动查询和获取:运营侧和C端用户侧均可以获取秒杀活动集合。不同的是,用户侧仅能看见已上线的活动;
- 秒杀活动下单条件检查:当用户执行秒杀下单时,秒杀活动可以根据此前配置的规则执行下单前置校验,已确认当前活动是否允许下单。
领域事件
- 秒杀活动上线:当秒杀活动被执行上线操作时,将发出对应的上线事件,以通知相关订阅者处理;
- 秒杀活动下线:当秒杀活动被执行下线操作时,将发出对应的下线事件,以通知相关订阅者处理。
二、秒杀品领域设计
秒杀品:可上架进行售卖和预定的商品或其他物品。
领域模型
- 所属活动:通用名称:activityId. 秒杀品不可以独立存在,需要关联到秒杀活动;
- 秒杀品标题:通用名称:itemTitle. 秒杀品的主标题;
- 秒杀品副标题:通用名称:itemSubTitle. 可选,可用于对主标题的补充;
- 初始库存:通用名称:initialStock. 秒杀品的原始库存,该值在秒杀活动期间不会发生变化;
- 可用库存:通用名称:availableStock. 秒杀品当前可用库存,该值在初始时与”初始库存“的数值一致,库存扣减将发生该值上。因此,该值会随着用户下单二不断变化,并且是高并发的主要发生字段;
- 原价:通用名称:originalPrice. 秒杀品在其他渠道的售卖价格,该值在活动期间不会发生变化,仅用于信息展示;
- 秒杀价:通用名称:flashPrice. 秒杀品的秒杀价格,该值与”原价“可能不同,并参与到价格计算中;
- 秒杀开始和结束时间:通用名称:startTime与endTime. 当系统时间出于startTime和endTime之间时,秒杀品出于活动中状态,可进行秒杀下单。超过该时间范围后,将不再接收用户下单;
- 秒杀品状态:通用名称:status. 当前秒杀品的状态,存在发布、上线和下线等不同状态,仅处于”上线“状态的秒杀品才会对用户侧透出并接收下单。
领域服务
- 发布秒杀品:当需要发布新的秒杀品,可以执行秒杀品发布操作;
- 上线秒杀品:当确认秒杀品可以上线时,可对其执行上线操作,并在上线后对用户侧透出;
- 下线秒杀品:当秒杀品不再需要继续上线时,可以对其执行下线操作;
- 获取秒杀品及列表:对运营侧和用户侧提供秒杀品获取能力,用户侧仅能获取已上线的秒杀品和秒杀活动;
- 库存扣减:当用户执行下单订单操作时,将进行库存扣减,库存扣减失败时将执行事务回滚;
- 库存恢复:当用户执行取消订单等相关操作时,将进行库存恢复,只有库存恢复成功时,其他操作才能认定成功或继续操作,否则将执行事务回滚;
- 秒杀品下单条件检查:当用户执行秒杀下单时,秒杀品可以根据此前配置的规则执行下单前置校验,已确认当前秒杀品是否允许下单。比如,业务规定同一用户仅能下单一次,那么当用户再次下单时即可拒绝;
领域事件
- 上线秒杀品:当秒杀品被执行上线操作时,将发出对应的上线事件,以通知相关订阅者处理;
- 下线秒杀品:当秒杀品被执行下线操作时,将发出对应的下线事件,以通知相关订阅者处理。
三、订单领域设计
秒杀订单:秒杀业务中由系统向用户提供的产品确认凭据。
领域模型
- 订单ID:通用名称orderId. 订单ID由系统根据业务规则生成,不推荐使用自增ID,极易造成数据安全问题。FlashSale中提供了默认的雪花算法生成规则;
- 秒杀品ID:通用名称itemId. 当前订单对应的秒杀品ID.FalshSale现有的设计仅支持单品下单,如果要支持多品下单,可以通过创建主子订单的方式实现;
- 秒杀品标题:通用名称itemTitle. 秒杀品的标题可能会经常变动,因此在下单时冗余了一份秒杀品的标题;
- 秒杀价:通用名称flashPrice. 下单时的秒杀价;
- 活动ID:通过名称activityId. 下单时的活动ID,虽然通过itemId也能查到,但itemId对应的活动可能存在变化;
- 秒杀品数量:通用名称quantity. 下单时的秒杀品数量;
- 总金额:通用名称totalAmount. 由秒杀价* 数量得到的订单总金额,在实际应用中,总金额的计算可能需要涉及优惠券和其他折扣等;
- 订单状态:通用名称status. 订单的状态,包括创建、取消等;
- 订单创建时间:通用名称createTime.
领域服务
- 下单:订单域的核心服务;
- 根据用户获取订单:当前用户可以获取个人所创建过的订单;
- 根据ID获取订单详情:当前用户可以通过订单ID查看订单详情;
- 根据ID取消订单:当用户不再需要订单时,可以根据ID执行取消操作。当然,是否能取消成功,要看具体的规则。
领域事件
- 下单
- 下单:当用户成功下单后,将发出对应的领域事件,以通知相关订阅者处理。
- 取消订单
取消订单是下单的逆向操作。因此,下单时所发生的数据变更,在订单取消时需要恢复,包括订单数据、库存数据和库存缓存数据。
这部分是我们后面需要关注的重点,包括订单的库存处理、落库处理、预扣缓存、缓存方案等。
总结
好啦,以上就是我们今天要介绍的全部内容,希望能帮助你
如果大家还有什么问题,欢迎私信或者在评论区提出来,让我们一起学习进步!!!
参考链接
https://zhuanlan.zhihu.com/p/361427612
https://juejin.cn/book/7008372989179723787/section/7020612512164888613