六 领域驱动设计-领域对象的生命周期

领域驱动设计-领域对象的生命周期

每一个对象都有生命周期,如图6-1所示。对象自建立后,可能会经历各类不一样的状态,直至最终消亡——要么存档,要么删除。固然,不少对象是简单的临时对象,仅经过调用构造函数来建立,用来作一些计算,然后由垃圾收集器回收。这类对象不必搞得那么复杂。但有些对象具备更长的生命周期,其中一部分时间不是在活动内存中度过的。它们与其余对象具备复杂的相互依赖性。它们会经历一些状态变化,在变化时要遵照一些固定规则。管理这些对象时面临诸多挑战,稍有不慎就会偏离MODEL-DRIVEN DESIGN的轨道。数据库

图6-1

主要的挑战有如下两类。设计模式

(1) 在整个生命周期中维护完整性。并发

(2) 防止模型陷入管理生命周期复杂性形成的困境当中。框架

AGGREGATE(聚合),它经过定义清晰的所属关系和边界,并避免混乱、错综复杂的对象关系网来实现模型的内聚。聚合模式对于维护生命周期各个阶段的完整性具备相当重要的做用。咱们将注意力转移到生命周期的开始阶段,使用FACTORY(工厂)来建立和重建复杂对象和AGGREGATE(聚合),从而封装它们的内部结构。最后,在生命周期的中间和末尾使用REPOSITORY(存储库)来提供查找和检索持久化对象并封装庞大基础设施的手段。数据库设计

尽管REPOSITORY和FACTORY自己并非来源于领域,但它们在领域设计中扮演着重要的角色。这些结构提供了易于掌握的模型对象处理方式,使MODEL-DRIVEN DESIGN更完备。函数

使用AGGREGATE进行建模,而且在设计中结合使用FACTORY和REPOSITORY,这样咱们就可以在模型对象的整个生命周期中,以有意义的单元、系统地操纵它们。AGGREGATE能够划分出一个范围,这个范围内的模型元素在生命周期各个阶段都应该维护其固定规则。FACTORY和REPOSITORY在AGGREGATE基础上进行操做,将特定生命周期转换的复杂性封装起来。测试

AGGREGATE

减小设计中的关联有助于简化对象之间的遍历,并在某种程度上限制关系的急剧增多。但大多数业务领域中的对象都具备十分复杂的联系,以致于最终会造成很长、很深的对象引用路径,咱们不得不在这个路径上追踪对象。在某种程度上,这种混乱状态反映了现实世界,由于现实世界中就不多有清晰的边界。但这倒是软件设计中的一个重要问题。设计

假设咱们从数据库中删除一个Person对象。这我的的姓名、出生日期和工做描述要一块儿被删除,但要如何处理地址呢?可能还有其余人住在同一地址。若是删除了地址,那些Person对象将会引用一个被删除的对象。若是保留地址,那么垃圾地址在数据库中会累积起来。虽然自动垃圾收集机制能够清除垃圾地址,但这也只是一种技术上的修复;就算数据库系统存在这种处理机制,一个基本的建模问题依然被忽略了。3d

即使是在考虑孤立的事务时,典型对象模型中的关系网也使咱们难以判定一个修改会产生哪些潜在的影响。仅仅由于存在依赖就更新系统中的每一个对象,这样作是不现实的。对象

在多个客户对相同对象进行并发访问的系统中,这个问题更加突出。当不少用户对系统中的对象进行查询和更新时,必须防止他们同时修改互相依赖的对象。范围错误将致使严重的后果。

在具备复杂关联的模型中,要想保证对象更改的一致性是很困难的。不只互不关联的对象须要遵照一些固定规则,并且紧密关联的各组对象也要遵照一些固定规则。然而,过于谨慎的锁定机制又会致使多个用户之间毫无心义地互相干扰,从而使系统不可用。

换句话说,咱们如何知道一个由其余对象组成的对象从哪里开始,又到何处结束呢?在任何具备持久化数据存储的系统中,对数据进行修改的事务必需要有范围,并且要有保持数据一致性的方式(也就是说,保持数据遵照固定规则)。数据库支持各类锁机制,并且能够编写一些测试来验证。但这些特殊的解决方案分散了人们对模型的注意力,很快人们就会回到“走一步,看一步”的老路上来。

