基于DDD的微服务落地
DDD四层架构
对实时性要求高的强一致性业务场景,可采取分布式事务。分布式事务有性能代价,在设计时需要平衡考虑业务拆分、数据一致性、性能和实现的复杂度,尽量避免分布式事务的产生。
领域事件驱动的异步方式是分布式架构常用的设计方式,可以解决非实时性场景下的数据最终一致性问题。基于消息中间件的领域事件发布和订阅,可以很好的解耦微服务。通过削峰填谷,实现读写分离、减轻数据库实时访问压力,提高业务吞吐量和业务处理能力。
DDD设计思想和方法
DDD设计思想和方法
聚合根
聚合根包括聚合根属性、关联的实体和值对象以及自身的业务行为等。通过引用实体和值对象,协调聚合内的多个实体,在聚合根类方法中完成多实体的复杂业务逻辑。
充血模型:即领域模型模式。有自己的业务行为(方法),如下充血模型中提供getDuration、addHistoryApprovalInfo等方法。
贫血模型:即事务脚本模式。只有对象属性和settergetter。DatapublicclassLeave{Stringid;Applicantapplicant;Approverapprover;LeaveTypetype;Statusstatus;DatestartTime;DateendTime;longduration;审批领导的最大级别intleaderMaxLevel;ApprovalInfocurrentApprovalInfo;ListhistoryApprovalInfos;publiclonggetDuration(){returnendTime。getTime()startTime。getTime();}publicLeaveaddHistoryApprovalInfo(ApprovalInfoapprovalInfo){if(nullhistoryApprovalInfos)historyApprovalInfosnewArrayList();this。historyApprovalInfos。add(approvalInfo);returnthis;}publicLeavecreate(){this。setStatus(Status。APPROVING);this。setStartTime(newDate());returnthis;}publicLeaveagree(ApprovernextApprover){this。setStatus(Status。APPROVING);this。setApprover(nextApprover);returnthis;}publicLeavereject(Approverapprover){this。setApprover(approver);this。setStatus(Status。REJECTED);this。setApprover(null);returnthis;}publicLeavefinish(){this。setApprover(null);this。setStatus(Status。APPROVED);this。setEndTime(newDate());this。setDuration(this。getEndTime()。getTime()this。getStartTime()。getTime());returnthis;}}
实体
实体有自己的属性和关联的值对象。DatapublicclassApprovalInfo{StringapprovalInfoId;Approverapprover;ApprovalTypeapprovalType;Stringmsg;longtime;}
值对象publicenumStatus{APPROVING,APPROVED,REJECTED}DataAllArgsConstructorNoArgsConstructorBuilderpublicclassApprover{StringpersonId;StringpersonName;intlevel;publicstaticApproverfromPerson(Personperson){ApproverapprovernewApprover();approver。setPersonId(person。getPersonId());approver。setPersonName(person。getPersonName());approver。setLevel(person。getRoleLevel());returnapprover;}}
领域对象
如果一个业务行为由多个实体对象参与完成,就将该业务逻辑放在领域服务中实现。
实体方法:完成单一实体自身的业务逻辑,是相对简单的原子业务逻辑。
领域服务:由多个实体组合的相对复杂的业务逻辑。ServiceSlf4jpublicclassLeaveDomainService{AutowiredEventPublishereventPublisher;AutowiredILeaveRepositoryiLeaveRepository;AutowiredLeaveFactoryleaveFactory;TransactionalpublicvoidcreateLeave(Leaveleave,intleaderMaxLevel,Approverapprover){leave。setLeaderMaxLevel(leaderMaxLevel);leave。setApprover(approver);leave。create();iLeaveRepository。save(leaveFactory。createLeavePO(leave));LeaveEventeventLeaveEvent。create(LeaveEventType。CREATEEVENT,leave);iLeaveRepository。saveEvent(leaveFactory。createLeaveEventPO(event));eventPublisher。publish(event);}TransactionalpublicvoidupdateLeaveInfo(Leaveleave){LeavePOpoleaveRepositoryInterface。findById(leave。getId());if(nullpo){thrownewRuntimeException(leave不存在);}iLeaveRepository。save(leaveFactory。createLeavePO(leave));}TransactionalpublicvoidsubmitApproval(Leaveleave,Approverapprover){LeaveEventevent;if(ApprovalType。REJECTleave。getCurrentApprovalInfo()。getApprovalType()){leave。reject(approver);eventLeaveEvent。create(LeaveEventType。REJECTEVENT,leave);}else{if(approver!null){leave。agree(approver);eventLeaveEvent。create(LeaveEventType。AGREEEVENT,leave);}else{leave。finish();eventLeaveEvent。create(LeaveEventType。APPROVEDEVENT,leave);}}leave。addHistoryApprovalInfo(leave。getCurrentApprovalInfo());iLeaveRepository。save(leaveFactory。createLeavePO(leave));iLeaveRepository。saveEvent(leaveFactory。createLeaveEventPO(event));eventPublisher。publish(event);}publicLeavegetLeaveInfo(StringleaveId){LeavePOleavePOiLeaveRepository。findById(leaveId);returnleaveFactory。getLeave(leavePO);}publicListLeavequeryLeaveInfosByApplicant(StringapplicantId){ListLeavePOleavePOListiLeaveRepository。queryByApplicantId(applicantId);returnleavePOList。stream()。map(leavePOleaveFactory。getLeave(leavePO))。collect(Collectors。toList());}publicListLeavequeryLeaveInfosByApprover(StringapproverId){ListLeavePOleavePOListiLeaveRepository。queryByApproverId(approverId);returnleavePOList。stream()。map(leavePOleaveFactory。getLeave(leavePO))。collect(Collectors。toList());}}
在应用服务组合不同聚合的领域服务时,通过Id或参数传参,尽量避免领域对象传参,以减少聚合之间的耦合度。
领域事件
领域事件基类DatapublicclassDomainEvent{Stringid;Datetimestamp;Stringsource;Stringdata;}ServicepublicclassEventPublisher{publicvoidpublish(LeaveEventevent){sendtoMQmq。send(event);}}
领域事件实体DatapublicclassLeaveEventextendsDomainEvent{LeaveEventTypeleaveEventType;publicstaticLeaveEventcreate(LeaveEventTypeeventType,Leaveleave){LeaveEventeventnewLeaveEvent();event。setId(IdGenerator。nextId());event。setLeaveEventType(eventType);event。setTimestamp(newDate());event。setData(JSON。toJSONString(leave));returnevent;}}
领域事件的执行逻辑:执行业务逻辑,产生领域事件。调用仓储接口,完成业务数据持久化。调用仓储接口,完成事件数据持久化。完成领域事件发布。
仓储模式
仓储接口publicinterfaceILeaveRepository{voidsave(LeavePOleavePO);voidsaveEvent(LeaveEventPOleaveEventPO);LeavePOfindById(Stringid);ListLeavePOqueryByApplicantId(StringapplicantId);ListLeavePOqueryByApproverId(StringapproverId);}
仓储实现RepositorypublicclassLeaveRepositoryImplimplementsILeaveRepository{AutowiredLeaveDaoleaveDao;AutowiredApprovalInfoDaoapprovalInfoDao;AutowiredLeaveEventDaoleaveEventDao;publicvoidsave(LeavePOleavePO){leaveDao。save(leavePO);leavePO。getHistoryApprovalInfoPOList()。forEach(approvalInfoPOapprovalInfoPO。setLeaveId(leavePO。getId()));approvalInfoDao。saveAll(leavePO。getHistoryApprovalInfoPOList());}publicvoidsaveEvent(LeaveEventPOleaveEventPO){leaveEventDao。save(leaveEventPO);}OverridepublicLeavePOfindById(Stringid){returnleaveDao。findById(id)。orElseThrow(()newRuntimeException(leave不存在));}OverridepublicListLeavePOqueryByApplicantId(StringapplicantId){ListLeavePOleavePOListleaveDao。queryByApplicantId(applicantId);leavePOList。forEach(leavePO{ListapprovalInfoPOListapprovalInfoDao。queryByLeaveId(leavePO。getId());leavePO。setHistoryApprovalInfoPOList(approvalInfoPOList);});returnleavePOList;}OverridepublicListLeavePOqueryByApproverId(StringapproverId){ListLeavePOleavePOListleaveDao。queryByApproverId(approverId);leavePOList。forEach(leavePO{ListapprovalInfoPOListapprovalInfoDao。queryByLeaveId(leavePO。getId());leavePO。setHistoryApprovalInfoPOList(approvalInfoPOList);});returnleavePOList;}}
这里为什么没有使用一对多、多对对呢?时间紧任务重或者并发量不高时可以使用,后期并发量起来了后,数据库将成为瓶颈。
JPAHibernate注解:OneToMany、ManyToOne、ManyToMany;
Mybatis注解:
Results(id,value{Result(column,property,jdbcTypeJdbcType。INTEGER),
Result(column,property,javaTypexx。class,oneOne(selectcom。xx。。XxMapper。selectById))
})
或
Results(id,value{Result(property,column),
Result(propertyxxList,javaTypeList。class,manyMany(select),column)});
Mybatisxml:association、collection。
工厂模式
工厂模式将与业务无关的职能从聚合根中剥离,放在工厂中统一创建和初始化。可考虑使用MapStructServicepublicclassLeaveFactory{publicLeavePOcreateLeavePO(Leaveleave){LeavePOleavePOnewLeavePO();leavePO。setId(UUID。randomUUID()。toString());leavePO。setApplicantId(leave。getApplicant()。getPersonId());leavePO。setApplicantName(leave。getApplicant()。getPersonName());leavePO。setApproverId(leave。getApprover()。getPersonId());leavePO。setApproverName(leave。getApprover()。getPersonName());leavePO。setStartTime(leave。getStartTime());leavePO。setStatus(leave。getStatus());ListhistoryApprovalInfoPOListapprovalInfoPOListFromDO(leave);leavePO。setHistoryApprovalInfoPOList(historyApprovalInfoPOList);returnleavePO;}publicLeavegetLeave(LeavePOleavePO){LeaveleavenewLeave();ApplicantapplicantApplicant。builder()。personId(leavePO。getApplicantId())。personName(leavePO。getApplicantName())。build();leave。setApplicant(applicant);ApproverapproverApprover。builder()。personId(leavePO。getApproverId())。personName(leavePO。getApproverName())。build();leave。setApprover(approver);leave。setStartTime(leavePO。getStartTime());leave。setStatus(leavePO。getStatus());ListapprovalInfosgetApprovalInfos(leavePO。getHistoryApprovalInfoPOList());leave。setHistoryApprovalInfos(approvalInfos);returnleave;}publicLeaveEventPOcreateLeaveEventPO(LeaveEventleaveEvent){LeaveEventPOeventPOnewLeaveEventPO();eventPO。setLeaveEventType(leaveEvent。getLeaveEventType());eventPO。setSource(leaveEvent。getSource());eventPO。setTimestamp(leaveEvent。getTimestamp());eventPO。setData(JSON。toJSONString(leaveEvent。getData()));returneventPO;}privateListapprovalInfoPOListFromDO(Leaveleave){returnleave。getHistoryApprovalInfos()。stream()。map(this::approvalInfoPOFromDO)。collect(Collectors。toList());}privateApprovalInfoPOapprovalInfoPOFromDO(ApprovalInfoapprovalInfo){ApprovalInfoPOponewApprovalInfoPO();po。setApproverId(approvalInfo。getApprover()。getPersonId());po。setApproverLevel(approvalInfo。getApprover()。getLevel());po。setApproverName(approvalInfo。getApprover()。getPersonName());po。setApprovalInfoId(approvalInfo。getApprovalInfoId());po。setMsg(approvalInfo。getMsg());po。setTime(approvalInfo。getTime());returnpo;}privateApprovalInfoapprovalInfoFromPO(ApprovalInfoPOapprovalInfoPO){ApprovalInfoapprovalInfonewApprovalInfo();approvalInfo。setApprovalInfoId(approvalInfoPO。getApprovalInfoId());ApproverapproverApprover。builder()。personId(approvalInfoPO。getApproverId())。personName(approvalInfoPO。getApproverName())。level(approvalInfoPO。getApproverLevel())。build();approvalInfo。setApprover(approver);approvalInfo。setMsg(approvalInfoPO。getMsg());approvalInfo。setTime(approvalInfoPO。getTime());returnapprovalInfo;}privateListgetApprovalInfos(ListapprovalInfoPOList){returnapprovalInfoPOList。stream()。map(this::approvalInfoFromPO)。collect(Collectors。toList());}}
服务的组合和编排
应用层的应用服务主要完成领域服务的组合与编排。ServicepublicclassLeaveApplicationService{AutowiredLeaveDomainServiceleaveDomainService;AutowiredPersonDomainServicepersonDomainService;AutowiredApprovalRuleDomainServiceapprovalRuleDomainService;创建一个请假申请并为审批人生成任务paramleavepublicvoidcreateLeaveInfo(Leaveleave){intleaderMaxLevelapprovalRuleDomainService。getLeaderMaxLevel(leave。getApplicant()。getPersonType(),leave。getType()。toString(),leave。getDuration());PersonapproverpersonDomainService。findFirstApprover(leave。getApplicant()。getPersonId(),leaderMaxLevel);leaveDomainService。createLeave(leave,leaderMaxLevel,Approver。fromPerson(approver));}更新请假单基本信息paramleavepublicvoidupdateLeaveInfo(Leaveleave){leaveDomainService。updateLeaveInfo(leave);}提交审批,更新请假单信息paramleavepublicvoidsubmitApproval(Leaveleave){findnextapproverPersonapproverpersonDomainService。findNextApprover(leave。getApprover()。getPersonId(),leave。getLeaderMaxLevel());leaveDomainService。submitApproval(leave,Approver。fromPerson(approver));}publicLeavegetLeaveInfo(StringleaveId){returnleaveDomainService。getLeaveInfo(leaveId);}publicListLeavequeryLeaveInfosByApplicant(StringapplicantId){returnleaveDomainService。queryLeaveInfosByApplicant(applicantId);}publicListLeavequeryLeaveInfosByApprover(StringapproverId){returnleaveDomainService。queryLeaveInfosByApprover(approverId);}}
服务接口的提供
facade门面接口主要抽象出ControllerApi作为OpenFeign调用的接口门面类。authorlyonardoDescriptioncreateTime2020年03月08日15:06:00FeignClient(nameleaveservice,pathleave)publicinterfaceLeaveFeignClient{PostMapping(submit)ResponsesubmitApproval(LeaveDTOleaveDTO);PostMapping({leaveId})ResponsefindById(PathVariableStringleaveId);根据申请人查询所有请假单paramapplicantIdreturnPostMapping(queryapplicant{applicantId})ResponsequeryByApplicant(PathVariableStringapplicantId);根据审批人id查询待审批请假单(待办任务)paramapproverIdreturnPostMapping(queryapprover{approverId})ResponsequeryByApprover(PathVariableStringapproverId)();}
实现类RestControllerRequestMapping(leave)Slf4jpublicclassLeaveApi{AutowiredLeaveApplicationServiceleaveApplicationService;PostMapping(create)publicResponsecreateLeaveInfo(LeaveDTOleaveDTO){LeaveleaveLeaveAssembler。toDO(leaveDTO);leaveApplicationService。createLeaveInfo(leave);returnResponse。ok();}PutMapping(update)publicResponseupdateLeaveInfo(LeaveDTOleaveDTO){LeaveleaveLeaveAssembler。toDO(leaveDTO);leaveApplicationService。updateLeaveInfo(leave);returnResponse。ok();}PostMapping(submit)publicResponsesubmitApproval(LeaveDTOleaveDTO){LeaveleaveLeaveAssembler。toDO(leaveDTO);leaveApplicationService。submitApproval(leave);returnResponse。ok();}PostMapping({leaveId})publicResponsefindById(PathVariableStringleaveId){LeaveleaveleaveApplicationService。getLeaveInfo(leaveId);returnResponse。ok(LeaveAssembler。toDTO(leave));}根据申请人查询所有请假单paramapplicantIdreturnPostMapping(queryapplicant{applicantId})publicResponsequeryByApplicant(PathVariableStringapplicantId){ListLeaveleaveListleaveApplicationService。queryLeaveInfosByApplicant(applicantId);ListLeaveDTOleaveDTOListleaveList。stream()。map(leaveLeaveAssembler。toDTO(leave))。collect(Collectors。toList());returnResponse。ok(leaveDTOList);}根据审批人id查询待审批请假单(待办任务)paramapproverIdreturnPostMapping(queryapprover{approverId})publicResponsequeryByApprover(PathVariableStringapproverId){ListLeaveleaveListleaveApplicationService。queryLeaveInfosByApprover(approverId);ListLeaveDTOleaveDTOListleaveList。stream()。map(leaveLeaveAssembler。toDTO(leave))。collect(Collectors。toList());returnResponse。ok(leaveDTOList);}}
数据组装层
LeaveAssembler
可使用MapStruct做对象转换和组装publicclassLeaveAssembler{publicstaticLeaveDTOtoDTO(Leaveleave){LeaveDTOdtonewLeaveDTO();dto。setLeaveId(leave。getId());dto。setLeaveType(leave。getType()。toString());dto。setStatus(leave。getStatus()。toString());dto。setStartTime(DateUtil。formatDateTime(leave。getStartTime()));dto。setEndTime(DateUtil。formatDateTime(leave。getEndTime()));dto。setCurrentApprovalInfoDTO(ApprovalInfoAssembler。toDTO(leave。getCurrentApprovalInfo()));ListhistoryApprovalInfoDTOListleave。getHistoryApprovalInfos()。stream()。map(historyApprovalInfoApprovalInfoAssembler。toDTO(leave。getCurrentApprovalInfo()))。collect(Collectors。toList());dto。setHistoryApprovalInfoDTOList(historyApprovalInfoDTOList);dto。setDuration(leave。getDuration());returndto;}publicstaticLeavetoDO(LeaveDTOdto){LeaveleavenewLeave();leave。setId(dto。getLeaveId());leave。setApplicant(ApplicantAssembler。toDO(dto。getApplicantDTO()));leave。setApprover(ApproverAssembler。toDO(dto。getApproverDTO()));leave。setCurrentApprovalInfo(ApprovalInfoAssembler。toDO(dto。getCurrentApprovalInfoDTO()));ListhistoryApprovalInfoDTOListdto。getHistoryApprovalInfoDTOList()。stream()。map(ApprovalInfoAssembler::toDO)。collect(Collectors。toList());leave。setHistoryApprovalInfos(historyApprovalInfoDTOList);returnleave;}}
小米霸气占榜,5个纬度性价比机型排行榜,拿下3个第一说到手机性价比,大家都会不约而同的想到小米手机,特别是小米旗下的Redmi手机,没错小米就是手机中的性价比之王,从8月份手机性价比排行榜也可以看出,小米手机性价比是无敌的,5个不同
又一家芯片企业德明利杀入触摸屏市场,主攻中大屏近日刚上市不久的存储主控芯片企业德明利(SZ001309)在半年报中表示,公司已在人机交互触控领域完成初步业务布局,目前,公司已完成自研触摸控制芯片投片,并在此基础上形成针对不同应
盘点三款适合女孩子使用的笔记本电脑,不玩游戏只办公很多数码博主都会给男生推荐电脑,但很少有给女生推荐的。男女生使用电脑的习惯是不同的,我相信学生时代的男生,买一款电脑的目的有很多都在玩游戏上,而女生就并非如此。所以,借此机会给女孩
手机进行直连卫星的时代?没有意外!华为和苹果两家将前后脚发布新手机,而新手机的直连卫星的能力备受瞩目。按照现在流传的信息,华为手机将发布捅破天的技术,而苹果也将随后推出手机的卫星通信能力。华为手机将率先支
环形战争主线12怎么打边境沼泽北通关攻略环形战争主线12边境沼泽北通关攻略流浪骑士与阔剑兵互换位置,阔剑兵移动至草丛,流浪骑士移动至草丛一站位流浪骑士与阔剑兵互换位置二第一回合阔剑兵移动至草丛流浪骑士移动至草丛长弓手移动
橘右京修罗皮肤手感最好?感受枫霜尽之后,玩家直呼真香苦瓜原创,翻版必究!苦瓜电竞让你体验不一样的电竞主播圈!对于王者荣耀玩家来说,每一次峡谷有新皮肤到来的时候,总是能够掀起一波热度,特别是品质优秀的皮肤,热度都是成倍的。这不,这一次
助力高产!科学家解析玉米株高关键基因中国农业科学院生物技术研究所玉米功能基因组团队和作物代谢调控与营养强化团队合作研究,构建出首个玉米全节间时空特异转录调控网络,发掘和解析了关键基因CYP90D1调控玉米节间发育的分
科学家成功将塑料变成钻石文李木子科学家用强大的激光冲击波制造出小钻石。图片来源Shutterstocktonkid用强大的激光对塑料进行爆破可以产生微小的钻石。在行星内部高温高压环境下也可能发生类似的过程
新突破!中国科学家展示新型阿托秒尺度条纹方法提供相干HH脉冲对和TH探头脉冲的干涉仪。图片来源超快科学理化学研究所先进光子学中心和东京大学的研究小组开发了一种新型干涉仪,用于解析由阿托秒脉冲的光学干扰和物质中电子态的量子干扰
融入大自然城市新空间来源人民网人民日报数据来源北京市朝阳区园林绿化局北京奥林匹克公园位于北京市朝阳区,地处城市中轴线北延长线。图为从空中俯瞰北京奥林匹克公园。本报记者雷声摄北京市朝阳区大望京科技商务创
科学家或已破解灯塔水母永生之谜目前人类所知,只有一种名为T。dohrnii的特殊灯塔水母能够永生不死。在最新研究中,科学家将其基因与其它水母进行比对,并从中发掘其长生不老的秘密。几千年来,长生不老一直是许多人生