DDD面试篇

DDD面试篇,第1张

DDD面试篇

背景:程序员都是面向数据库思维,拿到业务需求之后,第一时间考虑数据库层面的东西,需要哪些表+怎么 *** 作,一顿CRUD之后业务需求就完成了(SSM框架+springBoot+springCloud,controller+service+dao+mapper(mybatis写sql))
问题:代码完全丢弃了所有的业务语义,代码跟真实的业务流程,业务模型没关系。导致数据库思维写出来的代码和业务脱离严重,可读性&可维护性差

玩DDD分四步:
1)战略建模
2)战术建模
3)代码落地(业内比较好的实践以及开源的DDD框架,阿里开源的cola。)
4)集成技术框架(rpc/MQ/持久化/存储等用什么)

第一步:战略设计。===================================================
1)有界上线文的划分(厘定清楚各自负责的东西,边界),
2)映射(有界上下文之间的交互),
3)子域类型(核心子域,支持子域,通用子域)的确定,
4)通用语言的确定(专门描述你的有界上下文里面的各种事情,动作, *** 作,行为,名词,事务。字段,数据,状态,命令,事件,统一命名规范)

事件风暴会议
1)召集一个会议,产品,开发,用户,管理人员,运营等一起进行头脑风暴。梳理履约相关的所有事件(动词,动作)有多少说多少
2)把梳理出来的所有事件按照时间线进行排列
3)梳理出履约相关的所有的用户界面和命令,针对上面所有事件的发生,是否有系统用户界面,有用户参与发送命令(点击按钮,提交表单等)驱动了履约的各个事件的发生
4)把用户界面,命令,事件,按照时间线,先后顺序,交互逻辑,全部串联起来
5)在上面的大串联逻辑中,找出履约有界上线文,哪些是履约应该解决的问题,哪些是别人应该去做的事情
备注:
1.头脑风暴需要根据自己对业务的理解,把技术的当西全部剥离掉,存粹站在业务的角度去梳理所有和业务有关的流程和东西
2.如果业务特别简单,没必要使用DDD。只有业务特别复杂才需要使用DDD
会议结论:
1.解决不同类别的问题的上下文,边界,就很清晰的出现了
2.电商领域 ——>
订单有界上下文核心子域
履约有界上线文核心子域(解决履约问题域)
仓储有界上下文支持子域
物流有界上下文支撑子域
第三方物流有界上下文通用子域

领域类型划分
核心子域:对于一个电商领域而言,用于完成用户购物这个核心需求,所需要具备的一些核心子域,就属于核心子域。比如商品子域,订单子域,支付子域,履约子域,会员子域,营销子域
支撑子域:负责支持核心子域。作用是明显用于支持别的核心子域的功能实现的。履约的过程需要仓储和物流,仓储支撑子域和物流支撑子域。财务子域支持我们的一些业务运作需要报表支撑子域
通用子域:不用自己公司来开发,都是外采的软件,或者第三方通用的平台和系统,第三方支付平台,第三方物流平台都是通用子域

映射:描述有界上下文之间的关系
1)separate way:完全没关系
2)customer-supplier:u-d,可以商量,常见于一个打的项目中内的不同的小项目组之间的依赖关系。
大厂有几块大的业务,新零售,O2O,B端,云计算,电商。假设大的事业部技术团队有500人,划分了n个大的团队,每个团队有几十个人,每个团队的几十个人又划分为了几个小组,每个小组负责维护一个系统,系统之间有依赖的关系,接口之间的互相调用。由于大家关系和距离,组织架构比较近,一些接口的需求可以很好的进行商量
3)public-subscribe:ps发布订阅。可以是别人发布自己监听订阅处理。也可以是自己发布,自己/别人去监听处理
4)anti corruption layer:acl防腐层,不是一个鼓励,单独使用的映射关系,一般是和其他的映射关系一起使用
cs+cal:A服务调用B服务接口,B接口本来返回的8个字段,现在字段名称或者值变化了,对于A来讲可以加一个防腐层,将接受的B接口的结果进行适配和转化,
public class ServiceA{
public void method(){
RawResult rawResult = serviceB.method();//原始数据结果
Result result = antiCorruptionLayer.vonvert(rawResult);//防腐层
//代码里用自己的result就可以了,不用关心原始数据RawResult
}
}
5)conformist:(遵奉模型)u-d完全没法商量。不同的大部门之间,进行系统的调用。对外提供的接口都是死的,能用就用,用不了拉倒。按照自己的计划进行升级迭代,不提供定制化接口
6)partnership:强耦合,合作。那种小而紧凑的系统,有几个人独立维护开发,负责不同的模块,也按照DDD划分了多个子域,每个子域(有界上下文)就对应了一个微服务,系统可以划分为多个微服务,微服务之间耦合性特别高。新需求必须一起评审,开发,上线,一起成功上线/一起失败回滚
7)open host service + published language:OHS+PL对于第三方支付平台/物流平台,saas云平台(提供出来的API),他们会定义一套自己的通信协议(一般是http)和数据格式(一般是自定义JSON)
8)shared kernel:共享内核,两个有界上下文,共享一个数据模型。数据模型是两个团队一起维护,比较少见
【总结】:常见的就是cs,ps,conformist,acl,OHS+PL