实际上,要想找到一种兼顾各类问题的解决方案,要求对领域有深入的理解,例如,要了解特定类实例之间的更改频率这样的深层次因素。咱们须要找到一个使对象间冲突较少而固定规则联系更紧密的模型。

尽管从表面上看这个问题是数据库事务方面的一个技术难题,但它的根源却在模型,归根结底是因为模型中缺少明肯定义的边界。从模型获得的解决方案将使模型更易于理解,而且使设计更易于沟通。当模型被修改时,它将引导咱们对实现作出修改。

首先,咱们须要用一个抽象来封装模型中的引用。AGGREGATE就是一组相关对象的集合,咱们把它做为数据修改的单元。每一个AGGREGATE都有一个根(root)和一个边界(boundary)。边界定义了AGGREGATE的内部都有什么。根则是AGGREGATE所包含的一个特定ENTITY。对AGGREGATE而言,外部对象只能够引用根,而边界内部的对象之间则能够互相引用。除根之外的其余ENTITY都有本地标识,但这些标识只在AGGREGATE内部才须要加以区别,由于外部对象除了根ENTITY以外看不到其余对象。

汽车修配厂的软件可能会使用汽车模型。如图6-2所示。汽车是一个具备全局标识的ENTITY:咱们须要将这部汽车与世界上全部其余汽车区分开(即便是一些很是类似的汽车)。咱们可使用车辆识别号来进行区分,车辆识别号是为每辆新汽车分配的惟一标识符。咱们可能想经过4个轮子的位臵跟踪轮胎的转动历史。咱们可能想知道每一个轮胎的里程数和磨损度。要想知道哪一个轮胎在哪儿,必须将轮胎标识为ENTITY。当脱离这辆车的上下文后,咱们极可能就再也不关心这些轮胎的标识了。若是更换了轮胎并将旧轮胎送到回收厂,那么软件将再也不须要跟踪它们,它们会成为一堆废旧轮胎中的一部分。没有人会关心它们的转动历史。更重要的是,即便轮胎被安在汽车上,也不会有人经过系统查询特定的轮胎,而后看看这个轮胎在哪辆汽车上。人们只会在数据库中查找汽车,而后临时查看一下这部汽车的轮胎状况。所以,汽车是AGGREGATE的根ENTITY,而轮胎处于这个AGGREGATE的边界以内。另外一方面,发动机组上面都刻有序列号,并且有时是独立于汽车被跟踪的。在一些应用程序中,发动机能够是本身的AGGREGATE的根。

固定规则(invariant)是指在数据变化时必须保持的一致性规则,其涉及AGGREGATE成员之间的内部关系。而任何跨越AGGREGATE的规则将不要求每时每刻都保持最新状态。经过事件处理、批处理或其余更新机制,这些依赖会在必定的时间内得以解决。但在每一个事务完成时,AGGREGATE内部所应用的固定规则必须获得知足,如图6-3所示。

如今,为了实现这个概念上的AGGREGATE,须要对全部事务应用一组规则。

根ENTITY具备全局标识,它最终负责检查固定规则。

根ENTITY具备全局标识。边界内的ENTITY具备本地标识,这些标识只在AGGREGATE内部才是惟一的。

AGGREGATE外部的对象不能引用除根ENTITY以外的任何内部对象。根ENTITY能够把对内部ENTITY的引用传递给它们,但这些对象只能临时使用这些引用,而不能保持引用。根能够把一个VALUE OBJECT的副本传递给另外一个对象,而没必要关心它发生什么变化,由于它只是一个VALUE,再也不与AGGREGATE有任何关联。

做为上一条规则的推论,只有AGGREGATE的根才能直接经过数据库查询获取。全部其余对象必须经过遍历关联来发现。

AGGREGATE内部的对象能够保持对其余AGGREGATE根的引用。

图6-3

删除操做必须一次删除AGGREGATE边界以内的全部对象。(利用垃圾收集机制,这很容易作到。因为除根之外的其余对象都没有外部引用,所以删除了根之后,其余对象均会被回收。)

当提交对AGGREGATE边界内部的任何对象的修改时,整个AGGREGATE的全部固定规则都必须被知足。

