幼儿饰品瑜伽美体用品微软
投稿投诉
微软创意
爱情通信
用品婚姻
爱好看病
美体软件
影音星座
瑜伽周边
星座办公
饰品塑形
搞笑减肥
幼儿两性
智家潮品

迄今为止最完整的DDD实践

  作者:章磊(章三)阿里飞猪技术团队一、为什么需要DDD
  对于一个架构师来说,在软件开发中如何降低系统复杂度是一个永恒的挑战。复杂系统设计:系统多,业务逻辑复杂,概念不清晰,有什么合适的方法帮助我们理清楚边界,逻辑和概念多团队协同:边界不清晰,系统依赖复杂,语言不统一导致沟通和理解困难。有没有一种方式把业务和技术概念统一,大家用一种语言沟通。例如:航程是大家所理解的航程吗?设计与实现一致性:PRD,详细设计和代码实现天差万别。有什么方法可以把业务需求快速转换为设计,同时还要保持设计与代码的一致性?架构统一,可复用资产和扩展性:当前取决于开发的同学具备很好的抽象能力和高编程的技能。有什么好的方法指导我们做抽象和实现。二、DDD的价值边界清晰的设计方法:通过领域划分,识别哪些需求应该在哪些领域,不断拉齐团队对需求的认知,分而治之,控制规模。统一语言:团队在有边界的上下文中有意识地形成对事物进行统一的描述,形成统一的概念(模型)。业务领域的知识沉淀:通过反复论证和提炼模型,使得模型必须与业务的真实世界保持一致。促使知识(模型)可以很好地传递和维护。面向业务建模:领域模型与数据模型分离,业务复杂度和技术复杂度分离。三、DDD架构3。1分层架构
  用户接口层:调用应用层完成具体用户请求。包含:controller,远程调用服务等应用层App:尽量简单,不包含业务规则,而只为了下一层中的领域对象做协调任务,分配工作,重点对领域层做编排完成复杂业务场景。包含:AppService,消息处理等领域层Domain:负责表达业务概念和业务逻辑,领域层是系统的核心。包含:模型,值对象,域服务,事件基础层:对所有商城提供技术能力,包括:数据操作,发送消息,消费消息,缓存等调用关系:用户接口层应用层领域层基础层依赖关系:用户接口层应用层领域层基础层3。2六边形架构
  六边形架构:系统通过适配器的方式与外部交互,将应用服务于领域服务封装在系统内部分层架构:它依然是分层架构,它核心改变的是依赖关系。领域层依赖倒置:领域层依赖基础层倒置成基础层依赖领域层,这个简单的变化使得领域层不依赖任务层,其他层都依赖领域层,使得领域层只表达业务逻辑且稳定。3。3调用链路
  四、DDD的基本概念4。1领域模型
  领域(战略):业务范围,范围就是边界。
  子领域:领域可大可小,我们将一个领域进行拆解形成子领域,子领域还可以进行拆解。当一个领域太大的时候需要进行细化拆解。
  模型(战术):基于某个业务领域识别出这个业务领域的聚合,聚合根,界限上下文,实体,值对象。4。1。1核心域
  决定产品和公司核心竞争力的子域是核心域,它是业务成功的主要因素和公司的核心竞争力。直接对业务产生价值。4。1。2通用域
  没有太多个性化的诉求,同时被多个子域使用的通用功能子域是通用域。例如,权限,登陆等等。间接对业务产生价值。4。1。3支撑域
  支撑其他领域业务,具有企业特性,但不具有通用性。间接对业务产生价值。4。1。4为什么要划分核心域、通用域和支撑域
  一个业务一定有他最重要的部分,在日常做业务判断和需求优先级判断的时候可以基于这个划分来做决策。例如:一个交易相关的需求和一个配置相关的需求排优先级,很明显交易是核心域,规则是支持域。同样我们认为是支撑域或者通用域的在其他公司可能是核心域,例如权限对于我们来说是通用域,但是对于专业做权限系统的公司,这个是核心域。4。2限界上下文(战略)
  业务的边界的划分,这个边界可以是一个领域或者多个领域的集合。复杂业务需要多个域编排完成一个复杂业务流程。限界上下文可以作为微服务划分的方法。其本质还是高内聚低耦合,只是限界上下文只是站在更高的层面来进行划分。如何进行划分,我的方法是一个界限上下文必须支持一个完整的业务流程,保证这个业务流程所涉及的领域都在一个限界上下文中。4。3实体(ENTITY)
  定义:实体有唯一的标识,有生命周期且具有延续性。例如一个交易订单,从创建订单我们会给他一个订单编号并且是唯一的这就是实体唯一标识。同时订单实体会从创建,支付,发货等过程最终走到终态这就是实体的生命周期。订单实体在这个过程中属性发生了变化,但订单还是那个订单,不会因为属性的变化而变化,这就是实体的延续性。
  实体的业务形态:实体能够反映业务的真实形态,实体是从用例提取出来的。领域模型中的实体是多个属性、操作或行为的载体。
  实体的代码形态:我们要保证实体代码形态与业务形态的一致性。那么实体的代码应该也有属性和行为,也就是我们说的充血模型,但实际情况下我们使用的是贫血模型。贫血模型缺点是业务逻辑分散,更像数据库模型,充血模型能够反映业务,但过重依赖数据库操作,而且复杂场景下需要编排领域服务,会导致事务过长,影响性能。所以我们使用充血模型,但行为里面只涉及业务逻辑的内存操作。
  实体的运行形态:实体有唯一ID,当我们在流程中对实体属性进行修改,但ID不会变,实体还是那个实体。
  实体的数据库形态:实体在映射数据库模型时,一般是一对一,也有一对多的情况。4。4值对象(VALUEOBJECT)
  定义:通过对象属性值来识别的对象,它将多个相关属性组合为一个概念整体。在DDD中用来描述领域的特定方面,并且是一个没有标识符的对象,叫作值对象。值对象没有唯一标识,没有生命周期,不可修改,当值对象发生改变时只能替换(例如String的实现)
  值对象的业务形态:值对象是描述实体的特征,大多数情况一个实体有很多属性,一般都是平铺,这些数据进行分类和聚合后能够表达一个业务含义,方便沟通而不关注细节。
  值对象的代码形态:实体的单一属性是值对象,例如:字符串,整型,枚举。多个属性的集合也是值对象,这个时候我们把这个集合设计为一个CLASS,但没有ID。例如商品实体下的航段就是一个值对象。航段是描述商品的特征,航段不需要ID,可以直接整体替换。商品为什么是一个实体,而不是描述订单特征,因为需要表达谁买了什么商品,所以我们需要知道哪一个商品,因此需要ID来标识唯一性。
  我们看一下下面这段代码,person这个实体有若干个单一属性的值对象,比如Id、name等属性;同时它也包含多个属性的值对象,比如地址address。
  值对象的运行形态:值对象创建后就不允许修改了,只能用另外一个值对象来整体替换。当我们修改地址时,从页面传入一个新的地址对象替换调用person对象的地址即可。如果我们把address设计成实体,必然存在ID,那么我们需要从页面传入的地址对象的ID与person里面的地址对像的ID进行比较,如果相同就更新,如果不同先删除数据库在新增数据。
  值对象的数据库形态:有两种方式嵌入式和序列化大对象。
  案例1:以属性嵌入的方式形成的人员实体对象,地址值对象直接以属性值嵌入人员实体中。
  当我们只有一个地址的时候使用嵌入式比较好,如果多个地址必须有序列化大对象。同时可以支持搜索。
  案例2:以序列化大对象的方式形成的人员实体对象,地址值对象被序列化成大对象Json串后,嵌入人员实体中。
  支持多个地址存储,不支持搜索。
  值对象的优势和局限:
  1。简化数据库设计,提升数据库操作的性能(多表新增和修改,关联表查询)
  2。虽然简化数据库设计,但是领域模型还是可以表达业务
  3。序列化的方式会使搜索实现困难(通过搜索引擎可以解决)4。5聚合和聚合根
  多个实体和值对象组成的我们叫聚合,聚合的内部一定的高内聚。这个聚合里面一定有一个实体是聚合根。
  聚合与领域的关系:聚合也是范围的划分,领域也是范围的划分。领域与聚合可以是一对一,也可以是一对多的关系
  聚合根的作用是保证内部的实体的一致性,对外只需要对聚合根进行操作。4。6限界上下文,域,聚合,实体,值对象的关系
  领域包含限界上下文,限界上下文包含子域,子域包含聚合,聚合包含实体和值对象4。7事件风暴参与者
  除了领域专家,事件风暴的其他参与者可以是DDD专家、架构师、产品经理、项目经理、开发人员和测试人员等项目团队成员事件风暴准备的材料
  一面墙和一支笔。事件风暴的关注点
  在领域建模的过程中,我们需要重点关注这类业务的语言和行为。比如某些业务动作或行为(事件)是否会触发下一个业务动作,这个动作(事件)的输入和输出是什么?是谁(实体)发出的什么动作(命令),触发了这个动作(事件)我们可以从这些暗藏的词汇中,分析出领域模型中的事件、命令和实体等领域对象。
  实体执行命令产生事件。业务场景的分析
  通过业务场景和用例找出实体,命令,事件。领域建模
  领域建模时,我们会根据场景分析过程中产生的领域对象,比如命令、事件等之间关系,找出产生命令的实体,分析实体之间的依赖关系组成聚合,为聚合划定限界上下文,建立领域模型以及模型之间的依赖。领域模型利用限界上下文向上可以指导微服务设计,通过聚合向下可以指导聚合根、实体和值对象的设计。五、如何建模
  用例场景梳理:就是一句话需求,但我们需要把一些模糊的概念通过对话的方式逐步得到明确的需求,在加以提炼和抽象。建模方法论:词法分析(找名词和动词),领域边界模型验证5。1协同单自动化分单案例5。1。1领域建模
  需求:我们需要把系统自动化失败转人工订单自动分配给小二,避免人工挑单和抢单,通过自动分配提升整体履约处理效率。产品小A:把需求读了一遍。。。。。。。。开发小B:那就是将履约单分配给个小二对吧?产品小A:不对,我们还需要根据一个规则自动分单,例如退票订单分给退票的小二开发小B:恩,那我们可以做一个分单规则管理。例如:新增一个退票分单规则,在里面添加一批小二工号。履约单基于自身属性去匹配分单规则并找到一个规则,然后从分单规则里面选择一个小二工号,履约单写入小二工号即可。
  产品小A:分单规则还需要有优先级,其中小二如果上班了才分配,如果下班了就不分配。开发小B:优先级没有问题,在匹配分单规则方法里面按照优先级排序即可,不影响模型。而小二就不是简单一个工号维护在分单规则中,小二有状态了。
  产品小A:分单规则里面添加小二操作太麻烦了,例如:每次新增一个规则都要去挑人,人也不一定记得住,实际客服在管理小二的时候是按照技能组管理的。开发小B:恩,懂了,那就是通过新增一个技能组管理模块来管理小二。然后在通过分单规则来配置1个技能组即可。获取一个小二工号就在技能组里面了。
  开发小B:总感觉不对,因为新增一个自动化分单需求,履约单就依赖了分单规则,履约单应该是一个独立的域,分单不是履约的能力,履约单实际只需要知道处理人是谁,至于怎么分配的他不太关心。应该由分单规则基于履约单属性找匹配一个规则,然后基于这个规则找到一个小二。履约单与分单逻辑解耦。
  产品小A:分单要轮流分配或者能者多劳分配,小二之前处理过的订单和航司优先分配。开发小B:获取小二的逻辑越来越复杂了,实际技能组才是找小二的核心,分单规则核心是通过履约单特征得到一个规则结果(技能组ID,分单策略,特征规则)。技能组基于分单规则的结果获得小二工号。
  产品小A:还漏了一个信息,就是履约单会被多次分配的情况,每一个履约环节都可能转人工,客服需要知道履约单被处理多次的情况开发小B:那用履约单无法表达了,我们需要新增一个概念叫协同单,协同单是为了协同履约单,通过协同推进履约单的进度。
  产品小A:协同单概念很好,小二下班后,如果没有处理完,还可以转交给别人。开发小B:恩,那只需要在协同单上增加行为即可
  5。1。2领域划分
  沟通的过程就是推导和验证模型的过程,最后进行域的划分:
  5。1。3场景梳理
  穷举所有场景,重新验证模型是否可以覆盖所有场景。
  场景名称
  锁
  场景动作
  域
  域服务
  事件
  聚合根
  方法
  创建协同单
  无
  1、判断关联业务单是否非法
  协同单创建协同单
  1、问题分类是否符合条件
  (例如:商家用户发起自营商家的协同单)
  2、save
  协同单
  创建协同单
  分配协同单
  协同单ID分配协同单到人。
  1、判断协同单状态(待处理)
  2、记录操作日志
  3、save
  协同单
  分配协同单
  协同单
  分配协同单
  受理协同单
  协同单ID
  处理协同单
  协同单受理协同单
  1。判断订单状态(待处理验收失败)
  2。更改订单状态(待处理验收失败处理中)
  3。记录操作日志
  4。save
  协同单
  受理协同单
  转交协同单
  协同单ID
  转交协同单
  协同单转交协同单
  1。判断订单状态。(处理中、待处理)校验转交的人是否在正确的组织下面
  2。更改协同人值对象(同一组织下的不同人,从坐席管理域中取)
  3。记录操作日志
  4。save
  协同单
  转交协同单
  关闭协同单
  协同单ID
  关闭协同单
  协同单关闭协同单
  1。判断订单状态
  (处理中、待处理)
  2。更改订单状态
  (关闭)
  3。记录操作日志
  4。save
  协同单
  关闭协同单
  处理协同单
  协同单ID
  处理协同单
  协同单处理协同单
  1。判断订单状态
  (处理中)
  2。更改订单状态(处理中待验收)
  3。记录操作日志
  4。save
  协同单
  处理协同单
  驳回协同单
  协同单ID
  驳回协同单
  协同单驳回协同单
  1。判断订单状态
  (待验收)
  2。更改订单状态(待验收处理中)
  3。记录操作日志
  4。save
  协同单
  驳回协同单
  完结协同单
  协同单ID
  完结协同单
  协同单完结协同单
  1。判断订单状态
  (待验收)
  2。更改订单状态(待验收已完结)
  3。记录操作日志
  4。save
  协同单
  完结协同单
  拒绝协同单
  协同单ID
  拒绝协同单
  协同单拒绝协同单
  1。判断订单状态(处理中、待处理)
  2。更改订单状态(已拒绝)
  3。记录操作日志
  4。save
  协同单
  拒绝协同单
  催单
  协同单ID
  催单
  协同单催单
  1。判断订单状态(处理中、待处理)
  2、修改催单值对象
  3、记录操作日志
  4、save
  协同单
  催单六、怎么写代码6。1DDD规范
  每一层都定义了相应的接口主要目的是规范代码:
  application:CRQS模式,ApplicationCmdService是command,ApplicationQueryService是query
  service:是领域服务规范,其中定义了DomainService,应用系统需要继承它。
  model:是聚合根,实体,值对象的规范。Aggregate和BaseAggregate:聚合根定义Entity和BaseEntity:实体定义Value和BaseValue:值对象定义Param和BaseParam:领域层参数定义,用作域服务,聚合根和实体的方法参数Lazy:描述聚合根属性是延迟加载属性,类似与hibernate。Field:实体属性,用来实现updatetracing实体属性,updatetracingparamTpublicfinalclassFieldTimplementsChangeable{privatebooleanchangedfalse;privateTvalue;privateField(Tvalue){this。valuevalue;}publicvoidsetValue(Tvalue){if(!equalsValue(value)){this。changedtrue;}this。valuevalue;}OverridepublicbooleanisChanged(){returnchanged;}publicTgetValue(){returnvalue;}publicbooleanequalsValue(Tvalue){if(this。valuenullvaluenull){returntrue;}if(this。valuenull){returnfalse;}if(valuenull){returnfalse;}returnthis。value。equals(value);}publicstaticTFieldTbuild(Tvalue){returnnewFieldT(value);}}
  repositoryRepository:仓库定义AggregateRepository:聚合根仓库,定义聚合根常用的存储和查询方法
  event:事件处理
  exception:定义了不同层用的异常AggregateException:聚合根里面抛的异常RepositoryException:基础层抛的异常EventProcessException:事件处理抛的6。2工程结构
  6。2。1application模块
  CRQS模式:commad和query分离。重点做跨域的编排工作,无业务逻辑6。2。2domain模块
  域服务,聚合根,值对象,领域参数,仓库定义6。2。3infrastructurre模块
  所有技术代码在这一层。mybatis,redis,mq,job,opensearch代码都在这里实现,domain通过依赖倒置不依赖这些技术代码和JAR。6。2。4client模块
  对外提供服务
  6。2。5model模块
  内外都要用的共享对象
  6。3代码示例6。3。1application示例publicinterfaceCaseAppFacadeextendsApplicationCmdService{接手协同单paramhandleCaseDtoreturnResultDOVoidhandle(HandleCaseDtohandleCaseDto);}publicclassCaseAppImplimplementsCaseAppFacade{ResourceprivateCaseServicecaseService;域服务ResourceCaseAssemblercaseAssembler;DTO转ParamOverridepublicResultDOVoidhandle(HandleCaseDtohandleCaseDto){try{ResultDOVoidresultDOcaseService。handle(caseAssembler。from(handleCaseDto));if(resultDO。isSuccess()){pushMsg(handleCaseDto。getId());returnResultDO。buildSuccessResult(null);}returnResultDO。buildFailResult(resultDO。getMsg());}catch(Exceptione){returnResultDO。buildFailResult(e。getMessage());}}}mapstruct:VO,DTO,PARAM,DO,PO转换非常方便,代码量大大减少。CaseAppImpl。handle调用域服务caseService。handle6。3。2domainService示例publicinterfaceCaseServiceextendsDomainService{接手协同单paramhandleParamreturnResultDOVoidhandle(HandleParamhandleParam);}publicclassCaseServiceImplimplementsCaseService{ResourceprivateCoordinationRepositorycoordinationRepository;OverridepublicResultDOVoidhandle(HandleParamhandleParam){SyncLocklocknull;try{lockcoordinationRepository。syncLock(handleParam。getId()。toString());if(nulllock){returnResultDO。buildFailResult(协同单handle加锁失败);}CaseAggregatecaseAggregatecoordinationRepository。query(handleParam。getId());caseAggregate。handle(handleParam。getFollowerValue());coordinationRepository。save(caseAggregate);returnResultDO。buildSuccessResult(null);}catch(RepositoryExceptionAggregateExceptione){StringmsgLOG。error4Tracer(OpLogConstant。traceId(handleParam。getId()),e,协同单handle异常);returnResultDO。buildFailResult(msg);}finally{if(null!lock){coordinationRepository。unlock(lock);}}}}领域层不依赖基础层的实现:coordinationRepository只是接口,在领域层定义好,由基础层依赖领域层实现这个接口业务逻辑和技术解耦:域服务这层通过调用coordinationRepository和聚合根将业务逻辑和技术解耦。聚合根的方法无副作用:聚合根的方法只对聚合根内部实体属性的改变,不做持久化动作,可反复测试。模型与数据分离:改变模型:caseAggregate。handle(handleParam。getFollowerValue());改变数据:coordinationRepository。save(caseAggregate);事务是在save方法上6。3。3Aggregate,Entity示例publicclassCaseAggregateextendsBaseAggregateimplementsNoticeMsgBuilder{privatefinalCaseEntitycaseEntity;publicCaseAggregate(CaseEntitycaseEntity){this。caseEntitycaseEntity;}接手协同单paramfollowerValuereturnpublicvoidhandle(FollowerValuefollowerValue)throwsAggregateException{try{this。caseEntity。handle(followerValue);}catch(Exceptione){throwe;}}}publicclassCaseEntityextendsBaseEntity{创建时间privateFieldDategmtCreate;修改时间privateFieldDategmtModified;问题分类privateFieldLongcaseType;是否需要支付privateFieldBooleanneedPayFlag;是否需要自动验收通过协同单privateFieldIntegerautoAcceptCoordinationFlag;发起协同人值对象privateFieldCreatorValuecreatorValue;跟进人privateFieldFollowerValuefollowerValue;状态privateFieldCaseStatusEnumstatus;关联协同单idprivateFieldStringrelatedCaseId;关联协同单类型see读配置com。alitrip。agent。business。flight。common。model。dataobject。CoordinationCaseTypeDOprivateFieldStringrelatedBizType;支付状态privateFieldPayStatusEnumpayStatus;省略。。。。publicCaseFeatureValuegetCaseFeatureValue(){returnget(caseFeatureValue);}publicBooleanisCaseFeatureValueChanged(){returncaseFeatureValue。isChanged();}publicvoidsetCaseFeatureValue(CaseFeatureValuecaseFeatureValue){this。caseFeatureValueset(this。caseFeatureValue,caseFeatureValue);}publicBooleanisPayStatusChanged(){returnpayStatus。isChanged();}publicBooleanisGmtCreateChanged(){returngmtCreate。isChanged();}publicBooleanisGmtModifiedChanged(){returngmtModified。isChanged();}publicBooleanisCaseTypeChanged(){returncaseType。isChanged();}省略。。。。接手publicvoidhandle(FollowerValuefollowerValue)throwsAggregateException{if(isWaitProcess()isAppointProcess()){this。setFollowerValue(followerValue);this。setStatus(CaseStatusEnum。PROCESSING);this。setGmtModified(newDate());initCaseRecordValue(CaseActionNameEnum。HANDLE,null,followerValue);}else{throwStatusAggregateException();}}省略。。。。}
  充血模型VS贫血模型:充血模型:表达能力强,代码高内聚,领域内封闭,聚合根内部结构对外不可见,通过聚合根的方法访问,适合复杂企业业务逻辑。贫血模型:业务复杂之后,逻辑散落到大量方法中。
  规范大于技巧:DDD架构可以避免引入一些其他概念,系统只有域,域服务,聚合根,实体,值对象,事件来构建系统。
  聚合根的reconProcess的方法的业务逻辑被reconHandler和reconRiskHandler处理,必然这些handler要访问聚合根里面的实体的属性,那么逻辑就会散落。修改后:
  没有引入其他概念,都是在聚合根里面组织实体完成具体业务逻辑,去掉了handler这种技术语言。聚合根和实体定义的方法是具备单一原则,复用性原则与使用场景无关,例如:不能定义手工创建协调单和系统自动创建协同单,应该定义创建协同单。Updatetracing:handle方法修改属性后,然后调用coordinationRepository。save(caseAggregate),我们只能全量属性更新。Updatetracing是监控实体的变更。Entiy定义属性通过Field进行包装实现属性的变更状态记录,结合mapstruct转换PO实现Updatetracing。
  修改了mapstruct生成转换代码的源码,修改后生成的代码:
  当属性被改变后就转换到po中,这样就可以实现修改后的字段更新。修改后的mapstruct代码地址:gitgitlab。alibabainc。com:flightagentmapstruct。gitidea的get和set方法自动生成:由于使用field包装,需要自定义get和set生成代码
  6。3。4Repository示例publicinterfaceCoordinationRepositoryextendsRepository{保存更新paramaggregatethrowsRepositoryExceptionvoidsave(CaseAggregateaggregate)throwsRepositoryException;}RepositorypublicclassCoordinationRepositoryImplimplementsCoordinationRepository{Overridepublicvoidsave(CaseAggregateaggregate)throwsRepositoryException{try{聚合根转PO,updatetracing技术CasePOcasePOcaseConverter。toCasePO(aggregate。getCase());CasePOoldCasePOnull;if(aggregate。getCase()。isAppended()){casePOMapper。insert(casePO);aggregate。getCase()。setId(casePO。getId());}else{oldCasePOcasePOMapper。selectByPrimaryKey(casePO。getId());casePOMapper。updateByPrimaryKeySelective(casePO);}发送协同单状态改变消息if(CaseStatusEnum。FINISH。getCode()。equals(casePO。getStatus())CaseStatusEnum。WAITDISTRIBUTION。getCode()。equals(casePO。getStatus())CaseStatusEnum。PROCESSING。getCode()。equals(casePO。getStatus())CaseStatusEnum。APPOINTPROCESS。getCode()。equals(casePO。getStatus())CaseStatusEnum。WAITPROCESS。getCode()。equals(casePO。getStatus())CaseStatusEnum。CLOSE。getCode()。equals(casePO。getStatus())CaseStatusEnum。REJECT。getCode()。equals(casePO。getStatus())CaseStatusEnum。PENDINGACCEPTANCE。getCode()。equals(casePO。getStatus())){FollowerDtofollowerDtonewFollowerDto();followerDto。setCurrentFollowerId(aggregate。getCase()。getFollowerValue()。getCurrentFollowerId());followerDto。setCurrentFollowerGroupId(aggregate。getCase()。getFollowerValue()。getCurrentFollowerGroupId());followerDto。setCurrentFollowerType(aggregate。getCase()。getFollowerValue()。getCurrentFollowerType());followerDto。setCurrentFollowerName(aggregate。getCase()。getFollowerValue()。getCurrentFollowerName());拒绝和关闭都使用CLOSEStringtagCaseStatusEnum。codeOf(casePO。getStatus())。name();if(CaseStatusEnum。REJECT。name()。equals(tag)){tagCaseStatusEnum。CLOSE。name();}statusChangeProducer。send(CaseStatusChangeEvent。build()。setId(casePO。getId())。setFollowerDto(followerDto)。setStatus(aggregate。getCase()。getStatus()。getCode())。setCaseType(aggregate。getCase()。getCaseType())。setOldStatus(null!oldCasePO?oldCasePO。getStatus():null)。setAppointTime(aggregate。getCase()。getAppointTime()),(tag));}操作ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志aif(CollectionUtils。isNotEmpty(aggregate。getCase()。getCaseRecordValue())){CaseRecordValuecaseRecordValueLists。newArrayList(aggregate。getCase()。getCaseRecordValue())。get(0);caseRecordValue。setCaseId(casePO。getId());recordPOMapper。insert(caseConverter。from(caseRecordValue));}}catch(Exceptione){thrownewRepositoryException(,e。getMessage(),e);}}}CoordinationRepository接口定义在领域层CoordinationRepositoryImpl实现在基础层:数据库操作都是基于聚合根操作,保证聚合根里面的实体强一致性。七、最后结束语好的模型,可以沉淀组织资产,不好的模型,逐渐成为负债功能才是表象,模型才是内在建模过程是不断猜想与反驳的过程演化观点是建模过程的基本心智模式

