书山天道-领域驱动设计-第二遍精读

目标:深入特定知识点


2021-02-12

1. 分层架构 用户界面层; 应用层;领域层;基础设施层; p44

各层之间松散连接,上层可以直接使用或者操作下层元素,方法是通过调用下层元素的公共接口,保持对下层的引用(至少是暂时的),

以及采用常规的交互手段,

如果下层元素需要与上层元素通信(不只是回应直接查询),则需要采用另一种通信机制,使用架构模式来连接上下层,

回调模式或 Observers 模式

将 用户界面层与 应用层和领域层 相连的模式 : MVC ,变体: Model-View separation 模式, Application coordinator 应用协调器是连接应用层的一种方法。

只要连接方式能够维持领域层独立性,保证在设计领域对象时不需要同时考虑可能与其交互的用户界面,那么连接方式都是可行的。

 


 

约束 和 过程 是两大类  模型概念,一旦被我们视为模型元素,就真的可以让我们的设计更为清晰。

约束是模型概念中非常重要的类别,通常是隐含的,将它们显示地表现出来可以极大地提高设计质量.

但如果是执行的逻辑比较复杂的话,就会像所有隐式概念一样淹没掉被约束的对象或操作.

规则十分简单时,放在同一类里的方法,以命名方法来表述,比较容易理解。”有名有姓“的概念。

但约束条件是无法用单独的方法来轻松表达的. 即使方法自身能够保持其简单性,可能会调用一些信息,

但对于对象的主要职责而言,这些信息毫无用处,这种规则就不适合放到现在的对象中.

警告信号: 表明约束的存在 正在扰乱其 ”宿主对象“ (Host Object ) 的设计

  1. 计算约束所需的数据从定义上看并不属于这个对象.
  2. 相关规则在多个对象中出现, 造成了代码重复或导致不在同一族的对象之间产生了继承关系.
  3. 很多设计和需求是围绕这些约束进行的,而在代码实现中, 它们却隐藏在过程代码中.

如果约束的存在掩盖了对象的基本职责,

或者约束在领域中非常突出,但在模型中却不明显,

那么就可以将其提取到一个显示的对象中,甚至可以把它建模为一个对象和关系的集合。


 

大部分规则可以归类几种特定的情况,可以借用谓词概念创建可计算出布尔值的特殊对象,

为特殊目的创建谓词形式的显式的 Value Object, Specification 就是一个谓词,可用来确定对象是否满足某些标准.

Specification 将规则保留在领域层, 由于规则是一个完备的对象,所以这种设计能够更加清晰地反映模型,

利用工厂,可以用来自其他资源(客户账户或企业政策数据库)的信息对规则进行配置。之所以使用 Factory, 是为了避免

Invoice直接访问这些资源,这样会使得Invoice 与这些资源发生不正确得关联(Invoice 基本职责是请求付款,而这些资源与这一职责无关)


 


基础设施层不会发起领域层中的操作,处于“领域层”之下,不包含其所服务的领域中的知识,

这种技术能力常以 Service 形式提供。

消息发送的接口放在基础设施层,应用层中元素可以请求发送消息,

最主要的好处是简化了应用层,使其只专注与自己所负责的工作。

应用层和领域层可以调用基础设施层所提供的 Service, 把详细行为封装到服务接口中,调用程序就可以保持与 Service的松散连接,自身会很简单。

并非所有的基础设施都是以可供上层调用的 Service 形式出现,

有些技术组件被设计成直接支持其他层的基本功能,如为所有的领域对象提供 抽象基类,且提供关联机制 (如 MVC 及类似框架),架构框架。


架构框架:

明确使用目的:建立一种可以表达领域模型的实现,并且用它来解决重要问题。

以J2EE 应用程序为例,将所有的领域对象实现为“实体bean" ,不但影响程序性能,还会减慢开发速度,

最佳实践,利用J2EE框架来实现大粒度对象,而用普通Java对象来实现大部分的业务逻辑。


领域驱动设计只需要一个特定的层存在即可。

领域模型是一系列概念的集合。

”领域层“ 则是领域模型以及所有与其直接相关的设计元素的表现,它由业务逻辑的设计和实现组成,在MDD中,

领域层的构造反映了模型概念。


软件中所表示的模型

表示模型的3种模型元素模式: Entity, Value object 和 Service