通用语言
user和customer都是用户,一个上下文中应该对所有的名词,东西,业务 *** 作,业务语义都是用同一的一套规则和规范,同一个词在不同的有界上下文中理解是不一样的。仅限当前有界上下文中通用语言有效。

第二步:战术设计。===================================================
牵扯到类层面的设计,以及这些类是如何配合,可以实现你的上下文里要解决的各种问题
战术设计定义:聚合,实体,值对象,仓储,领域服务,业务组件,领域事件,命令(六边形架构,清洁架构,CQRS架构)
1)实体:之前在项目里高德domain类,Order(有唯一表示orderId,它里面的很多数据是可以变化的)。我们的上下文中,对于一些核心的数据一般都会设计出一些类,如果他有唯一的标识,并且他的数据是允许变化的就称之为实体Entity
2)值对象:ValueObject。对于一个订单,他的唯一标识orderId而言,里面可能包含和封装了在数据库里面的唯一主键和我们分配给他的一个订单编号,这些东西是的绝对都不会变化的,DDD战术设计里称之为值对象
3)聚合:Aggregate。
面向数据库设计,一个表对应domain/entity包里面的一个class,order,orderItem,每个class里面就对应了表里的各个字段,一般不会说在建模的时候,order包含了orderItem,一般都是orderItem表中存在orderId字段表示他们之间的关系。
DDD里面不是这样,比较面向对象的思想,要求你去考虑各个class质检的关系,要求你吧有相关系的class给搞成一个聚合
public class Order{
private Long orderId;
//…此处省略几十个order自己的字段
List orderItems;
DeliveryAddress deliveryAddress;
PayType payType;
PaymentDetail paymentDetail;
OrderSnapshot orderSnapshot;
OrderStatus orderStatus;
}
在DDD战术设计里,上面的这套东西就是一个聚合,一个聚合代表了多个class绑定在一起,一个聚合里放在最碗面的最上层的就是聚合根aggregate root。
聚合里面的classes生命周期是一致的,创建的时候一起创建,更新的时候一起更新,删除的时候一起删除,并且放到一个事务里面,保证一起成功/失败
4)贫血模型:以前我们设计domain包里面的class一般都是贫血模型,Order类里面仅包含数据(和表字段一一对应),方法只有getter和setter,没有别的方法。业务逻辑都封装袋service里面,service依赖多个dao进行CRUD。
5)充血模型:聚合是有业务行为的
DDD战术设计最与众不同的就是要把聚合(实体,值对象)设计成充血模型,对于聚合而言要根据业务语义,放进去对应的业务逻辑&行为,符合业务语义
6)领域服务:有一些业务行为没有办法放到聚合里面。order举例,如果我们要对多个order进行运算,这个时候就不能放在order里面去设计这个语义,就需要一个领域服务
public class XXXService{
//包含了一写没法直接放在聚合里面的业务行为,针对多个聚合执行的业务行为
}
7)业务组件:包含一写贴合业务语义的业务组件。比如订单状态机既不属于聚合,也不属于领域服务,他其实就是一个业务组件
8)命令:你的一些核心的业务动作和行为,应该是人驱动的,用来驱动我们领域服务的核心业务逻辑和方法执行。web系统的页面点击 *** 作,通过UI界面,人下发的一些指令,执行一些动作
Command:一般来说对应的都是更新数据一类的动作
Query:一般都是查询类的动作
9)领域事件:Event,OrderPayedEvent(订单已支付的事件)就是驱动和发起履约流程,
订单上下文–>发布订单已支付时间–>履约上下文进行订阅,消费到消息后进行履约
10)仓储:就是把我们的聚合数据,实体,把我们数据和具体的持久层进行一个交互。如果我们要从持久层查询一个聚合出来,此时就可以用聚合对应的仓储进行查询。
可以封装底层的缓存,建立es,nosql,读写mq以及其他的持久层技术,都是封装到仓储的底层,对于领域模型层来说,是不关心具体的底层实现的
11)接口层:对外提供的服务。最外侧的接口层,其他上下文进行交互。
可以是我ssm框架中的springmvc对外提供的controller,用户界面提供的http接口
可以是我们的有界上下文对其他的有界上下文提供的映射和交互的,提供一个内部调用的接口,我们一般命名为XXXApi,通过spring Cloud Netflix(openFeign)或者spring cloud alibaba(dubbo)rpc框架
也可以是别的有界上下文发不出来一个领域事件,发布到各种MQ,我们去进行领域事件的监听
12)应用服务:application service layer.接口层收到了东西(http请求/rpc调用/MQ的event事件),做一些转化(防腐层,把外部的东西转化为内部的一些东西。比如httpRequest/rpc的请求-响应模型,转化为一个内部的command或者query)
应用服务层就是用于进行业务流程编排,订单履约流程,有applicationService进行流程编排,他来负责基于仓储,聚合,领域服务,执行各种动作,把这个业务链路里面的各个环节的动作都完成
13)domaon层:聚合,领域服务,业务组件,仓储