咱们应该将ENTITY和VALUE OBJECT分门别类地汇集到AGGREGATE中,并定义每一个AGGREGATE的边界。在每一个AGGREGATE中,选择一个ENTITY做为根,并经过根来控制对边界内其余对象的全部访问。只容许外部对象保持对根的引用。对内部成员的临时引用能够被传递出去,但仅在一次操做中有效。因为根控制访问,所以不能绕过它来修改内部对象。这种设计有利于确保AGGREGATE中的对象知足全部固定规则,也能够确保在任何状态变化时AGGREGATE做为一个总体知足固定规则。

有一个可以声明AGGREGATE的技术框架是颇有帮助的,这样就能够自动实施锁机制和其余一些功能。若是没有这样的技术框架,团队就必须靠自我约束来使用事先商定的AGGREGATE,并按照这些AGGREGATE来编写代码。

示例 采购订单的完整性

一个典型的采购订单(Purchase Order,PO)视图,它被分解为采购项(Line Item),一条固定规则是采购项的总量不能超过PO总额的限制。当前实现存在如下3个互相关联的问题。

(1) 固定规则的实施。当添加新采购项时,PO检查总额,若是新增的采购项使总额超出限制,则将PO标记为无效。正如咱们将要看到的那样,这种保护机制并不充分。

(2) 变动管理。当PO被删除或存档时,各个采购项也将被一块处理,但模型并无给出关系应该在何处中止。在不一样时间更改部件(Part)价格所产生的影响也不明确。

(3) 数据库共享。数据库会出现因为多个用户竞争使用而带来的问题。

多个用户将并发地输入和更新各个PO,所以必须防止他们互相干扰。让咱们从一个很是简单的策略开始,当一个用户开始编辑任何一个对象时,锁定该对象,直到用户提交事务。这样,当George编辑采购项001时,Amanda就没法访问该项。Amanda能够编辑其余PO上的任何采购项(包括George正在编辑的PO上的其余采购项)

每一个用户都将从数据库读取对象,并在本身的内存空间中实例化对象,然后在那里查看和编辑对象。只有当开始编辑时,才会请求进行数据库锁定。所以,George和Amanda能够同时工做,只要他们不一样时编辑相同的采购项便可。一切正常,直到George和Amanda开始编辑同一个PO上的不一样采购项.

从这两个用户和他们各自软件的角度来看,他们的操做都没有问题,由于他们忽略了事务期间数据库其余部分所发生的变化,并且每一个用户都没有修改被对方锁定的采购项。当这两个用户保存了修改以后,数据库中就存储了一个违反领域模型固定规则的PO。一条重要的业务规则被破坏了,但并无人知道

显然,锁定单个行并非一种充分的保护机制。若是一次锁定一个PO,能够防止这样的问题发生

直到Amanda解决这个问题以前,程序将不容许保存这个事务,Amanda能够经过提升限额或减小一把吉他来解决此问题。这种机制防止了问题,若是大部分工做分布在多个PO上,那么这多是个不错的解决方案。但若是是不少人同时对一个大PO的不一样项进行操做时,这种锁定机制就显得很笨拙了。

即使是不少小PO,也存在其余方法破坏这条固定规则。让咱们看看“Part”。若是在Amanda将长号加入订单时,有人更改了长号的价格,这不也会破坏固定规则吗?

咱们试着除了锁定整个PO以外,也锁定Part,当George、Amanda和Sam在不一样PO上工做时将会发生的状况。

工做变得愈来愈麻烦,由于在Part上出现了不少争用的状况。这样就会发生图6-10中的结果:3我的都须要等待。

如今咱们能够开始改进模型,在模型中加入如下业务知识。

(1) Part在不少PO中使用(会产生高竞争)。

(2) 对Part的修改少于对PO的修改。

(3) 对Price(价格)的修改不必定要传播到现有PO,它取决于修改价格时PO处于什么状态。

当考虑已经交货并存档的PO时,第三点尤其明显。它们显示的固然是填写时的价格,而不是当前价格。

这个模型获得的实现能够确保知足PO和采购项相关的固定规则,同时,修改部件的价格将不会当即影响引用部件的采购项。涉及面更广的规则能够经过其余方式来知足。例如,系统能够天天为用户列出价格过时的采购项,这样用户就能够决定是更新仍是去掉采购项。但这并非必须一直保持的固定规则。经过减小采购项对Part的依赖,能够避免争用,而且可以更好地反映出业务的现实状况。同时,增强PO与采购项之间的关系能够确保遵照这条重要的业务规则。

