领域驱动设计与模型驱动开发-PPT课件

上传人:大米 文档编号:568596130 上传时间:2024-07-25 格式:PPT 页数:142 大小:11.24MB
返回 下载 相关 举报
领域驱动设计与模型驱动开发-PPT课件_第1页
第1页 / 共142页
领域驱动设计与模型驱动开发-PPT课件_第2页
第2页 / 共142页
领域驱动设计与模型驱动开发-PPT课件_第3页
第3页 / 共142页
领域驱动设计与模型驱动开发-PPT课件_第4页
第4页 / 共142页
领域驱动设计与模型驱动开发-PPT课件_第5页
第5页 / 共142页
点击查看更多>>
资源描述

《领域驱动设计与模型驱动开发-PPT课件》由会员分享,可在线阅读,更多相关《领域驱动设计与模型驱动开发-PPT课件(142页珍藏版)》请在金锄头文库上搜索。

1、领域驱动设计与模型驱动开发致谢: 此培训材料借鉴了来自参考文献以及互联网的大量资料,部分资料的参考来源未能尽数列举,谨在此对那些在网络中无私分享自己知识的人表达我的衷心感谢!培训内容领域驱动设计简介领域通用语言领域驱动设计的构造块领域驱动设计编程实践CQRS架构模型驱动开发领域驱动设计思想的发展2002年MartinFower在其出版企业应用架构模式中,归纳总结了40多种企业应用架构的设计模式。其中所提到的多种设计模式和概念,如事务脚本、活动记录和领域模型等,对业界产生了深远的影响。2004年著名建模专家EricEvans发表了他最具影响力的著名书籍:Domain-DrivenDesignTa

2、cklingComplexityintheHeartofSoftware(中文译名:领域驱动设计软件核心复杂性应对之道),书中提出了“领域驱动设计(简称DDD)”的概念。2010年GregYoung在“CQRS,TaskBasedUIs,EventSourcingagh!”一文中对BetrandMeyer的CQS模式进行改造,提出CQRS模式。此后JimmyNilsson的ApplyingDomain-DrivenDesignandPatterns、AbelAvram和FloydMarinescu合作的Domain-DrivenDesignQuickly、DanHaywood的Domain-D

3、rivenDesignUsingNakedObjects、以及VaughnVernon的ImplementingDomain-DrivenDesign等书籍的出版,丰富了领域驱动设计的实践和指导。领域驱动设计是什么领域驱动设计事实上针对是OOAD的一个扩展和延伸,DDD基于面向对象分析与设计技术,对技术框架进行了分层规划,同时对每个类进行了策略和类型的划分。nItsasetofprovenmodelingtechniquesespeciallytargetedtocomplexapplications.nItsasetofprinciplesandpracticessupportingthed

4、evelopmentprocess.nItsasetofpatternsthatsupportacleanandcoherentviewofthedomainmodel.nItsasetofpragmaticstrategiesallowingapplicationstoscaleinsizeandcomplexitymaintainingtheirintegrity.领域驱动设计的特性领域驱动设计分层规划(一)领域驱动设计分层规划领域驱动设计分层规划(二)领域驱动设计是对传统N层架构模式的继承和发展大家有疑问的,可以询问和交流大家有疑问的,可以询问和交流可以互相讨论下,但要小声点可以互相讨论

5、下,但要小声点可以互相讨论下,但要小声点可以互相讨论下,但要小声点领域驱动设计分层规划(三)领域驱动设计是对传统N层架构模式的继承和发展CoreJ2EEPatterns例:J2EE参考分层架构传统J2EE或Spring+Hibernate等事务性编程模型只关心数据,这些数据对象除了简单sette/getter方法外,没有任何业务方法,被比喻成“失血模型”。领域驱动设计分层规划(四)分布式领域驱动设计领域驱动设计分层规划(五)分布式领域驱动设计与DotNET技术架构体系之间的关系映射面向对象分析与设计技术面向过程vs.面向对象事务脚本模式把业务逻辑组织成单个过程,在过程中直接调用数据库,业务逻辑

6、在服务(Service)层处理。事务脚本模式的特点是简单容易理解,面向过程设计。对于少量逻辑的业务应用来说,事务脚本模式简单自然,性能良好,容易理解,而且一个事务的处理不会影响其他事务。不过缺点也很明显,对于复杂的业务逻辑处理力不从心,难以保持良好的设计,事务之间的冗余代码不断增多,通过复制粘贴方式进行复用。可维护性和扩展性变差。对类的策略和类型的划分对类进行StereoType(“构造型”)划分的好处在于:(1)指导设计(2)帮助命令对象(3)辅助理解按照策略和类型对类进行划分六边形架构以领域模型为核心的六边形架构领域驱动设计中的设计模式有助于获得柔性设计的设计模式每个元素的名称都提供了一次

7、揭示设计意图的机会。站在客户开发人员的角度上来思考它。人们为了使所有类和操作都具有相似的规模而寻找一种一致的力度。粒度的大小并不是唯一要考虑的问题,我们还要考虑粒度在哪种场合下使用。随着代码重构不断适合新理解的概念或需求,概念轮廓也就逐渐形成了。搞内聚低耦合原则既适用于代码,也适用于概念。领域驱动设计软件核心复杂性应对之道第10章任何对未来操作产生影响的系统状态的改变都可以成为副作用。把命令和查询严格地放到不同操作中;创建并返回ValueObject。允许我们安全地对多个操作进行组合。使用断言把副作用明确表示出来,使它们更易于处理。寻找在概念上内聚的模型,更易推出预期ASSERTION,从而加

8、快学习过程并避免代码矛盾。尽一切可能保持低耦合。把所有无关概念提取到对象之外,类就变成完全孤立的了,使得我们可以单独地研究和理解它。每个孤立类都极大减轻了因理解Module而带来的负担。操作闭合:在适当的情况下,在定义操作时让它的返回类型与其参数相同。闭合操作提供了一个高层接口,同时又不会引入对其他概念的任何依赖性。培训内容领域驱动设计简介领域通用语言领域驱动设计的构造块领域驱动设计编程实践CQRS架构模型驱动开发使用通用语言的重要性Talkingdifferentlanguagesmakesprojectsfail.nProgrammersspeakusingtechnicaljargon(

9、designpatterns,acronyms,geekyin-jokes)nDomainexpertsuseterminologyspecifictotheirfieldofexpertisenComputersspeakprogramminglanguagesn大家必须妥协领域驱动设计的关键点关注核心领域(CoreDomain)领域专家和软件从业者共同开发模型在一个明确的限界上下文(BoundedContext)中使用领域通用语言(ubiquitouslanguage)通用语言(一)通用语言(UBIQUITOUSLANGUAGE)是团队共享的语言。领域专家和开发者使用相同的通用语言进行交流

10、。事实上,团队中每个人都使用相同的通用语言。不管你在团队中的角色如何,只要你是团队的一员,你都将使用通用语言。n通用语言是团队自己创建的公用语言。团队中同时包含领域专家和软件开发人员。n通用语言更多地是关于业务本身如何思考和运作的,领域专家对通用语言有很大影响。不同领域专家会在概念和术语上产生分歧,甚至也会犯错,当领域专家和开发者一起创建领域模型的时候,他们有时会达成一致,有时会做一些妥协,但最终目的都是为了创造最适合项目的通用语言。团队成员们妥协的绝对不应是通用语言的质量,而是概念、术语和含义。最初的一致并不表示始终一致,通用语言也会随着时间推移而不断演化改变。n领域驱动设计的一个核心思想就

11、是使用基于模型的共同语言。因为模型是软件满足领域的共同点,它很适合作为这种通用语言的构造基础。使用模型作为语言的核心骨架,要求团队在进行所有的交流都是使用一致的语言,在代码中也是这样。在共享知识和推敲模型时,团队会使用语言、文字和图形。这儿需要确保团队使用的语言在所有的交流形式中看上去都是一致的,这种语言被称为“通用语言(UbiquitousLanguage)”。n通用语言的词汇表包括类名称和主要操作。语言中包含术语,有些术语用来讨论模型中已经明确的规则,还有一些术语则来自施加于模型上的高级组织原则。最后,团队一致应用于领域模型的模式名称使这种语言更为丰富。模型之间的关系成为所有语言都具有的组

12、合规则,词和短语的意义反映了模型的语义。通用语言(二)在应用通用语言时,应注意:n将模型作为语言的中心。确保团队在所有交流活动和代码中坚持使用这种语言。在画图、写东西特别是讲话时也要使用这种语言。n通过尝试不同的表示方法(它们反映了不同模型)来消除难点。然后重构代码,并对类、方法和模块重新命名,以便与新模型相一致。解决交谈中的术语混淆问题,就像我们对普通词汇形成一个公认的理解一样。n要认识到UBIQUITOUSLANGUAGE中的更改就是对模型的更改。n领域专家应该避免使用拗口或无法表达领域理解的术语或结构,开发人员应该密切监视那些将会妨碍设计的有歧义和不一致的地方有了通用语言,模型就不仅仅是

13、一个设计工作了。它成为开发人员和领域专家共同完成的每项工作中的不可或缺的部分。语言以动态形式传递知识。使用这种语言进行讨论能够更清楚地表达图和代码背后的真实含义。通用语言是那些不以代码形式出现的设计方面的主要载体,这些方面包括把整个系统组织在一起的比例结构、定义了不同系统和模型之间关系的BoundedContext,以及在模型和设计中使用的其他模式。通用语言的应用通用语言贯穿于项目的各个环节nUserStoriesnProjectMeetingsnTeamEmailsnInstantMessagesnSchedulePlannSoftwareDocuments在限界上下文中,保持语言的一致性(

14、如口语、图形(如UML图等)、文字、代码等)。通用语言的应用示例(一)UserStoriesNOWhenUserlogsonwithvalidcredentials,anemptypanelisdisplayed.YESWhenPlayerlogsonwithvalidcredentials,anemptyboardgameisdisplayed.(fromaTicTacToeGamesoftwareexample)通用语言的应用示例(二)CodeExampleNO.Integeri=newInteger();.Stringchar1=newString();.publicclassGameD

