您好,欢迎访问三七文档
当前位置:首页 > 医学/心理学 > 药学 > DomainModel浅析
在深入讨论之前我先抛出一些原则和概念,最后你会看到这些概念和原则的威力。1.按照概念依赖的原则来组织业务层。2.将业务活动(业务流程)建模成类。3.用业务活动(业务流程)作为关联整个业务层各种对象的骨架。4.在业务活动中凿出扩展点,使用不同接口分离不同性质业务对象。5.将对象的存储理解为业务层概念。......概念依赖这是我认为能否得到良好业务层最重要的概念。在我系统框架设计将要完成,开始涉及业务层设计时,我脑袋一片空白,书上,大家讨论的大多是整个系统的结构从UI层到服务层到数据访问层到数据库。到底业务层该如何组织?MartinFowler的POEAA的书中没有回答。找到的相关书籍也都过于空泛。MartinFowler的分析模式有些用处,但不够系统。透过Martinfowler网站,我拿到了DomainDrivenDesign的发行前版本。该书给了我很大的启示。其中的要点有:关于关联:1.Imposingatraversaldirection(强制一个关联的导航方向)......关于ResponsibilityLayers(业务职责层)的划分:作者给出了三个指导原则:Conceptualdependency.(概念依赖)为其中一项。书中给出的描述的是业务职责层上层的对象需要通过下层对象才能在概念上完整,相反下层对象则可独立于上层对象存在含义。这样天然的下层对象相对于上层对象会更稳定。并且在今后演变的过程中,使同扩展的方式来完善系统,而不是改变对象的方式。通过实践,我觉得这条原则可以应用在任何两个有关联的业务对象上。通常可以通过概念依赖先建立一个导航方向。这能够满足大多数的需求。当确实需要反向导航时,只要理由充分可以随时加上,并且如果先前将这两个对象放入不同包中,这时需要将他们合并到同一个包中。我见过一个不好的设计。Customer具有很多Flag分别标记该客户是否挂失,冻结,注销等等。通常叫做客户状态,然而这是不对的,这违背了单一职责原则。事实上除了注销外挂失和冻结都不应该算作Customer的本质属性。相反我把他们看作某种约束,进而把挂失看作一种协议.....因为Customer的概念可以不依赖于挂失和冻结的概念,相反挂失和冻结却要依赖Customer的概念,应为这是他们动作的主体。同样的一开始就让Customer有GetAccount的方法同样不好。因为Customer的概念确实不依赖AccountXXXAccount却可以有Customer的属性,Account在概念上依赖Customer。按照概念依赖原则我们能更好的理解业务职责层的划分。DDD中建议了如下的职责层。按从高到低分别为:依赖方向|Decision|Policy|Commitment|OperationVPotentialPotential中包括类似Customer,Employee等Party方面的类。对应支持业务。Operation中包括了核心业务如存取款,买卖以及同这些业务关联的Account,Product等等。Commmitment对于客户包括同客户签订的协议等。对于员工来说包括授权等。Policy包括计算某种收费的策略,比如电信收费的算法。对应支持业务。Decision包括主要对应于管理业务。监控系统多在这层。从上到下观察,很好的遵循了概念依赖的原则。从另一方面来看,可以根据概念随着时间发展的顺序来建立对象之间的关系。这样会天然的满足概念依赖原则。后面发展起来的概念可以依赖前面的已经存在的概念,而反过来这不可。这是系统稳定的关键。同客户签订的各种协议可以不断的发展,但是客户对象是稳定的。同理收费的策略可以变化但是最终反映到帐户上的都只是对Balance的变更。所以收费策略比帐户更不稳定。客户对象也要比帐户对象稳定。从按照稳定的方向依赖的原则出发,我们可以得到对象间的单向依赖。当然也会存在双向关联的对象,然而这种情况在我的实践中不是很多。而且一旦你懂得了单向关联的好处后,你就会谨慎的使用双向关联。滥用关联会使得整个业务层象DDD中说的,变成一大块“果冻”,你随便触动果冻某一块,整个果冻都会颤动。同样为了简化设计,对象的关系中多对多的关系尽量避免。如果可以则通过限定角色转化为一对多或一对一的关系。以上是关于概念依赖的观念,下面让我们看看如何建模业务中的活动。有一种做法是使用分析模型中的控制类直接映射到设计中类中。我看来这不是好的做法。这里谈谈分析与设计的区别。从分析的角度来看,业务实体总是被动的。业务是通过控制对象操作业务实体来完成的。分析时我们是关注是什么问题。这要求我们客观的来描述现实。进入设计阶段我们关注的是如何解决的问题。控制对象施加与业务实体的操作加入不涉及第三者,则这个操作可以并入被操作的实体类中。然而分析中控制对象的概念是如此的深刻,以至于只涉及Customer的ChangePassword方法该放到哪里都成了问题。类不是“某概念+所关心该概念的属性+最终施加与这些属性上的操作”的封装,又是什么呢?下面的问题是如何建模跨越多个业务实体的操作?举个例子:银行开户。现在假设开户时涉及到下面的一些操作对象。创建一个Customer对象。创建一个CapitalAccount对象。存入一定数额的现金。记录一笔开户流水。整个业务活动,我可以建模为OpenCustomerAct对象。伪码如下:publicclassOpenCustomerActextendsCustomerAct{...publicvoidoverridedoRun(){Customercustomer=Customer.create(...);CapitalAccountcapitalAccount=CapitalAccount.create(customer,...);capitalAccount.deposit(...);OpenCustomerLog.create(this);}...}所需的参数通过构造函数得到。将所有的业务活动都建模成一个Act,这非常重要。甚至你可以在Session中放入一个Act来表示当前正在进行的业务。所有的扩展都是从Act开始的。假如你要对Act施加某种检查,那么对doRun方法进行拦截可以达到该目的。用例能够简化到只剩下流程,同样道理Act也可以做到这点。对于象RichClient的交互模式,通常只在最后才提交业务,中间的交互都是在准备提交的数据。那么在中间调用的方法中可以只newXXXAct而不执行doRun操作。这样做是因为中间的调用可能会用到XXXAct来作为上下文。现在我还没有想好在这样的中间过程中,如何能够触发植入到donRun前的检查?或许可以创建一个空doRun的子类覆盖掉父类实际的操作?ActpublicinterfaceAct{OperatorgetOperator();//谁DategetOccurDate();//在什么时间StringgetOccurPlace();//什么地点BusinessTypegetBusinessType();//做什么业务ActStategetActState();//业务运行的当前状态}“谁在什么时间什么地点做了什么业务。”这描述了任何业务的基本方面。从哲学的角度来看,“我们得到了Act,我们就得到了事物的基础”。当我们具体的描述某项业务时,假如需要向调用方暴露特定的属性。我们可以随时添加到Act的子接口中。例如同Customer相关的Act可定义为:publicinterfaceCustomerActextendsAct{CutomergetCustomer();//针对哪个客户}在复杂一点的情况下,如业务需要多人协作完成,可以通过组合模式达到目的。publicinterfaceCompositeActextendsAct{Act[]getActs();}涉及到一段时间有中间状态的工作流也应该可以作为Act的子接口进行扩展。不过我没有做过这方面的尝试。将Act放入Session将Act放入Session使得可以方便得到业务运行的上下文。而且通过扩展Act。可以从Act或其子接口中得到想得到的任何东西,这使得任何扩展都成为可能。这里说明一下Act类的位置应当放入Potential层中,并且与Operator在一起。因为Potential层的业务对象也需要业务活动来维护。如果你的框架中Sesion在更基础的包中,则可以给Act提供一个空内容的父接口,放入Session所在的包中。publicinterfaceAbstractAct{}publicinterfaceActextendsAbstractAct{...}Session提供得到AbstractAct的入口。publicclassSession{...staticpublicAbstractActgetAbstractAct(){returnInstance().abstractAct;}...}Act上的扩展点按照分层的观点,下层不允许依赖上层,然而业务对象却是协作完成某个目的的。而且只要业务对象需要维护,就需要相关的Act。例如:银行中的存钱业务,参考上面的分层,我们把它放入Operation层。在存钱的业务中,我们需要检查该客户是否做了挂失。而挂失协议我们是放在Commitment层。显然,Operation层不能直接调用Commitment层的协议。DIP模式发话了“用我”。在Operation层中定义Commitment层接口,和一个工厂,使用反射实现这种调用。在Act中调用。abstractpublicclassActImplextendsabstractActImplimplementsAct{publicvirtualvoidrun(){doPreprocess();doRun();doPostprocess();}abstractpublicdoPreprocess();abstractpublicdoRun();abstractpublicdoPostprocess();}publicinterfaceCustomerCommitment{voidaffirmCanDo();}abstractpublicclassCustomerActImplextendsActImplimplementsCustomerAct{...publicoverridevoiddoPreprocess(){...//扩展点CustomerCommitmentcustomerCommitment=CustomerCommitmentFactory.create(this);customerCommitment.affirmCanDo();...}...}publicinterfaceInnerCustomerCommitment{voidaffirmCanDo(CustomerActcustomerAct);}publicclassCustomerCommitmentImplimplementsCustomerCommitment{privateCustomerActcustomerAct;publicCustomerCommitmentImpl(CustomerActcustomerAct){this.customerAct=customerAct;}publicvoidaffirmCanDo(){...//通过配置得到该customerAct对应需要检查的客户约束,包括协议,逐一检查。DomainObjectCollectioncommitmentTypes=CustomerCommimentRepository.findByBusinessType(customerAct.getBusinessType());...foreach(CommitmentTypetypeItemincommitmentTypes){InnerCustomerCommitmentcommitment=getCommitment(typeIt
本文标题:DomainModel浅析
链接地址:https://www.777doc.com/doc-2910203 .html