一个对象是用来表示某种

具有连续性和标识的事物呢(可以跟踪其所经历的不同状态,甚至可以跨不同的实现跟踪它)

还是用来描述某种状态的属性呢。

这是 Entity 与 Value object 之间的根本区别。 


 

领域种还有一些方面适合用动作或操作来表示,这比用对象更加清楚,最好用Service来表示,应客户端的请求来完成某事,

当对软件要做的某项无状态活动进行建模。


  关联

设计必须指定一种具体的遍历机制,这种遍历的行为应该与模型种的关联一致。

”多对多“,会使实现和维护变得很复杂,很少能表示出关系的本质:

至少由3种方法可以使得关联更易于控制:

1 规定一个遍历方向

2.添加一个限定符,以便有效地减少多重关联

3. 消除不必要的关联

被约束的关联,反映了对领域的深入理解,也是一种更实用的设计。  P53

坚持将关联限定为 领域所倾向的 方向。 


 

Specification

最有价值的地方在于它可以将看起来完全不同的应用功能统一起来。出于以下3个目的中的一个或多个,可能需要指定对象的状态

1. 验证对象。检查它是否满足能够满足某些需求,或者是否已经为实现某个目标做好了准备。

2. 从集合中选出一个对象,如查询过期的发票

3. 指定在创建新对象时必须满足某种需求。

 

 

 

 


2021-02-13

1 限定 对象间的关联 来简化其多重性,从而对模型进行精化。

不管是什么特殊的规则,只要发现了关联的约束,就应该将这些约束添加到模型和实现中,可以使模型更精确,使实现更而易于维护。

将 Set 集合转由 Map<T>  调用方法 增加的参数,代表着约束


 Entity

在对象的多个实现,存储形式和真实世界的参与者之间,概念性标识必须使匹配的,属性可以不匹配。

分布式软件中,多个用户可能从不同地点输入数据,需要在不同的数据库中异步地协调这些更新事务,使它们传播到真个系统.

满足两个条件即可:

1.它在整个声明周期中具有连续性,

2.它的区别并不是由那些对用户非常重要的属性决定的。


 当一个对象由其标识(而不是属性)区分时,那么在模型中应该主要通过标识来确定该对象的定义。

使类定义变得简单,并集中关注生命周期的连续性和标识。

模型必须定义出“符合什么条件才算是相同的事物”。


Entity 最基本的职责是确保连续性,以便使其行为更清楚且可预测。

保持实体的简练是实现这一责任的关键。不要将注意力集中在属性或行为上,摆脱这些细枝末节。

尤其是用于 识别查找匹配 对象的特征。

只添加那些对概念 至关重要的 行为 和这些 行为所必须的属性。

将行为和属性 转移到与 核心实体关联的其他对象中, Entity 或 Value object ,

除了标识问题之外, 实体往往通过协调 其他关联对象的操作 来完成自己的职责。


只关心一个模型元素的属性时,归类为 Value Object, Value object 是不可变对象,

value object 所包含的属性应该形成一个概念整体。在概念上应该是一个整体, Whole Value 模式.

Service

有些操作本质上是一些活动或动作,而不是事物.

Service 是作为接口提供的一种操作,在模型中是独立的,不像Entity 和 Value object 具有封装的状态。

强调的是与其他对象的关系。与Entity 和 Value object 不同,只是定义能够为客户做什么。

Service 往往以一个活动来命名,而不是以一个Entity来命名,它是动词而不是名词.

Service 也应该有定义的职责,这种职责以及履行它的接口也应该作为领域模型的一部分来加以定义。

参数和结果应该是 领域对象.


好的Service 有以下3个特征:

1.与领域概念相关的操作不是Entity 或 Value object 的一个自然组成部分.

2. 接口是根据领域模型的其他元素定义的。

3. 操作是无状态的.

Service 执行是将使用可全局访问的信息,甚至会更改这些全局信息,可能有副作用,但Service不保持影响其自身行为的状态,

这一点与大多数领域对象不同。

将模型中的独立操作声明为一个 Service, 而不是声明为一个不代表任何事情的虚拟对象,避免对任何人产生误导.


当领域中某个重要的过程转换操作 不是 Entity 或 Value Object 的自然职责时, 应该在模型中添加一个作为独立接口的操作,并将其声明为 Service.