2022欧洲足坛值得关注的夏季重磅转会(2)足球头条足球懂球大会我要上头条厉兵秣马,风云再起,你方唱罢我登场,几家欢喜几家愁,2022欧洲足坛夏季转会大戏拉开帷幕,谁会是你心目中今夏最亮的星?热苏斯(巴西)1997……区分支气管炎和感冒,从这3点判断,避免陷入4个误区,需了解支气管炎、感冒是困扰我们很久的疾病,而在冬春交替的季节,不仅是支气管炎易发的季节,也是人们感冒易发的季节,他们共同的症状咳嗽也常常会不期而至,但是人们对于咳嗽很难会有人往患有支……体坛连连看丨NBA11冠王离世梅西收获生涯第41冠体坛连连看,每日体育新闻早知道!这是小编为你搜罗的今天的体坛最新资讯。NBA11冠王比尔拉塞尔安详离世享年88岁北京时间8月1日凌晨,NBA11冠名宿比尔拉塞尔(1……苹果已停产的12英寸MacBook有望回归?自研ARM芯片立据MacRumors报道,虽然全尺寸的HomePod、12英寸的MacBook和iMacPro都已停产,但有传言称这三款产品都可能在未来回归。苹果正考虑在2024年之前推……努比亚新机7月20日发布,配置外观官方预热7月份已过半,在月初也发布了一批新机,比如ROG6、小米12S系列、真我GT2大师探索版、红魔7S等机型都在上半月所发布了。从这批机型就可以看得出来,主要是游戏手机和影像手机,……罗技与腾讯合作开发游戏掌机PC级3A大作都能玩今日(8月2日)罗技官方宣布与腾讯达成合作,双方将于今年晚些时候推出一款云游戏掌机,支持多种云游戏服务,PC级3A大作都能玩。罗技官方没有公布与腾讯合作的掌机具体细节,只……2022年NBA选秀球员之杰林威廉姆斯姓名:杰林威廉姆斯位置:中锋身高体重:6尺10237磅(2米10107。5公斤)学校:阿肯萨斯大学生日:2002年6月29日模板:马文巴格利三世……突发!苹果iOS15被攻破越狱工具即将发布近日,网传苹果iOS15系统已经被攻破,全球首个适用于IOS15的越狱工具即将亮相。据悉此次该工具的发布方为OdysseyTeam(奥德赛团队),该团队专门为iPhone……尼格买提喝红酒庆祝生日,在北京公然堂食引争议,评论区给出真相5月7日,央视主持人尼格买提在社交平台上晒出一组动态,为自己庆祝生日,还准备了蛋糕和红酒,生活很是滋润。照片中的尼格买提身穿黑色短袖,戴着同色系帽子,下巴处胡子十分明显,……大热天,内裤安全裤,又热又不透气,谁要穿?相信每个女生的衣柜里都有一大堆小裙子,毕竟夏天穿裙子轻盈又凉快,势必要穿整个夏天的节奏!穿上裙子的你,美是美了,可安全却没了保障,稍微不注意:上下楼梯、弯个腰,分分……比亚迪或许只是特斯拉用来与宁德时代电池议价的棋子作为中国新能源汽车的领军企业比亚迪最近成为了市场关注的焦点,尤其是其副总裁接受媒体采访中透露比亚迪即将为特斯拉供应电池的消息曝光后,比亚迪的市值突破了一万亿人民币,更是超越传统……卷土重来!皇马有望签下顶级射手!后防大将或遭扫地出门对于皇马来说,在完成了一桩重要的转会之后,他们看起来已经相当淡定。对于弗洛伦蒂诺来说,他已经完成了这个转会窗口的大部分任务,剩下的任务就是查漏补缺。如果转会市场上出现合适的人选……
图小球玫瑰夏天怎么样浇水注意三点植株更茂盛小球玫瑰是景天科费菜属的多肉植物,也叫龙血景天。小球玫瑰有着精致纤巧的外形,耐寒和耐旱性非常好,并具有很强的繁殖能力,适宜作为护盆草或草坪草。很多花友都特别喜欢让自己的植……西游记徒步雨崩转山转水转佛塔,不为修来世,只为途中与你相遇!清晨作别丽江古城,前往雨崩,即将开启我们的苦旅。一路上经纳帕海,香格里拉大峡谷,虎跳峡,金沙江第一湾,4292垭口,最……图凤仙花怎么养更健康邻居分享了三个管理要点凤仙花性喜阳光,怕湿,耐热不耐寒。它们比较喜欢生活在向阳的地势和疏松肥沃的土壤之中,在较贫瘠的土壤中也可生长。凤仙花生存力强,适应性好,一般很少有病虫害。大家都知道凤仙花……图仙人球嫁接蟹爪兰非常简单没经验也不会出错仙人球为仙人科多年生多肉类草本植物。茎呈球形或圆形,绿色,花着生于纵棱刺丛中,仙人球开花一般在清晨或傍晚。这种植物本身是比较好的砧木,上面能嫁接很多的植物。仙人球深受花友……图生石花播种方法全公开把握三点从小白变行家有些爱好者反映生石花太难种,特别中国部分地区夏季多闷热潮湿的天气,栽培很容易失败。其实,养护生石花的乐趣之一就是播种,只要掌握了一些基础知识就能成功。生石花深受花友们的喜……邵阳新宁风车翻滚腾碧浪云雾缭绕似仙境4月16日,一场春雨过后,新宁黄金瑶族乡风雨殿风力发电场雾气弥漫,云海翻滚,出现美轮美奂的风车云海景观,风车翻滚腾碧浪,云雾缭绕似仙境。从高处俯瞰,层层叠叠的云雾如波涛海……图碗莲的种植方法知多少完成这四步就能看见属于你自己的美丽花朵你是不是觉得常规的绿色植物盆栽显示不出自己独特的个性呢?那么今天的文章就会介绍如何种植碗莲。只需要很简单的四个步骤,然后你就可以静静的等待碗莲花的盛开了。现在越来越多的人……图胧月多肉的养殖技巧浇水不要太频繁胧月是景天科风车草属多肉植物,常绿垂吊亚灌木,叶肥厚无柄。它们原产于墨西哥,非常适宜生长在排水良好的沙壤土上。这种植物喜欢光照充足的环境,缺光会徒长。胧月是多肉植物中广为……C罗大儿子变男子汉!帮爸爸安慰后妈,乔治娜迷你罗长大懂事了C罗原本已经有了4个孩子,包括大儿子迷你罗,一对双胞胎和乔治娜所生的小女儿。在去年年底,C罗和乔治娜甜蜜宣布他们将迎来一对双胞胎。可惜最后只有女儿顺利出生。虽然刚刚经历了失去家……双胞胎一个给奶奶带,一个给姥姥带,五年后见差距,两种不同的好由于二胎政策的开放,使得很多家庭都希望生两个孩子。但生两个孩子之后,我们的生活就变得非常繁忙,因此我们根本就没有办法把所有的精力全部都投入到孩子的身上,所以我们就会让孩子的奶奶……图多肉植物多久浇一次水为宜多种因素全面分析多肉植物的浇水问题除了困扰着初学者,甚至很多老手都一直没搞明白。因为影响浇水的因素实在的太多了,不过如果从内在和外在两方面进行分析,是可以解决很多的疑问的。多肉植物的浇水……郭富城老婆深夜晒美照,穿短裤大秀美腿,气质冷艳丝毫不输明星2月22日,被称为2022最有爱的一天,这天天王嫂方媛卡点在社交平台晒出一组美照,并配了一颗爱心,疑似隔空示爱老公郭富城,引发网友关注。照片中,方媛打扮精致,身穿黑色爱心……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网