上一讲,我们介绍了如何设计秒杀的系统架构。
在电商系统中,我们通常把完整的购物流程划分为下单前和下单后。下单是由用户在商城发起的,下单后订单涉及关单、支付、退款、筛单、投递、售后等流程。
其中,关单涉及库存系统,支付流程涉及支付系统,退款流程涉及客服、支付和财务系统,筛单涉及风控系统,投递涉及物流和仓储系统,售后涉及售后系统。
说了这么多,你有什么发现没?仅仅一个订单的完整流程,就涉及好几个系统。
此时,就有一个问题产生了:我们该如何划分各个系统的职责边界呢?也就是说,当订单数据被多个系统用到时,订单数据到底由谁来维护呢?
这就是我们本讲要解决的问题——DDD 原理及秒杀系统领域模型。
注:本讲结尾还有秒杀系统领域建模的思维导图哦,可以让你一目了然。
DDD(Domain Drive Design,领域驱动设计)是一种软件设计方法,是指在软件设计的过程中始终围绕领域来构建模型。构建领域模型的过程就叫“领域建模”。
怎么理解呢?
这里的领域,我们可以理解成业务对象。
举个例子:一个订单中心的业务逻辑总是围绕订单数据来实现的,比如下单、取消订单、订单退款、订单履约等业务逻辑。而订单数据在面向对象编程中的实体是订单对象,也就是订单中心的业务对象。
领域模型就是业务对象模型,是描述业务功能实现的对象模型,它是对业务对象协作关系和业务执行逻辑的一种抽象提炼。
那在一个业务系统中可以有多少领域呢?在一个遵循领域驱动设计的业务系统中,可以有多个领域。但是,应当有且只有一种核心领域,因为这样它的职责才非常清晰。另一些非核心领域,我们统称为子领域。
现在你是不是理解了领域模型是什么呢?而DDD 就是为了解决各个业务系统的职责划分,围绕业务对象而进行模型建构。
具体它是怎么做的呢?
DDD 将领域模型分为 4 层,如下图所示:
图1 领域驱动设计风格的架构草图(来自 DDDSample 官网)
这 4 层分别是:
Interfaces用户界面层,有时候也叫呈现层,比如前端或者客户端;
Application 应用层,负责给用户界面层提供业务应用的业务逻辑,如商城接口服务;
Domain 领域层,负责某个核心领域的具体业务逻辑,如电商的订单中心;
Infrastructure 基础设施层,如 MQ、MySQL、Redis 等。
有了这样的层级划分,加上一些划分层级的规则,我们就能快速找出各个系统的业务边界,将系统的职责划分清楚,以免逻辑混乱难以维护。
比如,有个接口是提供给运营后台前端用的,结果在划分职责的时候将其划分到商城 API 服务里了,这样可能会直接暴露给外网用户,不仅导致逻辑混乱,还存在严重风险。如果按照 DDD 的分层模型来划分,我们就能将 API 服务和管理后台的职责划分清楚,避免此类问题。
实际上,现代软件系统往往功能复杂,需要微服务化,以便进行长期维护。而在微服务架构中,由于需要将业务功能拆分成多个微服务,就涉及领域划分的问题。如果没有 DDD 的理论指导,我们很可能将某个功能,划分到错误的微服务里,比如,将订单功能划分到物流服务里。
如果你想了解有关 DDD 更多的内容,可以学习我们的另一个专栏《DDD 微服务落地实战》。
领域建模都有哪几个步骤呢?从整体上看,领域建模分为战略建模和战术建模。
什么叫作战略建模?战略建模是指从宏观上构建领域模型。
拿建楼来打个比方:建大楼之前,需要先绘制出大楼的整体外观设计并划分出大楼每个功能区,然后再进行整体结构设计。这些划分出来的功能区,就好比软件系统中的各个子系统和组件。比如在前几讲提到的前端、后端接口服务、后台配置服务是秒杀系统的组件,商品中心、库存中心、用户账号中心是秒杀系统依赖的子系统。
怎样进行战略建模呢?
战略建模主要分为以下几个步骤。
第一步,确认系统中的各个子系统和组件划分到领域模型中的哪一层。
以秒杀系统为例,秒杀商城的前端和管理后台的前端,我们可以把它划分到用户界面层。
秒杀系统接口服务,因为负责给秒杀前端提供活动信息和活动相关商品信息,我们可以把它划分到应用层。
秒杀系统配置服务、商品中心、库存中心和用户账号中心,因为涉及对应核心领域的具体业务逻辑,我们把这些划分到领域层。
最后是 MQ、MySQL、Redis、数据统计、监控系统等,负责提供数据的流转、存储、查询的能力,可以划分到基础设施层。
第二步:为领域层划分核心域和非核心域。
这样做的目的是区分哪些是核心业务逻辑,哪些是非核心业务逻辑,便于后期设计。
那么,秒杀系统的核心域和非核心域分别是什么呢?
我们知道,秒杀系统包括活动信息、商品信息、用户信息、库存信息。而秒杀系统核心功能是提供秒杀活动的能力,不管是商城前端页面还是管理后台页面,交互的入口都是从活动信息开始。所以,秒杀活动信息就是秒杀系统的核心域,而商品信息、库存信息、用户信息是非核心域。
需要注意的是,为了区分哪些是用于支撑秒杀业务的,哪些是在所有系统中通用的,非核心域要区分支撑子域和通用子域。
什么叫支撑子域和通用子域呢?
所谓支撑子域,是指跟当前业务有关联的非核心域,并在当前业务系统中起到支撑业务的作用,如秒杀系统中用到的商品信息、库存信息都是为秒杀活动提供支撑作用。
所谓通用子域,是指跟当前系统的核心业务逻辑关系不大但又必须要有的非核心域,在所有系统中都通用的,如秒杀系统中的用户信息等。
第三步,确定各领域的限界上下文,确定领域的业务边界。
这一步是战略建模最核心的工作,主要是为了明确系统中各子系统和业务模块的具体职责。
那么,秒杀系统中的限界上下文到底是什么呢?在秒杀系统的核心域中主要涉及的对象有:专题信息、场次信息、商品信息。其中商品信息包括价格、库存、文案,而价格分为秒杀价和原价,库存分为秒杀库存和实际库存,文案主要是秒杀文案。
下面我们用一张图来表示它们的关系:
图中大的椭圆表示领域模型分层中的领域层,虚线表示各领域的边界,虚线圈定的范围是领域,领域内部的椭圆表示限界上下文,实线表示限界上下文之间的映射关系。限界上下文和上下文之间的关系组成上下文映射图。
最后一步,为各限界上下文建立上下文映射图,为战术建模做准备。
在秒杀系统中,核心域的限界上下文是活动上下文,比如:活动主题、活动场次等。对于商品的活动信息,因为包含活动价格、活动文案、活动库存,初看应该归属活动上下文。但是,我们也有按照商品原始信息安排的销售活动,比如新品发布会当天的新品销售。所以,商品活动信息是属于商品的销售属性,应当分拆给商品上下文和库存上下文。
各限界上下文的上下文映射图如下所示:
有了上下文映射图后,我们接下来就可以做战术建模了。
刚才咱们提到了战术建模,那战术建模又是什么呢?所谓战术建模,就是从具体细节上构建领域模型,它是对战略建模中限界上下文的具体实现。
还是以秒杀系统为例,秒杀系统的战术建模就是分析活动领域中各个对象的类型,针对类型特点做抽象设计。
那具体要怎么做呢?
不知道你有没有注意到,这些对象里有的是可以通过 ID 或者名称唯一确定的。
比如,通过专题名称或 ID 唯一标识专题,通过商品 ID 唯一标识商品,通过账号 ID 唯一标识账号或用户。这些能被唯一标识出来的对象我们称之为“实体”。虽然场次在时间上也是唯一的,但我们不需要唯一标识来区分它,只需要保证时间上不重叠即可。
那些不能被唯一标识出来的对象,要么是一个具体的值,比如库存数和价格,我们称它为“值对象”;要么是将其他对象聚合而来,比如场次信息聚合了专题 ID 和商品,我们把它叫“聚合根”。另外,领域对象还可能触发某些事件,比如活动开始和结束,我们把它叫“领域事件”。
所以,在秒杀系统中,战术建模后的结果如下:
实体,包括主题、商品、账号;
值对象,包括实际库存、销售库存、原价、活动价;
聚合根,包括场次、活动商品;
领域事件,包括活动未开始、活动进行中、库存已售罄、活动已结束。
有了战术建模后,我们就可以针对领域做各种抽象类设计了。比如,我们将抽象出主题类、商品类、场次类、库存类、账号类等。当然,这块属于详细设计,后续我会在代码实现那几节细讲。
这一讲我们初步学习了领域驱动设计,以及以秒杀系统为案例进行领域建模实战。通过领域建模实战,你是不是既加深了对需求的理解,又学会了一种新的软件工程方法论呢?
使用 DDD,我们可以清晰地划分领域边界,划分子系统的职责,设计出扩展性良好的系统架构。由于系统职责划分清晰,也就能给核心业务和非核心业务合理地分配好资源,确保核心业务系统的性能和可用性。
给你留一个思考题:如何为订单系统构建领域模型?可以把你的想法写的下面留言区哦。
这一讲我们就先介绍到这里,下一讲,我们将具体来介绍秒杀系统的高可用架构设计。
读完文章,关于秒杀系统的ddd的分析,已经很清楚了,但是如果我们企业级比较复杂的业务,进行战略和战术分析,有什么建议嘛,还有就是如何衡量业务适不适合ddd呢,谢谢老师
复杂的业务系统很适合用DDD。首先需要理清楚逻辑视图中的各种功能,然后按照功能之间的耦合关系强弱划分出多个业务领域,每个业务领域是个一级子系统。一级子系统内部视功能逻辑的复杂程度再拆二级子系统。总之就是通过拆分出多个高内聚低耦合的子系统,化繁为简。
牛,厉害。看别人的视频看几遍都看不懂别人讲的领域椭圆图,老师讲课完全是说给他自己听的。反而看您的文章,一看就懂了,好文
看得不是很明白啊
领域驱动设计中涉及多个比较抽象的概念,需要多结合工作中的项目来理解。就像软件设计的7大法则和23种设计模式,需要结合项目多看几遍,特别是项目中遇到痛点寻求解决方案的时候,结合问题来看更容易理解。
想请教下,domain模型里根据实体,值对象,服务,仓库,工厂和聚合如何去规划目录会好点
这个看项目复杂度。根据项目复杂度来采用合适的目录结构。根据实体、值对象等划分目录,颗粒度比较细,适合代码量较多的复杂项目。
核心域:订单上下文 订单信息支撑子域: 商品上下文 = 物流信息通用子域:用户上下文 = 账户信息
订单系统包括商品、库存、用户、结算、物流、售后几个部分。
感谢作者,收获蛮大,感觉还是比较精致的,后面看看如何,希望后面能把5视图详细分析清楚,以及案例,我不是计算机专业毕业的,物理视图这块还想能够有深入的理解,能推荐些文章看看也好
物理视图描述的是最底层的物理设备间的拓扑关系。需要对计算机物理设备有一定了解。特别是网络设备,它是整个互联网的神经系统。另外物理设备的部署方式决定了物理设备的可用等级,只要是设备,就必定有需要维护的时候,就需要在部署的时候考虑到维护设备带来的稳定性问题。可以找一本IDC机房相关的书籍了解一下。