定义接口时要使用模型语言,并确保操作名称是 ubiquitous language 中的术语。

Service 应该是无状态的. 


文献中所讨论的大多数Service是纯技术的Service,它们都属于基础设施层。

应用层Service和领域层Service可能很难区分。应用层负责通知的设置,而领域层负责确定是否满足临界值。

如 资金转账 在银行领域语言中是一项有意义的操作,且涉及基本的业务逻辑,因此作为领域层的Service,而纯技术的Service无任何业务意义。

Service 只是要求在两个 Account 对象完成大部分工作,如果将“转账”操作强加在 Account对象上会很别扭,这个操作涉及了两个账户和一些全局规则。

在一个领域对象和外部资源之间直接建立一个接口是很别扭的。可以利用 Facade 将这样的外部Service包装起来,这个外观可能以模型作为输入,并返回

一个 “Funds Transfer" 对象,作为它的结果。

但无论中间涉及什么Service,甚至那些超出我们掌控范围的 Service, 这些Service都是在履行资金转账的领域职责。

 

粒度:大型系统中,中等粒度的,无状态的Service更容易被复用,它们呢在简单的接口背后封装了重要功能,

而细粒度的对象可能导致分布式系统的消息传递的效率低下。

便于对组件进行打包。

有时,Service是标识领域概念最自然的方式。


规则引擎 p78

重要的是在使用规则的同时要继续考虑模型,团队必须找到能够同时适用于两种实现范式的单一模型。

条件是规则引擎支持否由表达力的实现方式。

如果不这样,数据和规则就会失去联系。与领域模型中的概念相比,引擎中的规则更像是一些较小的程序。

只有保持规则与对象之间紧密,清晰的关系,才能确保显示出这二者表达的含义。

如果没有无缝的环境,就要完全靠开发人员提炼出一个由清晰的基本概念组成的模型,以便完全支撑整个设计.

将各个部分紧密结合在一起的最有效工具就是健壮的 Ubiquitous Language. 它是构成整个异构模型的基础.



领域对象的生命周期

如何知道一个由其他对象组成的对象从哪里开始,又到何处结束?

在任何具有持久化数据存储的系统中,对数据进行修改的事务必须要有范围,且要保持数据一致性。DB支持各种锁,但是特殊解决方案。

要对领域又深刻的理解,要了解特定类实例之间的更改频率这样的深层次因素。需要找到一个使对象间冲突较少而固定规则练习更紧密的模型.

invariant 数据变化时必须保持的一致性规则. 涉及Aggregate 成员之间的内部关系.

跨越 Aggregate 的规则不要求每时每刻都保持最新状态,通过 事件处理,批处理,依赖会在一定时间内得以解决。

事务完成时,内部所应用的固定规则必须得到满足。

为此:需要对所有的 事务应用一组规则:

1.根 Entity 具有全局标识,最终负责检查固定规则。

2.根Entity 具有全局标识。边界内的Entity具有本地标识,这些标识只有在Aggregate内部才是唯一的。

3. Aggregate 外部的对象不能引用除根Entity之外的任何外部对象。

根Entity 可以把对内部Entity的引用传递给它们,但这些对象只能临时使用这些引用,而不能保持引用.

根可以把一个 Value object 的副本传递给另一个对象,不必管他,与Aggregate无关了。

4.只有 Aggregate 的根才能直接通过数据库查询数据,所有其他对象必须通过遍历关联来发现。

5.Aggregate内部的对象可以保持对其他Aggregate 根的引用。

6. 删除操作必须一次删除Aggregate边界之内的所有对象(利用垃圾收集机制,除根意外的其他对象都没有外部引用,因此删除以后,其他对象均会被回收)

7.当提交对Aggregate边界内部的任何对象的修改时,整个Aggregate的所有固定规则都必须被满足. 


在每个Aggregate中,选择一个Entity作为根,并通过根来控制对边界内其他对象的所有访问。

只允许外部对象保持对根的引用。

对内部成员的临时引用可以被传递出去,但仅在一次操作中有效。

由于根控制访问,因此不能绕过它来修改内部对象。

这种设计有利于确保Aggregate中的对象满足所有固定规则,也可以确保在任何状态变化时Aggregate作为一个整体满足固定规则.


示例 p88  采购订单: Purchase Order 与 Purchase Order Line Item [ quantity ]  的约束 总价不超limit , part [ price ]                  

