对象自创建后,可能会经历各种不同的状态,直至最终消亡——要么存档,要么删除。

生命周期的开始阶段,使用 FACTORY(工厂)来创建和重建复杂对象和 AGGREGATE(聚合),从而封装它们的内部结构。最后,在生命周期的中间和末尾使用 REPOSITORY(存储库)来提供查找和检索持久化对象并封装庞大基础设施的手段。

1、AGGREGATE

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

AGGREGATE 就是一组相关对象的集合,我们把它作为数据修改的单元。每个 AGGREGATE 都有一个根(root)和一个边界(boundary)。边界定义了 AGGREGATE 的内部都有什么。根则是 AGGREGATE 所包含的一个特定 ENTITY。对 AGGREGATE 而言,外部对象只可以引用根,而边界内部的对象之间则可以互相引用。除根以外的其他 ENTITY 都有本地标识,但这些标识只在 AGGREGATE 内部才需要加以区别,因为外部对象除了根 ENTITY 之外看不到其他对象。

固定规则(invariant)是指在数据变化时必须保持的一致性规则,其涉及 AGGREGATE 成员之间的内部关系。而任何跨越 AGGREGATE 的规则将不要求每时每刻都保持最新状态。

  • 根 ENTITY 具有全局标识,它最终负责检查固定规则。边界内的 ENTITY 具有本地标识,这些标识只在 AGGREGATE 内部才是唯一的。

  • AGGREGATE 外部的对象不能引用除根 ENTITY 之外的任何内部对象。根可以把一个 VALUEOBJECT 的副本传递给另一个对象,而不必关心它发生什么变化,因为它只是一个 VALUE,不再与 AGGREGATE 有任何关联。(对内部成员的临时引用可以被传递出去,但仅在一次操作中有效)

  • 删除操作必须一次删除 AGGREGATE 边界之内的所有对象。(由于除根以外的其他对象都没有外部引用,因此删除了根以后,其他对象均会被回收)

2、FACTORY

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

对象的创建本身可以是一个主要操作,但被创建的对象并不适合承担复杂的装配操作。让客户直接负责创建对象又会使客户的设计陷入混乱,并且破坏被装配对象或 AGGREGATE 的封装,而且导致客户与被创建对象的实现之间产生过于紧密的耦合。

FACTORY 封装了创建复杂对象或 AGGREGATE 所需的知识,相当于一个建造者,封装复杂细节

决定了需要封装内部细节,那么方法在哪里实现最合适呢?

  • 在 AGGREGATE 的根上创建一个 FACTORYMETHOD。这样就可以把 AGGREGATE 的内部实现细节隐藏起来,使任何外部客户看不到这些细节,同时使根负责确保 AGGREGATE 在添加元素时的完整性。

  • 在一个对象上使用FACTORYMETHOD,这个对象与生成另一个对象密切相关,但它并不拥有所生成的对象,这样就不用提取生成对象所需要的信息。

在一些简单场景下面并不需要使用到专门的工厂,只需要使用目标构造函数即可,构造函数的定义也有要求:不要在构造函数中调用其他类的构造函数,构造函数应该保持绝对简单。

2.1 接口设计

当设计 FACTORY 的方法签名时,无论是独立的 FACTORY 还是 FACTORYMETHOD,都要记住以下两点:

  • 每个操作都必须是原子的。

  • Factory 将与其参数发生耦合。

第一个很好理解,你不能在里面去调用一些乱七八糟的逻辑。第二个和构造函数的参数一样,传递的参数将会影响最终的对象创建,为了减少这个耦合,最好外层提供一个配置类的接口实现。

3、REPOSITORY

为每种需要全局访问的对象类型创建一个对象,这个对象相当于该类型的所有对象在内存中的一个集合的“替身”。通过一个众所周知的全局接口来提供访问。提供添加和删除对象的方法,用这些方法来封装在数据存储中实际插入或删除数据的操作。

提供根据具体条件来挑选对象的方法,并返回属性值满足查询条件的对象或对象集合(所返回的对象是完全实例化的),从而将实际的存储和查询技术封装起来。只为那些确实需要直接访问的 AGGREGATE 根提供 REPOSITORY。让客户始终聚焦于模型,而将所有对象的存储和访问操作交给 REPOSITORY 来完成。

REPOSITORY 概念在很多情况下都适用。可能的实现方法有很多,这里只能列出如下一些需要谨记的注意事项。

  • 对类型进行抽象。类型可以是一个层次结构中的抽象超类,类型也可以是一个接口——接口的实现者并没有层次结构上的关联,也可以是一个具体类。记住,由于数据库技术缺乏这样的多态性质,因此我们将面临很多约束。

  • 充分利用与客户解耦的优点。利用解耦来优化性能,因为这样就可以使用不同的查询技术,或在内存中缓存对象,可以随时自由地切换持久化策略。通过提供一个易于操纵的、内存中的(in-memory)哑实现,还能够方便客户代码和领域对象的测试。

  • 将事务的控制权留给客户。尽管 REPOSITORY 会执行数据库的插入和删除操作,但它通常不会提交事务。保存数据后紧接着就提交似乎是很自然的事情,但想必只有客户才有上下文,从而能够正确地初始化和提交工作单元。

每一个独立的 AGGREGATE 应该拥有自己的 REPOSITORY 对象。