备注:
1)聚合(数据+行为)+领域服务+业务组件–>已经可以把DDD里面的一些东西设计和做出来并且运行起来。
2)想要验证你的DDD做的对不对,关键点在于是否可以用代码还原出一套完整的业务语义,业务流程,业务模型,完全忽略技术细节(底层crud,sql,缓存,mq,es等)。通过找PM进行代码走读,根据英文单词+注释将业务语义和流程理解出来
3)DDD战术设计:聚合(值对象+实体)+仓储(Repository),领域服务,业务组件,命令和查询,领域事件,接口层(controller,api,listener),应用层服务(application service layer)
4)强调点:DDD最核心的就是战术建模和战略建模(high level高层次的。肯定要做,相对来说要好做一些,划分边界,映射关系,通用语言)。难点在于战术建模,达到通过查看代码可以了解业务业务功能流程,战术建模的时候完全忽略技术实现(表设计,持久化/缓存/rpc/MQ用什么)

履约上上下文详细的战术设计:
1.接受订单:保存订单,预分仓,风控拦截,分物流,下库房
2.人工审核:如果风控拦截出现问题,通知人工审核,人工审核,重分仓
详细DDD动作如下=
接受订单 --> 监听订单已支付领域事件 (驱动后面一系列的动作)
保存订单 --> 将订单相关的数据保存在履约上下文的仓储里面
预分仓 --> 调用仓储上下文获取所有的仓库,从所有的仓库里面选择距离收货地址最近的仓库,检查仓库的库存是否充足,锁定仓库库存,把订单分配给仓库
风控拦截 --> 调用风控上线文的风控拦截动作,查看是否有风险需要风控拦截
分物流 --> 调用物流上下文获取所有的物流公司,从所有的物流公司里面选择距离分配仓库最合适的物流公司,对这个物流公司申请电子面单(物流单),把订单分配给物流公司
下库房 --> 调用仓储的上下文的下发订单履约给指定的仓库
通知人工审核 --> 发布出去一个事件和通知
人工审核 --> 重分仓(可选)分物流,下库房
取消订单 --> 发布出去一个订单取消的时间,让订单上下文进行处理
重分仓 --> 指定一个仓库,把订单分配给指定的仓库

第三步:代码落地。===================================================
传统DDD分层:
1)interface:接口层。controller,api,listener组成,直接跟外界进行交互
2)application service:应用服务层。主要是业务流程编排
3)domain层: aggregate,entity,value Object,domain Service,domain event,command,query,business component -->把业务模型&流程&语义,完全用代码还原出来,让代码跟业务吻合,完全不从技术角度,数据库角度去设计和写代码
4)infrastructure:基础设施层repository,主要负责跟具体的基础设施进行交互,跟数据库,MQ,缓存,es,nosql以及其他的外部的技术设施一类的系统交互。偏向于技术流的地方主要在这里

清洁架构:了解即可规则太死没人使用。共分四层,调用严格从外而内。
六边形架构:有借鉴意义。外部各种适配器,内部维护应用程序(领域模型)
CQRS架构:更新和查询是分离的。维护两套存储,当更新动作更新仓储之后,会通过event bus同步给查询使用的仓储,提供查询服务
结论:把DDD四层和六边形架构个思想,CQRS思想结合起来使用,结合实际的业务和需要进行调整

阿里开源你的cola思想和框架,和ddd设计有一些相通之处,不是完全一样的,也都是为了业务系统的设计和建模去做的
最佳实践文档地址:https://blog.csdn.net/significantfrank/article/details/110934799
github地址:https://github.com/alibaba/COLA
快速创建cola结构地址:https://start.aliyun.com/bootstrap.html

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5719184.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存