并发修改:

1.锁定 PO,并发会破坏规则而无法保存。

2.锁定整个 PO 但是 part 的修改也会导致破坏规则。

3. 要锁定整个 PO, 同时锁定 part item,

逻辑复杂,降低了并发性。

该井模型,加入业务知识

1. Part 在很多PO中使用(会产生高竞争)

2. 对Part的修改少于对 PO的修改

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


将 Part 里的 Price 复制到 LineItem中,可以确保满足聚合的固定规则了。

将 PO 与 Purchase Order Line Item 组为 Aggregate, 强制了符合业务实际的所属关系. 它们的创建及删除很自然地联系在一起。

而 Part 的创建和删除却是独立的。


Factory

当创建一个对象 或 创建 整个 Aggregate 时, 如果创建工作 很复杂, 或者暴露了 过多的内部结构, 使用 Factory 进行封装.

对象的功能主要体现在其复杂的内部配置 以及 关联 方面,

要一直对对象进行精炼,直到所有与其意义 或在交互中的 角色 无关的内容被完全剔除为止。

如果再让复杂对象 负责自身的创建, 职责过载 会导致问题。


装备复杂的复合对象的工作也最好与对象要执行的工作分开.

不要将职责转移给client 对象,客户必须知道对象的规则,调用构造函数也会使客户与索要构建的对象的具体类产生耦合。

对领域对象实现所做的任何修改都要求客户做出相应修改,使得重构变得更加困难


复杂对象的创建是领域层的职责。任务并不属于哪些用于表示模型的对象.

要比 构造函数 更加抽象 且不与其他对象发生耦合的构造机制 -- Factory, 负责创建其他对象的程序元素.

 Factory 负责生成满足 客户 和 内部规则的对象.

对象的接口封装对象的实现一样,Factory 封装了创建复杂对象或 Aggregate 所需的知识,提供了反映客户目标的接口,以及被创建对象的抽象视图.

提供一个封装所有复杂装配操作的接口,而且这个接口不需要客户引用要被实例化的对象的具体类。

在创建Aggregate 时要把它作为一个整体,确保它满足规则。


Factory

通常与Aggregate有关

1. Factory Method , 2. Abstract Factory,  3. Builder

好的工厂需要满足两个基本需求

1. 每个创建方法都是原子的,要保证被创建对象或 Aggregate 的所有固定规则。Factory 生成的对象要处于一致的状态

在生成 Entity 时,意味着要创建满足所有固定规则的整个 Aggregate, 但在创建完成后可以向聚合添加可选元素。

在创建不变的 Value Object 时,意味着所有属性必须被初始化正确的最终状态

如果Factory 通过其接口接到一个创建对象的请求,它又无法正确地创建这个对象,它应该抛出一个异常,或采用其他机制,

以确保不会返回错误的值。

2. Factory 应该被抽象为所需的类型,而不是所要创建的具体类。


示例1:

需要向一个已存在的 Aggregate 添加元素,可以在Aggregate的根上创建一个Factory Method。

这样就可以把 Aggregate 的内部实现细节隐藏起来,使任何外部客户看不到这些细节,同时使根负责确保 Aggregate 在添加元素时的完整性.

示例2:

在一个对象上使用 Factory Method, 这个对象与生成另一个对象密切相关,但它并不拥有所生成的对象。

当一个对象的创建主要是使用另一个对象的数据(或许还有规则)时,在后者的对象上创建一个 Factory  Method, 这样就不必将后者的信息

提取到其他地方来创建前者,有利于表达前者与后者的关系.


使用构造函数的情景:


 

接口的设计

设计 Factory 的方法签名时,无论是独立的Factory还是 Factory Method. 要满足以下两点:

1. 每个操作都必须是原子的。 必须在与 Factory 的一次交互中把创建对象所需的所有信息传递给 Factory。

  同时必须确定当创建失败时将执行什么操作,例如固定规则没有被满足,可以抛出一个异常或仅仅返回 null,

  为了保持一致,可考虑采用编码标准来处理所有Factory的失败.

2. Factory 将与其参数发生耦合。

  如果选择输入参数不小心,会产生错综复杂的依赖关系。

  耦合程度取决于对 参数 的处理。如果只是简单地将参数插入到要构建的对象中,则依赖是适中的。

  如果从参数中选出一部分在构造对象中使用,耦合将更紧密。


 