AGGREGATE强制了PO与采购项之间符合业务实际的所属关系。PO和采购项的建立及删除很天然地被联系在一块儿,而Part的建立和删除倒是独立的。

AGGREGATE划分出一个范围,在这个范围内,生命周期的每一个阶段都必须知足一些固定规则。接下来要讨论的两种模式FACTORY和REPOSITORY都是在AGGREGATE上执行操做,它们将特定生命周期转换的复杂性封装起来……

我的理解:彻底没看懂,好像是建模处理资源竞争的问题。

FACTORY

当建立一个对象或建立整个AGGREGATE时,若是建立工做很复杂,或者暴露了过多的内部结构,则可使用FACTORY进行封装。

对象的功能主要体如今其复杂的内部配臵以及关联方面。咱们应该一直对对象进行提炼,直到全部与其意义或在交互中的角色无关的内容被彻底剔除为止。一个对象在它的生命周期中要承担大量职责。若是再让复杂对象负责自身的建立,那么职责过载将会致使问题。

汽车发动机是一种复杂的机械装臵,它由数十个零件共同协做来履行发动机的职责——使轴转动。咱们能够试着设计一种发动机组,让它本身抓取一组活塞并塞到汽缸中,火花塞也能够本身找到插孔并把本身拧进去。但这样组装的复杂机器可能没有咱们常见的发动机那样可靠或高效。相反,咱们用其余东西来装配发动机。或许是机械师,或者是工业机器人。不管是机器人仍是人,实际上都比两者要装配的发动机复杂。装配零件的工做与使轴旋转的工做彻底无关。只是在生产汽车时才须要装配工,咱们驾驶时并不须要机器人或机械师。因为汽车的装配和驾驶永远不会同时发生,所以将这两种功能合并到同一个机制中是毫无价值的。同理,装配复杂的复合对象的工做也最好与对象要执行的工做分开。

正如对象的接口应该封装对象的实现同样(从而使客户无需知道对象的工做机理就可使用对象的功能),FACTORY封装了建立复杂对象或AGGREGATE所需的知识。它提供了反映客户目标的接口,以及被建立对象的抽象视图。

应该将建立复杂对象的实例和AGGREGATE的职责转移给单独的对象,这个对象自己可能没有承担领域模型中的职责,但它还是领域设计的一部分。提供一个封装全部复杂装配操做的接口,并且这个接口不须要客户引用要被实例化的对象的具体类。在建立AGGREGATE时要把它做为一个总体,并确保它知足固定规则。

FACTORY有不少种设计方式。[Gamma et al.1995]中详尽论述了几种特定目的的建立模式,包括FACTORY METHOD(工厂方法)、ABSTRACT FACTORY(抽象工厂)和BUILDER(构建器)。该书主要研究了适用于最复杂的对象构造问题的模式。本书的重点并非深刻讨论FACTORY的设计问题,而是要代表FACTORY的重要地位——它是领域设计的重要组件。正确使用FACTORY有助于保证MODEL-DRIVEN DESIGN沿正确的轨道前进。

任何好的工厂都需知足如下两个基本需求

(1) 每一个建立方法都是原子的,并且要保证被建立对象或AGGREGATE的全部固定规则。FACTORY生成的对象要处于一致的状态。在生成ENTITY时,这意味着建立知足全部固定规则的整个AGGREGATE,但在建立完成后能够向聚合添加可选元素。在建立不变的VALUE OBJECT时,这意味着全部属性必须被初始化为正确的最终状态。若是FACTORY经过其接口收到了一个建立对象的请求,而它又没法正确地建立出这个对象,那么它应该抛出一个异常,或者采用其余机制,以确保不会返回错误的值。

(2) FACTORY应该被抽象为所需的类型,而不是所要建立的具体类。[Gamma et al.1995]中的高级FACTORY模式介绍了这一话题

我的理解:这个我懂了,对象的建立若是很是复杂(要组装或者初始化不少参数,规则必定)就可使用工厂模式,技术开发人员应该很是了解工厂模式,业务不须要知道啥是工厂模式。

总结:没看懂,这部分更加注重程序对象和数据库的设计,还有就是模型的生命周期,好好了解下设计模式和数据库设计范式应该就够了。

相关文章
相关标签/搜索