《实现领域驱动设计》读书笔记摘抄(12)—— 资源库

Administrator 521 2022-05-08

第12章 资源库

本章学习路线图

  • 学习资源库的两种类型以及如何选用
  • 学习如何通过Hibernate、TopLink、Coherence和MongoDB来实现资源库
  • 学习为什么需要向资源库添加额外的行为
  • 学习类型层级设计资源库时所面临的挑战
  • 学习资源库和数据访问对象(DAO)之间的基本区别
  • 学习测试资源库的不同方法,以及如何利用资源库来进行测试

像集合一样的对象都是和持久化相关的。每一种聚合类型都将拥有一个资源库,通常来说,聚合类型和资源库之间存在着一对一的关系。然而有时,当两个或多个聚合位于同一个对象层级中时,他们可以共享同一个资源库。

作者对于资源库设计分为了两种类型,面向集合的设计和面向持久化的设计。

面向集合资源库

这是一种传统的方式,它提现了原生DDD资源库模式的基本思想。在多数面向对象语言中,我们都可以将对象添加到集合中,这些对象将一直驻留在集合里,直到被删除为止。书中以CollectionHashSet为例说明直接使用集合时可能面临的一些问题,如对对象的修改。并给出了高性能ORM工具Hibernate和TopLink的使用案例。

面向持久化资源

如果持久化机制不支持对对象变化的跟踪,无论是显式的还是隐式,那么采用面向集合资源库不再适用。可以考虑使用面向持久化资源库,这是一种基于保存操作的资源库。

在向数据存储中添加新建对象或修改既有对象时,我们都必须显式的掉用put()方法,该方法将以新的值来替换先前关联在某个键上的原值。这种类型的数据存储可以极大地简化对聚合的读写。正因如此,这种数据存储也成为聚合存储或面向聚合数据库。

在使用NoSql数据时,他们以键值对的方式存储数据,也具有Map的特征,但是他们使用的是磁盘而不是内存来做存储介质。后续书中结合具体项目使用Coherence和MongoDB来做示例。

额外的行为

对于资源库来说,除了前面讲到的那些典型行为之外,还可以向资源库接口中添加一些额外的行为。如计算聚合实例的总数。

public interface CalendarEntryRepository {
public int size();
}

在使用Hibernate时,可以实现为:

public int size() {
Query query = this.session().createQuery("SELECT COUNT(*) FROM CalendarEntity");
return ((Integer)query.uniqueResult()).intValue();
}

管理事务

对事务的管理绝对不应该放在领域模型和领域层中,通常将事务放在应用层中。对事务的管理过程都与一下执行过程相似:

public void doSomeUseCaseTask() {
Transaction tsc = null;
try {
tsc = this.session().beginTransaction();
// do sth
tsc.commit();
} catch (Exception ex) {
if (tsc != null) {
tsc.rollback();
}
}
}

以Spring为例,可以直接使用注解来声明事务:

@Transactional
public void doSomeUseCaseTask() {
// do sth
}

与事务管理相关的原则同样适用于TopLink等其他持久化机制。

需要注意的是,不要过度使用事务,我们需要慎重的设计正确的一致性边界(见**聚合(10)**章节),防止出现由并发所导致的事务失败。

类型层级

在面向对象语言开发领域模型时,我们通常喜欢通过继承来创建类型层级。此时,将默认状态和行为放在基类中,然后创建子类对其行为进行扩展。

本章讨论的是那种都扩展自一个特定于领域的超类。这些关联密切的聚合所组成的类型层级具有可互换性和多态性的特征。此时我们使用单个资源库来保存和获取层级中的不同聚合类型,而客户端无须知道他们所使用的实际类型。示例参考书中的ServiceProvider抽象基类。

测试资源库

对于资源库的测试,我们需要从两个方面来进行。首先,我们需要测试资源库本身是能正确工作的。其次,我们还要测试对资源库的使用,以保证能够正确地保存和获取聚合实例。