最安全的参数是那些来自较低设计层的参数,即使是同一层,也有一种自然分层的倾向。  参考 Chapter 10 , Chapter 16.


选择模型中与被构建对象密切相关的对象,这样不会增加新的依赖,

Purchase Order Item , Factory Method 将 Catalog Part 作为一个参数,它是 Item 的一个重要的关联,

这在 Purchase Order类 和 Part 之间增加了直接依赖。

这3个对象组成了一个关系密切的概念小组。

使用抽象类型的参数,而不是它们的具体类。

Factory 与被构建对象的具体类发生耦合,而无需与具体的参数发生耦合。


固定规则的相关逻辑应放置在 Factory 中.

1. 把应用于一个对象的规则移到该对象外部之前应三思。Factory 可以将固定规则的检查工作委派给被创建对象,且这通常是最佳选择。

2. Factory 已经知道了被创建对象的内部结构,且创建Factory的目的与被创建对象的实现有密切的联系. 某些情况下,把固定规则的相关逻辑放在

Factory 中是有好处的,可以让被创建对象的职责更清晰,对于Aggregate 规则来说尤其如此,这些规则会约束很多对象。

3. 但固定规则的逻辑却特别不适合放到那些 与其他领域对象关联的 Factory Method 中.

4. 虽然原则上每个操作结束时都应该应用固定规则,但通常对象所允许的转换可能永远也不会用到这些规则。 例如,Entity 标识属性的赋值需要一条固定规则,

但创建后会一直不变,Value Object 则完全不变。

如果逻辑在对象的生命周期内永远也不被用到,对象就没有必要携带这个逻辑。

此时 Factory 是放置固定规则的合适地方,可使创建出来的对象更简单。


Value Object Factory 创建时必须得到被创建对象的完整描述,

Entity Factory 则只需具有构造有效 Aggregate 所需的那些属性。 对于固定规则不关心的细节,可以之后再添加。

Enttiy 分配标识的问题,

如果是用户提供一个标识符,则必须作为参数显式传递给 Factory,

程序分配标识时, Factory 是控制它的理想场所,虽然唯一跟踪ID 实际上是由数据库“序列” 或 其他基础设施机制生成的。

Factory 知道需要什么样的标识,以及将标识放到何处.


重建已存储的对象

对象存储或通过网络传输,网络传输要将对象转换为平面数据才能传输。

使得对象只能以非常有限的形式出现。

因此,检索操作潜在地需要一个复杂的过程将各个部分重新装配成一个可用的对象。


重建对象的 Factory 和 创建对象的 Factory 很类似。

1. 用于重建对象的 Entity Factory 不分配新的跟踪ID,以免丢失先前对象的连续性。重建Factory中,标识属性必须是输入参数的一部分。

2.当固定规则未被满足时,重建对象的 Factory 采用不同的方式处理

 新建对象Factory在此情况下可简单拒绝创建,但重建对象时说明对象已经存在于系统的某个地方,不能忽略。也不能任凭规则被破坏。

  要通过 1. 数据库重建对象时,对象映射技术可提供部分或全部所需服务。 其他介质重建对象时,使用Factory, 

  1. 从关系数据库检索一个Entity 并重建它, ORM

        2. 重建以 xml 形式传输的Entity.  xml parse

   Factory 封装了对象创建和 重建时生命周期转换。


对象与存储之间的互相转换 -- repository.

 


 

Repository

通过对象之间的关联来找到对象,但当它处于生命周期的中间时,必须要有一个起点,以便从这个起点遍历到一个 Entity 或 Value。

在所有持久化对象中,有一小部分必须通过基于对象属性的搜索来全局访问。

当很难通过遍历方式来访问某些Aggregate 根的时候,就需要使用这种访问方式。通常是:

1. Entity

2. 具有复杂内部结构的  Value object,

3. 枚举 Value.

而其他对象则不宜使用这种访问方式,因为这会混淆它们之间的区别。

随意的数据库访问会破坏领域对象的封装 和 Aggregate。

技术基础设施 和 数据库访问机制的 暴露会增加客户的复杂度, 并妨碍 模型驱动的设计。


获得对象引用

1. 创建操作,返回对新对象的引用。