15、AO().catch(Exceptione)YES.StringrealMeaningOfMyString=newString();.publicclassScoreDataLoader().catch(ExceptionNotLoggedInException)NO.Ambiguities.Inconsistencies.Synonyms.AbbreviationsYES.Clarity.Precision.Reuse.FullNamespackagetictactoe.client.userInterface;/*AddthestringOorXtoacellinthegrid.*/pub

16、licclassShowCellGridpublicstaticvoiddisplayUser(Gridgrid,Cellcell)if(!Initialization.flag&Initialization.gameStatus.getSequence() = null&isEmpty(grid, cell) Initialization.flag = true;Stringmk=showString(Initialization.gameStatus.getCurrentUser().getUserString();grid.setHTML(cell.getRowIndex(),cell.

17、getCellIndex(),mk);Initialization.gameStatus.getStatus()cell.getRowIndex()cell.getCellIndex()=Initialization.gameStatus.getCurrentUser();GameEnd.checkEnd(Initialization.gameStatus,cell.getRowIndex(),cell.getCellIndex();(.)AclassBEFOREandAFTERUbiquitousLanguagepackagetictactoe.client.userInterface;/*

18、Performsamoveinthegame.*/publicclassPlayerMove/*Whentheplayerclicksinacell,thegamedrawsanOoraXonthe*gamegriddependingonwhichplayersturnitis.*/publicstaticvoidmakeMove(GameGridgameGrid,Cellcell)if(!GameInitialization.waitingMoveFlag&GameInitialization.currentGameStatus.getSequenceWinner() = null &isC

19、ellEmpty(gameGrid, cell) GameInitialization.waitingMoveFlag = true;Stringmarker=showPlayerIcon(GameInitialization.currentGameStatus.getCurrentPlayer().getPlayerIcon();gameGrid.setHTML(cell.getRowIndex(),cell.getCellIndex(),marker);GameInitialization.currentGameStatus.getGameMoves()cell.getRowIndex()

20、cell.getCellIndex()=GameInitialization.currentGameStatus.getCurrentPlayer();CheckWinner.checkForWinner(GameInitialization.currentGameStatus, cell.getRowIndex(),cell.getCellIndex();(.)(ExcerptedfromaTicTacToeGamesourcecode)WhichonewouldaStakeholderbetterunderstand?PlayerMovePerformsamoveinthegame.Mak

21、eMoveWhentheplayerclicksinacell,thegamedrawsanOoraXonthegamegriddependingonwhichplayersturnitis.IsCellEmptyThePlayercanselectacellonlyifitwasntalreadyselected.ShowCellGridAddtheStringOorXtoacellinthegrid.DisplayUserIsEmpty(ExcerptedfromaTicTacToeGamesourcecode)模型的统一模型的内部一致性又叫做“统一”,这样每个术语都不会有模棱两可的意义,

22、也不会有规则冲突。除非模型在逻辑上是一致的,否则它就没有意义。识别限界上下文中的不一致:重复的概念和假同源n重复的概念是指两个模型元素(以及伴随的实现)实际上表示同一个概念。每当这个概念的信息发生改变时,都必须要更新两个地方。每次由于新的知识导致一个对象被修改时,也必须重新分析和修改另一个对象。如果不进行实际的重新分析,结果就会出现同一个概念的两个版本,它们遵守不同的规则,甚至不同的数据。更重要的是,团队成员必须学习同一操作的两种方法,以及保持这两种方法同步的各种方式。n假同源是指使用相同术语(或已实现的对象)的两个人认为他们是在谈论同一件事情,但实际上并不是这样。但是,当两个定义都与同一个领

23、域方面相关,而只是在概念上稍有区别时,这种冲突更难以发现。假同源会导致开发团队互相干扰对方的代码,也可能导致数据库中含有奇怪的矛盾,还会引起团队沟通的混淆。注意用词词汇n注意正确用词,不要歪曲词义n开发人员经常习惯于使用增/删/改/查(CRUD)此类动词词汇,也许有时候它们也确实属于通用语言,但大多数情况下,它们并不能正确反映业务,用词上混淆了业务概念。模型的分裂在理想的世界中,我们可以有一种把整个企业领域包含进来的单一模型;这个模型将是统一的,没有任何相互矛盾或相互重叠的术语定义;每个有关领域的逻辑声明都将是一致的。但大型系统开发并不是这样理想。大型系统领域模型的完全统一是不可行的,也不是一

24、种经济有效的做法。我们可以采用限界上下文(BoundedContext)定义每个模型的应用范围,采用上下文映射(ContextMap)给出项目上下文以及它们之间关系的总体视图。n任何一个大型项目都会存在多个模型。而当基于不同模型的代码被组合到一起后,软件就会出现bug、变得不可靠和难以理解。团队成员之间的沟通变得混乱。人们往往弄不清楚一个模型不应该在哪个上下文中使用。n明确地定义模型所应用的上下文。根据团队的组织、软件系统的各个部分的用法以及物理表现(代码和数据库模式等)来设置模型的边界。在这些边界中严格保持模型的一致性,而不要受到边界之外问题的干扰和混淆。在Context中,要保证模型在逻辑

25、上统一,而不用考虑它是不是适用于边界之外的情况。在其他Context中,会使用其他的模型,这些模型具有不同的术语、概念、规则和UBIQUITOUSLANGUAGE的技术行话。n定义BoundedContext:视察项目的现状,而不是它的理想状态。领域、子域和限界上下文核心域、支撑域和通用域ACore Core Domain omain isapartofthebusinessDomainthatisofprimaryimportancetothesuccessoftheorganization.Itisofutmostimportancetotheongoingsuccessofthebusi

26、ness.Ifadomainmodelssomeaspectofthebusinessthatisessential,yetnotCore,itisaSupporting Supporting SubdomainSubdomain.ifadomaincapturesnothingspecialtothebusiness,yetisrequiredfortheoverallbusinesssolution,itisaGeneric SubdomainGeneric Subdomain.Focusonthecoredomain战术建模与战略建模领域驱动设计的综合应用共享内核(SharedKerne

27、l)当不同团队开发一些紧密相关的应用程序时,如果团队之间不进行协调,即使短时间内能够取得快速进展,他们开发出的产品也可能互相不适合,最后可能不得不在转换层上花费大量时间,而且得到的产品也五花八门。n从领域模型中选出两个团队都同意共享的一个子集。当然,除了模型的这个子集以外,这还包括与该模型部分相关的代码子集,或数据库设计的子集。这部分明确共享的内容具有特殊的状态,而且一个团队在没与另一个团队商量的情况下不应擅自更改它。n功能系统要经常进行集成,但集成的频率应该比团队中ContinuousIntegration的频率低一些。在进行这些集成的时候,两个团队都要运行测试。nSharedKernel通

28、常是CoreDomain,或是一组GenericSubdomain(通用子领域),也可能二者兼有。企业架构方法与领域驱动设计3.架构架构内容内容框架框架 4.企业连续系列企业连续系列1.架构开发架构开发方法方法2.架构开发指引和技术架构开发指引和技术5.参考模型参考模型6.架构架构能力能力框架框架 两者都强调Business和IT的高度统一,很多企业架构方法对于领域驱动设计“战略设计”的具体实施办法具有详实的指导意义。如TOGAFV9构件:eTOM业务建模Level0ProcessesLevel1ProcessesLevel2Processes业务流程解耦/分解eTOM业务建模BSS业务流程框

29、架领域解决特定问题领域解决特定问题eTOM信息数据模型eTOM0级视图SID1级视图ABE:AggregateBusinessEntity,ABE是SID中一组定义良好的实体,具有高内聚、低耦合的特征。共享内核共享内核eTOM信息数据模型参考读物领域驱动设计软件核心复杂性应对之道及实现领域驱动设计中的相关章节软件方法-业务建模和需求第三章“业务建模”中的相关内容参考模型范例:nTMForum的eTOM模型:培训内容领域驱动设计简介领域通用语言领域驱动设计的构造块领域驱动设计编程实践CQRS架构模型驱动开发领域驱动设计的构造块Entity(实体)实体是一个具有唯一身份标识的对象,并且可以在相当长

30、的一段时间内持续地变化。我们可以对实体做多次修改,故一个实体对象可能和它先前的对象大不相同,但是由于它们拥有相同的身份标识(identity),它们依然是同一个实体。我们通过标识对对象进行区分,而不是属性,此时我们应该将标识作为主要的模型定义。同时我们需要保持简单的类定义,并且关注对象在其生命周期中的连续性和唯一标识性。随着对象的改变,我们可能会跟踪这样的改变,比如什么时候发生了改变,发生了什么改变,是谁做出的改变等。我们应该慎重对待在对象整个生命周期中所发生的合法改变。唯一的身份标识和可变性(mutability)特征将实体对象和值对象(ValueObjects)区分开来。n很多时候,一个领

31、域概念应该建模成值对象,而不是实体对象。n实体和值对象是领域模型概念,而不是数据存储模型概念。ValueObjects(值对象)值对象的特征n它度量或者描述了领域中的一件东西。n它可以作为不变量。n它将不同的相关的属性组合成一个概念整体n当度量和描述改变时,可以用另一个值对象予以替换n它可以和其他值对象进行相等性比较n它不会对协作对象造成副作用。当我们只关心一个模型元素的属性时,应把它归类为值对象。我们应该使这个模型元素能够表示出其属性的意义,并为它提供相关功能。值对象应该是不可变的。不要为它分配任何标识,而且不要把它设计成Entity那么复杂。应该尽量使用值对象来建模而不是实体对象,即便一个

32、领域概念必须建模成实体,在设计时也应该更偏向于将其作为值对象容器,而不是子实体容器。实体对象与值对象是领域概念,而不是数据存储模型概念n值对象可以与其所在的实体对象保存在同一张表中,值对象的每一个属性保存为一列;值对象也可以独立于其所在的实体对象保存在另一张表中,值对象获得委派主键,该主键对客户端是不可见的。Entity和ValueObject示例Aggregates(聚合)在具有复杂关联的模型中,要想保证对象更改的一致性是很困难的。不仅互不关联的对象需要遵守一些固定规则,而且紧密关联的各组对象也要遵守一些固定规则。然而,过于谨慎的锁定机制又会导致多个用户之间毫无意义地互相关绕,从而使系统不可

33、用。在任何具有持久化数据存储的系统中,对数据进行修改的事务必须要有一个范围,而且要有一种保持数据一致性的方式。聚合(Aggregate)是一组相关对象的集合,我们把它作为数据修改的单元。每个聚合都有一个根和一个边界,边界定义了聚合的内部都有什么,根则是聚合中所包含的一个特定实体。在聚合中,根是唯一允许外部对象保持对它的引用的元素,而边界内部的对象之间则可以互相引用。除根以外的其他Entity都有本地表示,但这些标识只有在聚合内部才需要加以区别,因为外部对象除了根Entity之外看不到其他对象。聚合行为视为是一个整体,在每个事务完成时,必须要满足聚合内所应用的固定规则的要求,即保证数据变化的一致

34、性。根实体最终检查固定规则;删除操作必须一次删除聚合边界之内的所有对象;当提交对聚合边界内部的任何对象的修改时,整个聚合中的所有固定规则都必须被满足。原则:在一致性边界之内建模真正的不变条件;设计小聚合;通过唯一标识引用其他聚合;在边界之外使用最终一致性尽量将根实体所包含的其他聚合建模成值对象,而不是实体。Aggregates(聚合)示例DomainEvent(领域事件)DomainEvent(领域事件)n有时候应用需要记录跟踪事情的发生n领域事件经常被建模为ValueObject,但这些ValueObject并不能被共享,因为领域事件本身是“唯一”的。n一个领域事件是指一个在领域中“有意义有

35、意义”的事件HintsnUML四色原型中有一个相近概念,称为时刻-时段原型(Moment-interval),即表示事物在某个时刻或某一段时间内发生。参考:四色原型四色原型是诞生于90年代,现在被广泛使用的一种系统分析方法,如Borland的Together架构师版,准确地说,是由PeterCoad和MarkMayfield首先提出,然后由DavidNorth拓展。Repositories(资源库/仓储)客户需要以一种符合实际的方式来获取对以存在的领域对象的引用。为每种需要全局访问的对象类型创建一个对象,这个对象就相当于该类型的所有对象在内存中的一个集合的“替身”。通过一个众所周知的接口来提供

36、访问。提供添加和删除对象的方法,用这些方法来封装在数据存储中实际插入或删除数据的操作。提供根据具体标准来挑选对象的方法,并返回属性值满足查询标准的对象或对象集合(所返回的对象是完全实例化的),从而将实际的存储和查询技术封装起来。只为那些确实需要直接访问的聚合提供Repository。让客户始终聚焦于模型,而将所有对象的存储和访问操作交给Repository来完成。Repository的接口应当采用领域通用语言。作为客户端,不应当知道数据库实现的细节。Repository和DAO的作用类似,二者的主要区别:nDAO是比Repository更低的一层,包含了如何从数据库中提取数据的代码。nRepo

37、sitory以“领域”为中心,所描述的是“领域语言”。Repository把ORM框架与领域模型隔离,对外隐藏封装了数据访问机制。Repositories(资源库/仓储)示例publicinterfaceAccountRepositoryAccountfindAccount(StringaccountId);voidaddAccount(Accountaccount);publicclassHibernateAccountRepositoryimplementsAccountRepositoryprivateHibernateTemplatehibernateTemplate ;publicH

38、ibernateAccountRepository(HibernateTemplatetemplate)hibernateTemplate=template;publicvoidaddAccount(Accountaccount)hibernateTemplate.save(account);publicAccountfindAccount(finalStringaccountId)return(Account)DataAccessUtils.uniqueResult(hibernateTemplate.findByNamedQueryAndNamedParam(“Account.findAc

39、countByAccountId”,“accountId”,accountId);Services(领域服务)当领域中的某个操作过程或转换过程不是实体或值对象的职责时,我们便应该将该操作放在一个单独的接口中,即领域服务。如果勉强地把这些重要的领域功能归为Entity或ValueObject的职责,那么不是歪曲了基于模型的对象的定义,就是人为地增加了一些无意义的对象。应确保领域服务和通用语言是一致的,并且保证它是无状态的。正确区分领域服务(DomainService)和应用服务(ApplicationService):n我们不应把业务逻辑置于应用服务,但我们会把业务逻辑置于领域服务中。(应用)服

40、务要做“薄”。n领域服务职责:跨聚合实例业务逻辑;没办法合理放到实体中的其它业务逻辑。n应用服务职责:跨限界上下文的业务逻辑;DTO转换;事务AOP、权限AOP、日志AOP、异常AOP;外部系统访问(邮件、消息队列)。n领域服务设计原则:用来组织业务逻辑,面向业务逻辑;细粒度;内部视图看系统;一个请求对应多个服务的多个方法;服务之间会存在依赖;n应用服务设计原则:用来封装业务逻辑;面向用例;粗粒度;外部视图看系统;一个请求对应一个方法;服务之间互不依赖。n应用服务和领域服务区分非常敏感,有时候需要在快速性/方便性上做折衷。Services(领域服务)示例publicinterfaceMoney

41、TransferServiceBankingTransactiontransfer(StringfromAccountId,StringtoAccountId,doubleamount);publicclassMoneyTransferServiceImplimplementsMoneyTransferServiceprivatefinalAccountRepositoryaccountRepository;privatefinalBankingTransactionRepositorybankingTransactionRepository;publicMoneyTransferServic

42、eImpl(AccountRepositoryaccountRepository,BankingTransactionRepositorybankingTransactionRepository)BankingTransactiontransfer(StringfromAccountId,StringtoAccountId,doubleamount)应用服务、领域服务和基础设施服务Factories(工厂)当创建一个对象或创建整个聚合时,如果创建工作很复杂,或者暴露了过多的内部结构,则可以使用Factory进行封装。应该将创建复杂对象的实例和聚合的职责转移到一个单独的对象,这个对象本身在领域模

43、型中可能没有职责,但它仍是领域设计的一部分。不同类型的工厂模式:n工厂类n工厂方法Modules(模块)Module为人们提供了两种观察模型的方式,一是可以在Module中查看细节,而不会被整个模型淹没,二是观察Module之间的关系,而不考虑其内部细节。模块之间应该是低耦合的,而在模块内部则是高内聚的。模块并不仅仅是代码的划分,而且也是概念的划分。一个人一次考虑的事情是有限的(因此才有低耦合);不连贯的思想和“一锅粥”似的思想同样难于理解(因此才有高内聚)。选择能够描述系统的Module,并使之包含一个内聚的概念集合。这通常会实现Module之间的低耦合,但如果效果不理想,则应寻找一种更改模

44、型的方式来消除概念之间的耦合,或者找到一个可作为Module基础的概念,基于这个概念组织的模型可以以一种有意义的方式将元素集中到一起。找到一种低耦合的概念组织方式,从而可以相互独立地理解和分析这些概念。对模型进行精化,直到可以根据高层领域概念对模型进行划分,同时相应的代码也不会产生耦合。Module的名称应该是领域通用语言中的术语。模块及其名称应反映出领域的深层知识。培训内容领域驱动设计简介领域通用语言领域驱动设计的构造块领域驱动设计编程实践CQRS架构模型驱动开发概念辨析-VO/DTO/DO/PO(一)ViewObject(视图对象):视图对象,用于展示层,其作用是把某个指定页面(或组件)的

45、所有数据封装起来。DataTransferObject(数据传输对象):这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。DomainObject(领域对象):从现实世界中抽象出来的有形或无形的业务实体、值对象或领域服务。PersistentObject(持久化对象):跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。Ref:概念

46、辨析-VO/DTO/DO/PO(二)VO与DTO:绝大多数应用场景下,VO与DTO的属性值基本一致,但对于设计层面来说,概念上还是存在VO和DTO的区别,DTO代表服务层需要接收的数据和返回的数据,而VO代表展示层需要显示的数据。n示例:服务层有一个getUser的方法返回一个系统用户,其中有一个属性是gender(性别),对于服务层来说,它只从语义上定义:1-男性,2-女性,0-未指定,而对于展示层来说,它可能需要用“帅哥”代表男性,用“美女”代表女性,用“秘密”代表未指定。说到这里,可能你还会反驳,在服务层直接就返回“帅哥美女”不就行了吗?对于大部分应用来说,这不是问题,但设想一下,如果需

47、求允许客户可以定制风格,而不同风格对于“性别”的表现方式不一样,又或者这个服务同时供多个客户端使用(不同门户),而不同的客户端对于表现层的要求有所不同,那么,问题就来了。再者,回到设计层面上分析,从职责单一原则来看,服务层只负责业务,与具体的表现形式无关,因此,它返回的DTO,不应该出现与表现形式的耦合。n实现层面是否需要区分二者概念?具体问题具体分析概念辨析-VO/DTO/DO/PO(三)DTO与DO:DTO是展示层和服务层之间的数据传输对象(可以认为是两者之间的协议),而DO是对现实世界各种业务角色的抽象,这就引出了两者在数据上的区别,例如UserInfo和User,对于一个getUser

48、方法来说,本质上它永远不应该返回用户的密码,因此UserInfo至少比User少一个password的数据。而在领域驱动设计中,DO不是简单的POJO,它具有领域业务逻辑。n在设计层面,展示层向服务层传递的DTO与服务层返回给展示层的DTO在概念上是不同的(如返回UserInfo应该不包含password,但创建User传入的参数需要包含password),但在实现层面,我们通常很少会这样做(定义两个UserInfo,甚至更多),因为这样做并不见得很明智,我们完全可以设计一个完全兼容的DTO,在服务层接收数据的时候,不该由展示层设置的属性(如订单的总价应该由其单价、数量、折扣等决定),无论展示

49、层是否设置,服务层都一概忽略,而在服务层返回数据时,不该返回的数据(如用户密码),就不设置对应的属性。n为什么不在服务层中直接返回DO:DO具有一些不应该让展示层知道的数据;DO具有业务方法,如果直接把DO传递给展示层,展示层的代码就可以绕过服务层直接调用它不应该访问的操作,对于基于AOP拦截服务层来进行访问控制的机制来说,这问题尤为突出,而在展示层调用DO的业务方法也会因为事务的问题,让事务难以控制;ORM框架(如Hibernate)“延迟加载”技术,如果直接把DO暴露给展示层,对于大部分情况,展示层不在事务范围之内,如果其尝试在Session关闭的情况下获取一个未加载的关联对象,会出现运行

50、时异常(对于Hibernate来说,就是LazyInitiliaztionException);从设计层面来说,展示层依赖于服务层,服务层依赖于领域层,如果把DO暴露出去,就会导致展示层直接依赖于领域层,这虽然依然是单向依赖,但这种跨层依赖会导致不必要的耦合。nDTO应该是一个“扁平的二维对象”概念辨析-VO/DTO/DO/PO(四)DO与PO:DO和PO在绝大部分情况下是一一对应的,PO是只含有get/set方法的POJO,但某些场景还是能反映出两者在概念上存在本质的区别。nDO在某些场景下不需要进行显式的持久化,例如利用策略模式设计的商品折扣策略,会衍生出折扣策略的接口和不同折扣策略实现类

51、,这些折扣策略实现类可以算是DO,但它们只驻留在静态内存,不需要持久化到持久层,因此,这类DO是不存在对应的PO的。同样的道理,某些场景下,PO也没有对应的DO,例如老师Teacher和学生Student存在多对多的关系,在关系数据库中,这种关系需要表现为一个中间表,也就对应有一个TeacherAndStudentPO的PO,但这个PO在业务领域没有任何现实的意义,它完全不能与任何DO对应上。这里要特别声明,并不是所有多对多关系都没有业务含义,这跟具体业务场景有关,例如:两个PO之间的关系会影响具体业务,并且这种关系存在多种类型,那么这种多对多关系也应该表现为一个DO,又如:“角色”与“资源”

52、之间存在多对多关系,而这种关系很明显会表现为一个DO“权限”。n某些情况下,为了某种持久化策略或者性能的考虑,一个PO可能对应多个DO,反之亦然。例如客户Customer有其联系信息Contacts,这里是两个一对一关系的DO,但可能出于性能的考虑(极端情况,权作举例),为了减少数据库的连接查询操作,把Customer和Contacts两个DO数据合并到一张数据表中。反过来,如果一本图书Book,有一个属性是封面cover,但该属性是一副图片的二进制数据,而某些查询操作不希望把cover一并加载,从而减轻磁盘IO开销,同时假设ORM框架不支持属性级别的延迟加载,那么就需要考虑把cover独立到

53、一张数据表中去,这样就形成一个DO对应对个PO的情况。nPO的某些属性值对于DO没有任何意义,这些属性值可能是为了解决某些持久化策略而存在的数据,例如为了实现“乐观锁”,PO存在一个version的属性,这个version对于DO来说是没有任何业务意义的,它不应该在DO中存在。同理,DO中也可能存在不需要持久化的属性。n现在的业务应用开发,基本上不需要区分DO与PO,PO完全可以通过JPA,HibernateAnnotations/hbm隐藏在DO之中。概念辨析-VO/DTO/DO/PO(五)VO/DTO/DO/PO转换:n工厂类或工厂方法;n构造函数;n工具类,如BeanUtils;n转换实

54、现中应注意类层次概念的依赖关系。DODTOVOPOEntity(一)Entity的标识生成:n用户提供n应用程序生成n持久化机制生成n另一个限界上下文提供在JPA中,有下面四种策略:n容器自动生成(GenerationType.AUTO):由JPA自动生成n使用数据库的自动增长字段生成(GenerationType.IDENTITY):需要数据库支持n根据数据库序列号(GenerationType.SEQUENCE):Oracle支持对序列号的支持n使用数据库表的字段生成(GenerationType.TA BLE):使用数据库中指定表的某个字段记录实体对象的标识,通过该字段的增长为新增加的实

55、体对象赋唯一值。Entity(二)继承关系:因为关系数据库的表之间不存在继承关系,Entity提供三种基本的继承映射策略:nSingleTablenJoinednTableperClassRef:Entity(三)继承关系:SingleTableSuppressWarnings(serial)EntityTable(name=Vehicle_Hierarchy)Inheritance(strategy=InheritanceType.SINGLE_TABLE)DiscriminatorColumn(name=Discriminator,discriminatorType=Discriminat

56、orType.STRING,length=30)DiscriminatorValue(Vehicle)publicclassVehicleimplementsSerializable/基类privateLongid;privateShortspeed;/速度IdGeneratedValueColumn(columnDefinition=integer)/指定使用适配Integer长度的数据类型publicLonggetId()returnid;publicvoidsetId(Longid)this.id=id;SuppressWarnings(serial)EntityDiscriminato

57、rValue(Car)publicclassCarextendsVehicle/Vehicle的子类privateStringengine;/发动机Column(nullable=true,length=30)publicStringgetEngine()returnengine;publicvoidsetEngine(Stringengine)this.engine=engine;SuppressWarnings(serial)EntityDiscriminatorValue(Camion)publicclassCamionextendsCar/Car的子类privateStringcont

58、ainer;/集装箱Column(nullable=true,length=30)publicStringgetContainer()returncontainer;publicvoidsetContainer(Stringcontainer)this.container=container;Entity(四)继承关系:JoinedSuppressWarnings(“serial”)EntityInheritance(strategy=InheritanceType.JOINED)Table(name=Vehicle)publicclassVehicleimplementsSerializab

59、le/基类privateLongid;privateShortspeed;/速度IdGeneratedValueColumn(columnDefinition=integer)publicLonggetId()returnid;publicvoidsetId(Longid)this.id=id;publicShortgetSpeed()returnspeed;publicvoidsetSpeed(Shortspeed)this.speed=speed;SuppressWarnings(serial)EntityTable(name=Car)PrimaryKeyJoinColumn(name=C

60、arID)/把主键对应的列名更改为CarIDpublicclassCarextendsVehicle/Vehicle的子类privateStringengine;/发动机Column(nullable=true,length=30)publicStringgetEngine()returnengine;publicvoidsetEngine(Stringengine)this.engine=engine;SuppressWarnings(serial)EntityTable(name=Camion)PrimaryKeyJoinColumn(name=CamionID)/把主键对应的列名更改为C

61、amionIDpublicclassCamionextendsCar/Car的子类privateStringcontainer;Column(nullable=true,length=30)publicStringgetContainer()returncontainer;publicvoidsetContainer(Stringcontainer)this.container=container;Entity(五)继承关系:TableperClassn一旦使用这种策略,意味着你不能使用AUTOgenerator和IDENTITYgenerator,即主键值不能采用数据库自动生成。Suppre

62、ssWarnings(serial)Entity/或MappedSuperclassInheritance(strategy=InheritanceType.TABLE_PER_CLASS)Table(name=“Vehicle”)/当为MappedSuperclass时,不要Table标注publicclassVehicleimplementsSerializable/基类privateLongid;privateShortspeed;/速度IdColumn(columnDefinition=integer)publicLonggetId()returnid;publicvoidsetId(

63、Longid)this.id=id;publicShortgetSpeed()returnspeed;publicvoidsetSpeed(Shortspeed)this.speed=speed;SuppressWarnings(serial)EntityTable(name=Car)publicclassCarextendsVehicle/Vehicle的子类privateStringengine;/发动机Column(nullable=true,length=30)publicStringgetEngine()returnengine;publicvoidsetEngine(Stringe

64、ngine)this.engine=engine;SuppressWarnings(serial)EntityTable(name=Camion)publicclassCamionextendsCar/Car的子类privateStringcontainer;/集装箱Column(nullable=true,length=30)publicStringgetContainer()returncontainer;publicvoidsetContainer(Stringcontainer)this.container=container;Entity(六)审计(Audit):最近一次修改时间,最

65、近一次修改人。EntityEntityListeners(JodaAuditListener.class)publicclassCargoextendsAbstractDomainObjectimplementsJodaAuditable,IdentifiablepublicinterfaceJodaAuditablepublicvoidsetCreatedBy(StringcreatedBy);publicStringgetCreatedBy();publicvoidsetCreatedDate(DateTimecreatedDate);publicDateTimegetCreatedDat

66、e();publicvoidsetLastUpdatedBy(StringupdatedBy);publicStringgetLastUpdatedBy();publicvoidsetLastUpdated(DateTimeupdateDate);publicDateTimegetLastUpdated();publicclassJodaAuditListenerPreUpdatePrePersistprivatevoidchangeAuditInformation(JodaAuditableauditableEntity)DateTimelastUpdated=newDateTime();a

67、uditableEntity.setLastUpdated(lastUpdated);StringlastUpdatedBy=ServiceContextStore.getCurrentUser();auditableEntity.setLastUpdatedBy(lastUpdatedBy);if(auditableEntity.getCreatedDate()=null)auditableEntity.setCreatedDate(lastUpdated);if(auditableEntity.getCreatedBy()=null)auditableEntity.setCreatedBy

68、(lastUpdatedBy);Entity(七)审计(Audit):使用事件记录实体状态变更事件,操作人以及状态改变内容等。Service(bettingService)publicclassBettingServiceImplimplementsBettingServiceprivatestaticfinalLoggerLOG=LoggerFactory.getLogger(BettingServiceImpl.class);Publish(eventType=BettingInstruction.class,topic=bettingInstructionTopic,eventBus=c

69、ommandBus)publicvoidplaceBet(Betbet)LOG.info(#Placingbet:,bet);/dosomeinitialvalidation./newBettingInstructionwillbepublishedSubscribe(topic=bettingInstructionTopic,eventBus=commandBus)publicclassBettingEngineImplimplementsBettingEnginepublicvoidreceive(Eventevent)DynamicMethodDispatcher.dispatch(th

70、is,event,handle);publicvoidhandle(BettingInstructionbetInstruction)LOG.info(#Handlingbet:,betInstruction);instructionRepository.save(betInstruction);Entity(八)并发冲突:乐观锁importjavax.persistence.Version;EntityEntityListeners(JodaAuditListener.class)publicclassCargoextendsAbstractDomainObjectimplementsJod

71、aAuditable,IdentifiableVersionColumn(name=VERSION,nullable=false)privateLongversion;ValueObject(一)值对象可以与其所在的实体对象保存在同一张表中,值对象的每一个属性保存为一列;值对象也可以独立于其所在的实体对象保存在另一张表中,值对象获得委派主键,该主键对客户端是不可见的。ValueObject(二)值对象可以与其所在的实体对象保存在同一张表中:EntityEntityListeners(JodaAuditListener.class)publicclassCargoextendsAbstractD

72、omainObjectimplementsJodaAuditable,IdentifiableEmbeddedAttributeOverrides(AttributeOverride(name=identifier,column=Column(name=TRACKINGID,nullable=false,length=100)NotNullprivateTrackingIdtrackingId;publicTrackingIdgetTrackingId()returntrackingId;EmbeddablepublicclassTrackingIdextendsAbstractDomainO

73、bjectprivatestaticfinallongserialVersionUID=1L;Column(name=,nullable=false,length=100,unique=true)NotNullprivateStringidentifier;protectedTrackingId()publicTrackingId(Stringidentifier)super();Validate.notNull(identifier,TrackingId.identifiermustnotbenull);this.identifier=identifier;ValueObject(三)使用数

74、据库实体保存值对象EntityEntityListeners(JodaAuditListener.class)publicclassCargoextendsAbstractDomainObjectimplementsJodaAuditable,IdentifiableOneToOne(mappedBy=cargo,cascade=CascadeType.ALL,fetch=FetchType.EAGER)privateItineraryitinerary;Entity(name=Itinerary)Table(name=ITINERARY)publicclassItineraryprivate

75、staticfinallongserialVersionUID=1L;staticfinalItineraryEMPTY_ITINERARY=newItinerary();publicItinerary(finalListlegs)Validate.notEmpty(legs);Validate.noNullElements(legs);super.getLegs().addAll(legs);Itinerary()OverridepublicListgetLegs()returnCollections.unmodifiableList(super.getLegs();/*Testiftheg

76、ivenhandlingeventisexpectedwhenexecutingthis*itinerary.*paramevent*Eventtotest.*returntrueiftheeventisexpected*/publicbooleanisExpected(finalHandlingEventevent).注意区分数据库实体和领域实体两个概念的不同存储-对象关联关系(一)对象关联关系:应用担负了维护对象关联关系的职责存储-对象关联关系(二)维护对象关联关系示例:EntitypublicclassEmployeeManyToOne(fetch=LAZY)privateDepartm

77、entdept;EntitypublicclassDepartmentOneToMany(mappedBy=“dept”,fetch=LAZY)privateCollectionemps=new;publicintaddNewEmployee()Employeee=newEmployee(.);Departmentd=departmentRepository.loadDepartment();e.setDepartment(d);/Reverserelationshipisnotsetem.persist(e);em.persist(d);returnd.getEmployees().size

78、();INC CORRECTCT!publicintaddNewEmployee()Employeee=newEmployee(.);Departmentd=departmentRepository.loadDepartment();e.setDepartment(d);d.getEmployees().add(e);em.persist(e);em.persist(d);returnd.getEmployees().size();存储-对象关联关系(三)对象关联关系映射策略:n单向一对一是关联关系映射中最简单的一种,简单地或就是可以从关联的一方去查询另一方,却不能反向查询。单向一对一关系的拥

79、有端单向一对一关系的拥有端EntitypublicclassPersonimplementsSerializableprivatestaticfinallongserialVersionUID=1L;IdGeneratedValue(strategy=GenerationType.AUTO)privateLongid;privateStringname;privateintage;OneToOneprivateAddressaddress;/Getters&Setters单向一对一关系的反端单向一对一关系的反端EntitypublicclassAddressimplementsSerializ

80、ableprivatestaticfinallongserialVersionUID=1L;IdGeneratedValue(strategy=GenerationType.AUTO)privateLongid;privateStringstreet;privateStringcity;privateStringcountry;/Gettes&Setters单向的一对一关系在数据库中是以外键的形式被映射的。其中关系的发出端存储一个指向关系的接收端的一个外键,缺省情况下这个外键的字段名称,是以它指向的表的名称加下划线“_”加“ID”组成的,当然我们也可以根据我们的喜好来修改这个字段,修改的办法就

81、是使用JoinColumn这个注解存储-对象关联关系(四)关联关系映射策略:n双向一对一关系:双向一对一关系中的接收端双向一对一关系中的接收端EntitypublicclassAddressimplementsSerializableIdGeneratedValue(strategy=GenerationType.AUTO)privateLongid;privateStringstreet;privateStringcity;privateStringcountry;OneToOne(mappedBy=address)privatePersonperson;/Gettes&Setters双向关

82、系有一方为关系的拥有端,另一方是关系的反端,也就是“Inverse”端。在这里例子中Person拥有这个关系,而Address就是关系的“Inverse”端。Address中我们定义了一个person属性,在这个属性上我们使用了OneToOne注解并且定义了他的“mappedBy”属性,这个在双向关系的“Inverse”端是必需的。存储-对象关联关系(五)对象关系维护的控制:n单向OneToMany关系:单向一对多关系的发出单向一对多关系的发出 端端EntitypublicclassPersonimplementsSerializableprivatestaticfinallongserial

83、VersionUID=1L;IdGeneratedValue(strategy=GenerationType.AUTO)privateLongid;privateStringname;privateintage;OneToManyprivateListcellPhones;/GettersandSetters单向一对多关系的接收端单向一对多关系的接收端EntitypublicclassCellPhoneimplementsSerializableprivatestaticfinallongserialVersionUID=1L;IdGeneratedValue(strategy=Generat

84、ionType.AUTO)privateLongid;privateStringmanufacture;privateStringcolor;privateLongphoneNo;/GettersandSetters在一对多关联关系映射中,默认是以中间表的方式来映射这种关系的。如在本例中,中间表为person_cellphone,表的名称为关系的拥有端和Inverse端中间用下划线连接。中间表的字两个字段分别为两张表的得表名加下划线“_”加ID组成。当然我们也可以改表这种默认的中间表的映射方式,我们可以在关系的拥有端使用JoinClolum来使用外键的方式映射这个关系。存储-对象关联关系(六)

85、对象关系维护的控制:n双向OneToMany关系:双向一对多关系的接受端双向一对多关系的接受端EntitypublicclassPersonimplementsSerializableIdGeneratedValue(strategy=GenerationType.AUTO)privateLongid;privateStringname;privateintage;OneToMany(mappedBy=person)privateListcellPhones;/GettersandSetters双向一对多关系的发出端双向一对多关系的发出端EntitypublicclassCellPhoneim

86、plementsSerializableIdGeneratedValue(strategy=GenerationType.AUTO)privateLongid;privateStringmanufacture;privateStringcolor;privateLongphoneNo;ManyToOneprivatePersonperson;/GettersandSetters在OneToMany里加入mappedBy属性避免生成中间表。在当前例子中,cellPhones这一端是关系的拥有者,CellPhone一方的表中生成到关联类的外键。存储-对象关联关系(七)对象关系维护的控制:n单向Ma

87、nyToMany关系:单向多对多关系的发出端单向多对多关系的发出端EntitypublicclassTeacherimplementsSerializableIdGeneratedValue(strategy=GenerationType.AUTO)privateLongid;privateStringname;privateBooleangender;privateintage;privateintheight;ManyToManyprivateListstudents;/GettersandSetters单向多对多关系的反端单向多对多关系的反端EntitypublicclassStuden

88、timplementsSerializableIdGeneratedValue(strategy=GenerationType.AUTO)privateLongid;privateStringname;privateBooleangender;privateintage;privateintheight;/GettersandSetters多对多关联关系中只能通过中间表的方式进行映射。我们使用了ManyToMany这个注解来对Teacher中的Students进行注释,其中Teacher就是关系的发出端。而在Student中我们并没有作任何定义,这是单向多对多的所要求的。存储-对象关联关系(八

89、)对象关系维护的控制:n双向ManyToMany关系:双向多对多关系的拥有端双向多对多关系的拥有端EntitypublicclassTeacherimplementsSerializableIdGeneratedValue(strategy=GenerationType.AUTO)privateLongid;privateStringname;privateBooleangender;privateintage;privateintheight;ManyToManyprivateListstudents;/GettersandSetters双向多对多关系的反端双向多对多关系的反端Entityp

90、ublicclassStudentimplementsSerializableIdGeneratedValue(strategy=GenerationType.AUTO)privateLongid;privateStringname;privateBooleangender;privateintage;privateintheight;ManyToMany(mappedBy=students)privateListteachers;/GettersandSetters存储-对象关联关系(九)关联对象加载策略:nEAGERimmediatenLAZYloadedonlywhenneedednLA

91、ZY适合对象数量多,或者嵌套层次深的关联对象nJPA中,1:m或m:n关系的默认加载策略是LAZY;n实际应用中,对于大数据库(egBLOB)的属性,以及不经常使用的关联对象,采用LAZYn对于Detached对象,使用LAZYload会报异常(参考DO/DTO区别)EntitypublicclassDepartmentIdprivateintid;OneToMany(mappedBy=“dept”)privateCollectiionemps;存储-对象关联关系(十)Cascade(级联):nCascadeType.PERSIST(级联保存):持久保存拥有方实体时,也会持久保存该实体的所有相

92、关数据nCascadeType.REMOVE(级联删除):删除一个实体时,也会删除该实体的所有相关数据nCascadeType.MERGE(级联更新):将Detached(游离)的实体重新合并到活动的持久性上下文时,也会合并该实体的所有相关数据nCascadeType.REFRESH(级联刷新):假如有一条数据(就有name值为B和sex值为male两个字段),A用户取出来在进行修改操作(修改name为A),正在A修改的过程中(未提交表单),B用户也对这条数据进行修改操作(修改sex为female),B先将性别修改后提交数据库.接着A用户也提交表单,但是,此时在entityManager中的持

93、久化实体的性别为male,没有更新为B用户修改成的female,所以此时执行一次Refresh操作,就会将该实体更新为数据库中的最新记录,然后再进行提交.做级联的时候就会将关联的实体的也获取最新的然后再更新,前提是要执行Refresh操作,CasCadeType.Refresh才会生效nCascadeType.ALL(包含以上全部级联操作)n是否应该使用级联应该是个业务问题而不是技术问题在一对多的级联中,如果对象之间是组合关系(UML术语),使用级联;如果是聚合关系(UML术语),不用级联。避免在深层嵌套关系中使用MERGE和ALL级联存储-对象关联关系(十一)Cascade(级联)代码示例:

94、EntityTable(uniqueConstraints=UniqueConstraint(columnNames=CLASSPACKAGE,NAME)publicclassSourceClassIdGeneratedValue(strategy=GenerationType.AUTO)privateLongid;/类的包路径privateStringclassPackage=;/类名称privateStringname=;/类描述Column(name=remark,length=1000)privateStringremark=;/类的字段OneToMany(mappedBy=sourc

95、eClass,cascade=CascadeType.ALL)privateCollectionfields=newHashSet();/类的方法集合OneToMany(mappedBy=sourceClass,cascade=CascadeType.ALL)privateCollectionmethods=newHashSet();/存储-对象关联关系(十二)Cascade(级联)代码示例:EntitypublicclassEmployee/类的字段ManyToOne(cascade=CascadeType.PERSIST)privateAddressaddress;/值对象Addressa

96、ddress=newAddress();em.persist(address);employee.setAddress(address);Em.persist(employee);如果不用Cascade,则提交一个有address的Employee对象需要这样EntitypublicclassPlanetextendsAbstractDomainObjectimplementsAuditable,Identifiable/OneToMany(cascade=CascadeType.ALL,orphanRemoval=true,mappedBy=planet,fetch=FetchType.EA

97、GER)ForeignKey(name=FK_MOON_PLANET_PLANET,inverseName=FK_MOON_PLANET_MOON)NotNullprivateSetmoons=newHashSet();/另举一个聚合根的例子(Planet是聚合根,Moon是非聚合根):EntityTable(name=MOON)EntityListeners(AuditListener.class)publicclassMoonextendsAbstractDomainObjectimplementsAuditable,Identifiable/Aggregate(一)超大聚合的问题:n并发

98、访问时的数据一致性(consistency)以及事务失败问题(transactionalfailures);n性能和扩展性问题,如一次加载过多数据或内存不足等;Aggregate(二)设计小聚合:n探索业务中真正必须一致(强一致)的规则,依据一致性边界发现聚合;n不要轻易相信所有用例,有时候数据的最终一致是可以接受的;下例中, 我们重新划分了四个聚合,每个聚合都依赖一个公共的值对象ProductId(Product领域实体的标识)。采用领域服务协调publicclassProduct.publicBacklogItemplanBacklogItem(StringaSummary,Stringa

99、Category,BacklogItemTypeaType,StoryPointsaStoryPoints).publicReleasescheduleRelease(StringaName,StringaDescription,DateaBegins,DateanEnds).publicSprintscheduleSprint(StringaName,StringaGoals,DateaBegins,DateanEnds).publicclassProductBacklogItemService.TransactionalpublicvoidplanProductBacklogItem(St

100、ringaTenantId,StringaProductId,StringaSummary,StringaCategory,StringaBacklogItemType,StringaStoryPoints)Productproduct=productRepository.productOfId(newTenantId(aTenantId),newProductId(aProductId);BacklogItemplannedBacklogItem=product.planBacklogItem(aSummary,aCategory,BacklogItemType.valueOf(aBackl

101、ogItemType),StoryPoints.valueOf(aStoryPoints);backlogItemRepository.add(plannedBacklogItem);.Aggregate(三)设计小聚合:如何解决聚合间相互引用问题n在聚合内引用其他聚合对象一致性边界控制问题:人们可能在一个聚合内,修改其它聚合对象;模型加载与遍历的性能问题publicclassBacklogItemextendsConcurrencySafeEntity.privateProductproduct;Aggregate(四)设计小聚合:如何解决聚合间相互引用问题n在聚合内使用标识(ValueOb

102、ject)引用其他聚合;publicclassBacklogItemextendsConcurrencySafeEntity.privateProductIdproductId;publicclassProductBacklogItemServiceTransactionalpublicvoidassignTeamMemberToTask(StringaTenantId,StringaBacklogItemId,StringaTaskId,StringaTeamMemberId)BacklogItembacklogItem=backlogItemRepository.backlogItemOf

103、Id(newTenantId(aTenantId),newBacklogItemId(aBacklogItemId);TeamofTeam=teamRepository.teamOfId(backlogItem.tenantId(),backlogItem.teamId();backlogItem.assignTeamMemberToTask(newTeamMemberId(aTeamMemberId),ofTeam,newTaskId(aTaskId);publicclassBacklogItemextendsConcurrencySafeEntitypublicvoidcommitTo(S

104、printaSprint)DomainEventPublisher.instance().publish(newBacklogItemCommitted(this.tenantId(),this.backlogItemId(),this.sprintId();领域服务协调聚合间访问采用最终一致方式保证聚合间数据一致性Aggregate(五)应遵循把聚合作为数据一致性边界的原则,允许有以下例外(谨慎使用):n用户交互的便捷性:如批量导入/更新等用户操作行为;n全局事务,两阶段提交事务;n因为查询性能,需要直接在聚合中引用其他聚合对象;n技术框架不支持。Aggregate(六)是否需要进一步划分聚

105、合应衡量哪些因素?n“通用语言”,即业务的一致性规则要求;n衡量聚合的代价:一次加载对象数,内存消耗,以及通用的使用场景(出现频率)ORAggregate(七)AggregateObjectnSometimestheresponsibilityofthewholeaggregatedoesntreallybelongtotheAggregateRootentity.nSometimespreservingintegrityoftheaggregateasawholeisaresponsibilitythatdeservesaroleofitsownnAnAggregateObjectmight

106、coordinateinvariantscheckingandstatemanagementbetweenthedifferententitiesoftheaggregate存储-事务控制(一)事务控制的边界位于服务层(ServiceLayer)publicclassProductBacklogItemServiceTransactionalTransactionalpublicvoidassignTeamMemberToTask(StringaTenantId,StringaBacklogItemId,StringaTaskId,StringaTeamMemberId)BacklogItem

107、backlogItem=backlogItemRepository.backlogItemOfId(newTenantId(aTenantId),newBacklogItemId(aBacklogItemId);TeamofTeam=teamRepository.teamOfId(backlogItem.tenantId(),backlogItem.teamId();backlogItem.assignTeamMemberToTask(newTeamMemberId(aTeamMemberId),ofTeam,newTaskId(aTaskId);存储-事务控制(二)事务管理方式:nJTA:全

108、局事务nRESOURCE_LOCAL:本地事务JPAconfigurationforDDDSampleorg.hibernate.ejb.HibernatePersistencejava:comp/env/jdbc/applicationDSorg.sculptor.dddsample.cargo.domain.Cargoorg.sculptor.dddsample.carrier.domain.CarrierMovementorg.sculptor.dddsample.carrier.domain.CarrierMovementIdorg.sculptor.dddsample.cargo.d

109、omain.HandlingEventorg.sculptor.dddsample.cargo.domain.Itineraryorg.sculptor.dddsample.cargo.domain.Legorg.sculptor.dddsample.location.domain.Locationorg.sculptor.dddsample.routing.domain.RtCarrierMovementorg.sculptor.dddsample.routing.domain.RtLocationorg.sculptor.dddsample.cargo.domain.TrackingIdo

110、rg.sculptor.dddsample.routing.domain.TransitEdgeorg.sculptor.dddsample.routing.domain.TransitPathorg.sculptor.dddsample.location.domain.UnLocodeENABLE_SELECTIVEAUTOpersistence.xml存储-事务控制(三)TransactionPropagation属性值nRequired:在有transaction状态下执行;如果当前没有transaction,则创建新的transaction。这是最常用的属性,也是默认属性。nManda

111、tory:必须在有transaction状态下执行,如果当前没有transaction,则抛出异常IllegalTransactionStateException。通常在“ClientOrchestrationtransactionstrategy”中使用。nRequiresNew:创建新的transaction并执行,如果当前已有transaction,则将当前transaction挂起。通常在Auditing或Logging等数据操作情形下使用,因为这些操作与整个基础事务的成败没有关系。nSupports:如当前有transaction,则在transaction状态下执行,如果当前没有t

112、ransaction,在无transaction状态下执行。主要用于数据库的只读操作,与NotSupported不同,Supports属性在有当前事务时,读取数据可以保持与当前事务的数据保持一致。nNotSupported:在无transactioin状态下执行;如果当前已有transaction,则将当前transaction挂起。通常在有数据库存储过程情形下使用(数据库不支持嵌套事务)。nNever:在无transaction状态下执行;如果当前已有transaction,则抛出异常IllegalTransactionStateException。唯一的使用场景可能就是测试了Ref:存储-

113、事务控制(四)事务控制需要注意的事项:n在事务中,不要运行昂贵而不必要的、与事务无关的操作指令,如日志记录,其磁盘读写代价高,非常消耗资源n不要在浏览数据的时候打开事务(设置值TransactionAttribute(NOT_SUPPORTED)存储-事务控制(五)事务策略:nClientOrchestrationtransactionstrategy:http:/nAPILayertransactionstrategy:http:/nHighConcurrencytransactionstrategy:http:/nHigh-SpeedProcessingtransactionstrateg

114、y:http:/存储-缓存(一)经过领域建模以后,整个模型对象都是完全面向业务的,并且具有丰富的行为。而对象和数据库之间又存在天然的矛盾,这种矛盾主要体现在领域对象保存到数据库时,需要把其一层层打开,然后放入数据库,而从数据库里生成领域对象又需要将裸体的数据穿上衣服,最终形成领域对象,这个过程非常的麻烦。因此我们需要把领域对象用完以后放到某一种地方,而这种地方可以方便快捷地取出对象和放入对象,这个对象其实就是缓存。缓存降低了应用程序对持久性数据源的访问,从而使得应用程序具有更好的性能。缓存是领域对象在内存中的生存场所,是一种面向业务的存储方式,而同时我们的领域模型也是一种面向业务的模型,有了面

115、向业务的存储以后,我们就可以进行面向业务的运算,而正是这种面向业务的运算使得我们的系统具有更好的伸缩性和扩展性。因为此时的领域对象通过缓存都是跑在中间件中,而在负载增多的时候,通过水平的增加中间件服务器来进行水平伸缩。缓存+领域模型是面向业务的对象模型,面向业务的存储,面向业务的运算结合的基础,而数据库则是一种完全面向数据的存储方式,因此数据库思维和对象模型思维是不匹配的。领域模型不应当关注缓存的技术实现细节,Repository是恰当的隐藏缓存技术实现的地方。存储-缓存(二)二级缓存机制与查询缓存n二级缓存机制一级缓存:生命周期和session的生命周期一致,当前session一旦关闭,一级

116、缓存就会消失,因此一级缓存也叫session缓存或者事务级缓存。二级缓存:也称为进程缓存或者sessionFactory级的缓存,它可以被所有的session共享,二级缓存的生命周期和sessionFactory的生命周期一致。二级缓存一般针对经常被读、很少被修改、过期也不产生重大影响的Entity对象。n查询缓存:二级缓存策略是针对于ID查询的缓存机制,对于条件查询则毫无作用。针对条件查询可以启用QueryCache,查询缓存中以键值对的方式存储,key键为查询的条件语句,value为查询之后等到的结果集的ID列表。当数据表发生数据变动的话,hibernate就会自动清除查询缓存中对应的Qu

117、eryKey存储-缓存(三)JPA一级缓存(PersistenceContext)工作机制,以及JPAEntity的生命周期nNew/Transient:新创建的实体对象,没有主键值nManaged:对象处于PersistentContext(持久化上下文)中,被EntityManager管理nDetached:对象已经游离到PersistentContext之外,进入ApplicationDomainnRemoved:实体对象被删除*注意注意JPA ContextA Context中的中的Entityntity和和DDD Context Context中的中的Entityntity的概念区别

118、!的概念区别!存储-缓存(四)HibernateJPA中配置Ehcache二级缓存示例n(1)JPA的persistence.xml配置存储-缓存(五)HibernateJPA中配置Ehcache二级缓存示例n(2)对ehcache进行简单的配置(ehcache.xml)示例中启用了Ecache非分布式缓存,也可以结合Terracotta启用分布式缓存。存储-缓存(六)HibernateJPA中配置Ehcache二级缓存示例n(3)JPA的Entity类中生命缓存的隔离机制importorg.hibernate.annotations.Cache;importorg.hibernate.ann

119、otations.CacheConcurrencyStrategy;Cache(usage=CacheConcurrencyStrategy.READ_WRITE)EntityTable(name=catagory)publicclassCatagoryEntityextendsBaseEntity.Event事件的两大类别:Command和Event两类事件的命名方式:nCommand:主动式,如PlaceOrder/PlaceOrderCmdnEvent:被动式,如OrderPlaced/OrderPlacedEventServiceService间通信方式n本地服务通信n远程服务通信Sy

120、nchronousHTTPasynchronousAMQPFormats:JSON,XML,ProtocolBuffers,Thrift,.Evenviathedatabase*Asynchronousispreferred*JSONisfashionablebutbinaryformatismoreefficientModule(一)模块在实际语言中的映射n模块在Java语言实现中用包(Package)表示n模块和模块之间不允许存在相互依赖。n一个模块中的服务(Service)不允许直接访问另一个模块的仓储(Repository),它们之间必须通过服务接口进行访问。Module(二)模块设计

121、的原则:低耦合,高内聚Module(三)示例:DDDwithoutmodularityModule(四)模块化数据库Schema设计要求n数据库结构不允许跨领域(模块)n查询不允许跨领域边界好处:l模块可以独立迁移到下一版本l在一个系统中,不同模块可以采用不同存储/数据库技术Module(五)跨边界查询两种方案对比:Module(六)使用ElasticSearch实现跨模块查询视图:测试(一)领域驱动设计软件开发/测试各阶段测试(二)测试的目的与相应方法ApplicationLevels Testing TypeExpectations UI / Presentation / Controls

122、 / Services / Classes / Models / CodeMeetsCustomersExpectationsCodeMeetsProgrammersExpectationsUnitTestingBDDSelenium测试(三)行为驱动开发(BehaviorDrivenDevelopment):BDD=TDD+DDDn测试驱动开发(TDD),是一种新型的开发方法,要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推动整个开发的进行。这有助于编写简介可用和高质量的代码,并加速开发过程。(Ref:Test-DrivenDevelopmentByE

123、xample,KentBeck)n行为驱动开发(BDD):BDD的重点是通过与利益相关者的讨论取得对预期的软件行为的清醒认识。它通过用自然语言书写非程序员可读的测试用例扩展了测试驱动开发方法。行为驱动开发人员使用混合了领域中统一的语言的母语语言来描述他们的代码的目的,使得开发者得以把精力集中在代码如何实现商业价值商业价值上。nTDD和BDD都是Test-FirstDevelopment,都借助自动化测试工具。BDD是对TDD的拓展,把关注点从“测试”本身,转移到“商业价值”上来。测试(四)行为驱动开发:用举例来阐述行为GivenacontextWhenaneventhappensThenano

124、utcomeshouldoccurGivenFredhasboughtamicrowaveAndthemicrowavecost100WhenwerefundthemicrowaveThenFredshould berefunded100.AnexampleofhowthesystemmightbehavefromauserperspectiveStory TemplateStory TemplateAs a As a User/RoleI want want Behaviourso that so that Ireceivebenefit测试(五)在项目中实施行为驱动开发的步骤1.Forea

125、chscenariodescribingafeature2.Runthescenarioitfails(gored)3.Definethefirststepgored4.Writedowntheapplicationcodegettingthesteptopassgogreen5.Refactorthecodeandrepeatsteps4&5foreachstepuntil66.Thescenariopassesgogreen7.Refactortheapplicationcode*测试驱动开发或者行为驱动开发在做法上的要求是一致的,即在编写代码的过程中,不要急于探索如何进行内部实现的细节或

126、机制,而是首先考虑客户(调用方/使用者)使用软件(应用/模块/类/)的场景。满足客户的需求,不多也不少。测试(六)自动化测试常用工具n单元测试框架:Junit(http:/junit.org/)n行为驱动测试框架:Jbehave(Ref:/)nMock框架:EasyMock(.easymoceasymock.org/)、JMockit()等n内存数据库:HSQLDB(http:/hshsqldbldb.org/)培训内容领域驱动设计简介领域通用语言领域驱动设计的构造块领域驱动设计编程实践CQRS架构模型驱动开发传统的CRUD方法传统的CRUD方法的问题:n使用同一个对象实体来进行数据库读写可能

127、会太粗糙,大多数情况下,比如编辑的时候可能只需要更新个别字段,但是却需要将整个对象都穿进去,有些字段其实是不需要更新的。在查询的时候在表现层可能只需要个别字段,但是需要查询和返回整个实体对象。n使用同一实体对象对同一数据进行读写操作的时候,可能会遇到资源竞争的情况,经常要处理的锁的问题,在写入数据的时候,需要加锁。读取数据的时候需要判断是否允许脏读。这样使得系统的逻辑性和复杂性增加,并且会对系统吞吐量的增长会产生影响。n同步的,直接与数据库进行交互在大数据量同时访问的情况下可能会影响性能和响应性,并且可能会产生性能瓶颈。n由于同一实体对象都会在读写操作中用到,所以对于安全和权限的管理会变得比较

128、复杂。n这里面很重要的一个问题是,系统系统中的读写频率比中的读写频率比,是偏向读,还是偏向写,就如同一般的数据结构在查找和修改上时间复杂度数据结构在查找和修改上时间复杂度不一样,在设计系统的结构时也需要考虑这样的问题。解决方法就是我们经常用到的对数据库进行读写分离。CQRS简介(一)CQRS由CregYound在CQRS,TaskBasedUIs,EventSourcingagh!这篇文章中提出。“CQRS只是简单的将之前只需要创建一个对象拆分成了两个对象,这种分离是基于方法是执行命令还是执行查询这一原则来定的”。nCQRS使用分离的接口将数据查询操作(Queries)和数据修改操作(Comm

129、ands)分离开,这也意味着在查询和更新过程中使用的数据模型也是不一样的。这样读和写逻辑就隔离开了。n主数据库处理CUD,从库处理R,从库的的结构可以和主库的结构完全一样,也可以不一样,从库主要用来进行只读的查询操作。在数量上从库的个数也可以根据查询的规模进行扩展,在业务逻辑上,也可以根据专题从主库中划分出不同的从库。CQRS简介(二)从库也可以实现成ReportingDatabase,根据查询的业务需求,从主库中抽取一些必要的数据生成一系列查询报表来存储。使用ReportingDatabase的一些优点通常可以使得查询变得更加简单高效:nReportingDatabase的结构和数据表会针对

130、常用的查询请求进行设计。nReportingDatabase数据库通常会去正规化,存储一些冗余而减少必要的Join等联合查询操作,使得查询简化和高效,一些在主数据库中用不到的数据信息,在ReportingDatabase可以不用存储。n可以对ReportingDatabase重构优化,而不用去改变操作数据库。n对ReportingDatabase数据库的查询不会给操作数据库带来任何压力。n可以针对不同的查询请求建立不同的ReportingDatabase库。CQRS架构CQRS架构示意图:ClientCommandsCommandBusSendsCommandHandlersModifyRep

131、ositoriesReadWriteDatastoreEventBusPublishEventsEventBusCommandServicesEventHandlersEventsReadstoreQueryFacadeQueryHandlersQueryResultsQueriesQueryServicesEventsDomain事件源事件源(EventSourcing):AggregatestracktheirownDomainEventsandderivestatefromthemOctober5October6October6October7October7October9事件源实现C

132、QRS(一)FULLCQRSwithEventSourcing:UIDomainEventStoreCommandsChangedataCommandsEventsSQLDBDocumentDBGraphDBUIDataQueriesAskfordataEventsQueryBuildOursinglesourceoftruth事件源实现CQRS(二)FULLCQRSwithEventSourcing代码风格示例:public class CustomerCommandHandler private Repository customerRepository; public CustomerC

133、ommandHandler(Repository customerRepository) this.customerRepository = customerRepository; CommandHandler public void handle(UnsignCustomer cmd) Customer customer = repository.load(cmd.getCustomerId(); customer.unsign(); public class Customer private boolean signedUp; public void unsign() if (signed

134、Up) apply(new CustomerUnsignedEvent(); EventHandler private void handle(CustomerUnsignedEvent event) signedUp = false; CQRS应用示例(一)SalesSalesProductSKUNamePriceQuantityOrderedInventory Service nventory Service (SASAP)ProductSKUQOHLocationCodePricing Servicericing ServiceProductSKUUnitPricePromotional

135、PriceInventorynventoryPricingricingSalesSalesCustomersCustomersNewSKUEventNewSKUEventNewSKUEventOrderAcceptedEventMessageBusWhocoordinatesthesalesprocess?OnlineOrderingSystemWebShop(CompositeUI)CQRS应用示例(二)应用功能扩展:Sales ServiceSales ServiceOrderAcceptedBilling Serviceilling ServiceShipping Shipping Pr

136、ocess rocess Manageranager(SagaSaga)Shipping ServiceShipping ServiceOnlineOrderingSystemMessageBusOrderAcceptedOrderAcceptedCustomerBilledCustomerBilledShipOrderShipOrderCQRS模式的优点分工明确,可以负责不同的部分将业务上的命令和查询的职责分离能够提高系统的性能、可扩展性和安全性。并且在系统的演化中能够保持高度的灵活性,能够防止出现CRUD模式中,对查询或者修改中的某一方进行改动,导致另一方出现问题的情况。逻辑清晰,能够看到

137、系统中的那些行为或者操作导致了系统的状态变化。可以从数据驱动(Data-Driven)转到任务驱动(Task-Driven)以及事件驱动(Event-Driven)。CQRS的应用场景当在业务逻辑层有很多操作需要相同的实体或者对象进行操作的时候。CQRS使得我们可以对读和写定义不同的实体和方法,从而可以减少或者避免对某一方面的更改造成冲突对于一些基于任务的用户交互系统,通常这类系统会引导用户通过一系列复杂的步骤和操作,通常会需要一些复杂的领域模型,并且整个团队已经熟悉领域驱动设计技术。写模型有很多和业务逻辑相关的命令操作的堆,输入验证,业务逻辑验证来保证数据的一致性。读模型没有业务逻辑以及验证

138、堆,仅仅是返回DTO对象为视图模型提供数据。读模型最终和写模型相一致。适用于一些需要对查询性能和写入性能分开进行优化的系统,尤其是读/写比非常高的系统,横向扩展是必须的。比如,在很多系统中读操作的请求时远大于写操作。为适应这种场景,可以考虑将写模型抽离出来单独扩展,而将写模型运行在一个或者少数几个实例上。少量的写模型实例能够减少合并冲突发生的情况适用于一些团队中,一些有经验的开发者可以关注复杂的领域模型,这些用到写操作,而另一些经验较少的开发者可以关注用户界面上的读模型。对于系统在将来会随着时间不段演化,有可能会包含不同版本的模型,或者业务规则经常变化的系统需要和其他系统整合,特别是需要和事件

139、溯源EventSourcing进行整合的系统,这样子系统的临时异常不会影响整个系统的其他部分。CQRS不适用的场景领域模型或者业务逻辑比较简单,这种情况下使用CQRS会把系统搞复杂。对于简单的,CRUD模式的用户界面以及与之相关的数据访问操作已经足够的话,没必要使用CQRS,这些都是一个简单的对数据进行增删改查。不适合在整个系统中到处使用该模式。在整个数据管理场景中的特定模块中CQRS可能比较有用。但是在有些地方使用CQRS会增加系统不必要的复杂性。CQRS与大数据技术StormStormStormStormSparSparSparSpark kL Lucene ucene ucene uce

140、ne / / E ElasticSearchlasticSearchlasticSearchlasticSearchCQRS示例:微软.NET框架组件提供的CQRS架构附:CQSBetrandMeyer(Eiffel语言之父,开-闭原则OCP提出者)在ObjectOrientedSoftwareConstruction一书中提到一种命令查询分离(CommandQuerySeparation,CQS)的概念。Separationoffunctionsfunctionsthatwritewrite&functionsfunctionsthatreadreadFunctionsFunctionsth

141、atwritearecalledCommandCommandmethodsandmustnot return a valuenot return a valueFunctionsFunctionsthatreadarecalledQueryuerymethodsandmusthaveno side effectsno side effectsCQSUIApplicationDomainDataCommandsChangedataUIApplicationDataQueriesAskfordata(nosideeffects)培训内容领域驱动设计简介领域通用语言领域驱动设计的构造块CQRS架构领

142、域驱动设计编程实践模型驱动开发领域驱动设计研发生命周期模型Amodelisarepresentationofthedomainserving a specific purpose.Thereisno“perfect”modelforagivendomain.Agoodmodelistailoredonthegivenpurpose.CopyrightAlberto Brandolini2008模型的表达Modelandtheunderlyingdesignmustevolveinsync.Code Code istheultimatewaytoexpressthemodel.Intermedi

143、ateartifacts,diagramsanddocsserveatemporarygoal.CopyrightAlberto Brandolini2008DesignesignModelodel自动代码生成(一)自动代码生成工具可以大量减少手工代码量,并且可以增强代码的规范性。自动代码生成(二)Java领域驱动设计自动化代码生成工具:SculptorGenerator介绍自动代码生成(三)Application Application Universe niverse basebasePacackageage=org.sculptor.example.helloworldorg.sculp

144、tor.example.helloworldModule milodule milkyway yway Service Service PlanetService lanetService String sayString sayHelloello(String planetString planetNameame) throws throws PlanetlanetNotFoundotFoundExceptionxception;protected findprotected findBy yKey ey = PlanetlanetRepository.findepository.findB

145、y yKeyey;PlanetgetPlanet(String planetString planetNameame) throws throws PlanetlanetNotFoundotFoundExceptionxception;Entity ntity Planet lanet gapgapscaffoldscaffoldString name String name keyey;String messageString message;Integer diameter nullable minnteger diameter nullable min=1 1;Integer popul

146、ation nullable minnteger population nullable min=0 0;- Set Set moons opposite planet moons opposite planet;Repository epository PlanetlanetRepository epository findByKeys;findByKey;save;findAll(PagingParameterpagingParameter);findAll;Entity ntity Moon oon not aggregatenot aggregateRoot oot / belongs to belongs to Planet Aggregatelanet AggregateString name String name keyey;Integer diameter nullablenteger diameter nullable;- Planet planet opposite moonslanet planet opposite moons;DSL建模示例:自动代码生成(四)系统演示系统演示

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 办公文档 > 教学/培训

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号