2. 遍历关联,面向对象,对象之间的链接。 首先必须获得作为起点的那个对象。具有更强的表达力。

3.基于对象的属性,执行查询来找到对象;或者是找到对象的组成部分,重建它


数据库搜索是全局可访问的,可以直接访问任何对象。所有对象不需要相互联结起来,整个对象关系网就能够保持在可控的范围。

是提供 遍历 还是 依靠 搜索,成为一个设计决策,需要在搜索的解耦与关联的内聚之间做出权衡。

从概念上讲,把Customer 对象保存在数据库中,然后检索,并不代表一个新客户,要视为 重建。 


反例:

开发人员构造了一个SQL查询,并将它传递给基础设施层中的某个查询服务,然后再根据得到的表行结果集提取出所需信息,

最后将这些信息传递给构造函数或 Factory。

这一连串操作,就没把模型当作重点了,自然地会把对象看作容器来放置查询出来的数据,整个设计就转向了数据处理风格.

客户处理的是技术,而不是模型。

客户代码直接使用数据时,开发人员会视图绕过模型的功能, Aggregate, 甚至是 对象封装。

而直接获取和操作他们所需的数据。

导致越来越多的领域规则被嵌入到查询代码中,甚至丢失。


最重要的:

不需要对那些很容易通过遍历来找到的持久对象进行查询访问。例如,地址可通过Person 对象获取。

除了通过根来遍历查找对象这种方法外,禁止用其他方法对 Aggregate 内部的任何对象进行访问.


不要绕过 Aggregate 的根来得到这些对象。会导致领域逻辑进入查询和客户代码中,而 Entity 和 Value Object 则编程单纯的数据容器。

采用大多数处理数据库访问的技术复杂性很快就会使客户代码变得混乱,这将导致开发人员简化领域层,使模型变得无关紧要。


 

临时对象,通常是 value object, 只存在很短时间,在客户操作中用到它们时才创建,用完就删除。


对 Value object 不要全局搜索访问,通过属性找到VO 相当于用这些属性创建一个新实例。

例外: 枚举 ,一个类型有一组严格限定的,预定义的可能值。

如果确实需要在数据库中搜索一个已存在的 Value, 要考虑其是否是一个 Entity, 只是尚未识别出它的标识。 


Query Object, Metadata mapping  《企业应用架构模式》 chapter 13, p217. 解决数据库访问的难题,

Factory 帮助重建对象。


Repository 将某种类型的所有对象表示为一个概念集合(通常是模拟的)。它的行为类似于集合( collection ), 只是具有更复杂的查询功能。

在添加或删除相应类型的对象时,Repository 的后台机制负责将对象添加到数据库中,或从数据库中删除对象。

这个定义将一组紧密相关的职责集中在一起,这些职责提供了对Aggregate根的整个生命周期的全程访问。

Client 使用 查询方法向 Repository 请求对象,这些查询方法根据客户所指定的条件(特定属性的值)的挑选对象。

Repository 检索被请求的对象,并封装数据库查询 和 元数据映射机制。

client -> selection criteria (客户根据模型来请求它所需的对象) repository -> delegate ( repository将数据库访问计数和策略封装起来) 

1. 数据库接口

2. MetaData mapping

3. Factory

4. 其他 repository

5. Query Object

为某种需要全局访问的对象类型创建一个对象, 这个对象相当于该类型的所有对象在内存中的一个集合的“替身”。

通过一个众所周知的全局接口来提供访问。

提供 添加  删除 的方法。封装在数据存储中实际插入和删除数据的操作。

提供根据具体条件来挑选对象的方法。并返回属性值满足查询条件的对象或对象集合(对象时完全实例化的)

从而将实际的存储和查询计数封装起来。

只为那些确实需要直接访问的 Aggregate 根提供 Repository,让客户始终聚焦于模型。

将所有对象的存储和访问操作交给 Repository 完成

1. 为客户提供了一个简单的模型,用来获取 持久化对象 并 管理它们的 生命周期。

2. 使 应用程序 和 领域设计 与 持久化 技术 解耦。

3. 容易测试,使用内存中的集合。

接口设计:

1. 最容易的 硬编码的方式 来实现一些特定参数的查询。

通过标识特定的属性值, 或复杂的参数组合   来请求

一个对象集合, 根据值域(如日期范围)来选择对象, 甚至可以执行某些属于 Repository 一般职责范围内的计算(特别

是利用那些底层数据库所支持的操作)例如 汇总计算,对象数目,或模型需要对某个数值属性进行统计。


 大量查询: Specificiation 基于规格的查询是将 Repository 通用化的好办法,创建一个对象来实际执行筛选操作。 chapter 9.

 

 

 


即使一个 Repository 的设计采取了灵活的查询方式,也应该郧西添加专门的硬编码查询。

作为便捷方法,可封装常用查询或不返回对象(如返回的是选中对象的汇总计算)。

不支持这些特殊查询方式的框架有可能会扭曲领域设计,或干脆被开发人员弃之不用。


 

注意事项

1.对类型进行抽象. Repository “含有”特定类型的所有实例,但这并不意味着每个类都需要有一个 Repository。 

    类型可以是一个层次结构中的抽象超类。(TradeOrder 可以是 BuyOrder 或 SellOrder ) 。

    类型可以是一个接口--接口的实现者并没有层次结构上的关联,也可以是一个具体类。

 数据库技术缺乏这样的多态性质,将面临很多约束.

2.充分利用与客户解耦的优点。可以很容易修改 Repository的实现。可以利用解耦来优化性能,这样可以使用不同的查询技术,或在内存中缓存对象。可以随时

自由地切换持久化策略。方便客户代码和领域对象的测试。

3.将事务的控制权留给客户: repository 执行数据库的插入和删除操作,但通常不提交事务。只有客户才有上下文,从而能够正确地初始化和提交工作单元。


 

在基础设施层中添加框架,用来支持Repository的实现。

Repository 超类除了与较低层的基础设施组件进行协作外,还可以实现一些基本查询,特别是要实现的灵活查询时。

类似Java的类型系统,该方法会使返回的对象只能是 Object 类型,用户要转换为 Repository包含的类型。如果Java查询返回的对象是集合时。要转换。


Repository 使用 Factory来重建一个已有对象;

客户使用Factory 创建一个新对象后,使用Repository来存储

Factory 摆脱持久化职责,factory 的工作是用数据来实例化一个可能很复杂的对象。


 

不要将 Entity 和 Value object 对象分开,会让很多功能不复存在。


 

为关系数据库设计对象

1. 数据库是对象的主要存储库

2.数据库是为另一个系统设计的。

3.数据库是为这个系统设计的,但它的任务不是用与存储对象。


 

 如果数据库模式 database schema 是专门为对象存储设计的,那么接受模型的一些限制是值得的。可以让映射变得简单些。

设计数据库结构,使得在更新数据时能更安全地保证聚合的完整性,并使数据更新变得更加高效。

从技术上来看,关系表的设计不必反映出领域模型。映射工具可以消除二者之间的巨大差别。

避免将分析与设计模型分开。确实会牺牲一些对象模型的丰富性,而且有时必须在数据库设计中做出一些这种(有些地方不能规范化)

数据库被视作对象存储时,数据模型与对象模型的差别不应太大,可以牺牲一些对象关系的丰富性,以保证它与关系模型的紧密关联,

如果有助于简化对象映射的话,不妨牺牲某些正式的关系标准(规范化)


对象系统外部的过程不应该访问这样的对象存储,它们可能会破坏对象必须满足的固定规则。它们的访问会锁定数据模型,重构很难修改模型。

大多数情况:

简单的对应关系才是最好的。

表中的一行应该包含一个对象,也可能还包含 Aggregate 中的一些附属项。

表中的外键应该转换为对另一个Entity对象的引用.有时不得不违背简单,但不要全盘放弃简单映射的规则。

Ubiquitous Language 可能有助于将对象和关系组件联系起来,使之成为单一的模型。

对象中的元素的名称 和 关联 应该严格地对应于关系表中 相应的项。


2021-03-09

Customer 概念在大型系统中,Customer 可能具有多种角色,以便与许多对象交互,因此不要将它限定为具体的职责。

可以通过Repository Customer 来查找 Cargo.

循环引用在设计时有时是必要的,但维护复杂,实现时,避免把必须同步的信息保存在两个不同的地方,这样对我们的工作很有帮助。

List 对象处理,简易。

数据库查询,查询不频繁,

直接引用,频繁。


 

posted @ 2021-02-12 12:57  君子之行  阅读(60)  评论(0)    收藏  举报