第六章、实现一个持久性适配器

在第1章中,我对传统的分层架构大加挞伐,并声称它支持 “基于数据库的设计”,因为归根结底,一切都取决于持久化层。在这一章中。 我们将看看如何使持久化层成为应用层的插件,以颠覆这种 依赖性

依赖性反转

端口实际上是应用程序服务和持久性代码之间的间接层。让我们提醒我们自己,我们添加这一层间接性是为了能够实现无需考虑持久性问题,这意味着不对持久性层的代码依赖。在持久性代码中的重构并不一定会导致 核心中的代码更改。

当然,在运行时,我们仍然有一个从应用程序核心到持久性适配器的依赖关系。例如,如果我们修改持久性层中的代码并引入一个错误,我们仍然可能中断 在应用程序核心中的功能。但是,只要端口的契约得到了满足,我们就可以在持久性适配器中自由地做我们想做的事情,而不影响核心。

持久性适配器的职责

让我们来看看持久性适配器通常会做些什么:

1、接受输入

2、将输入映射到数据库格式中

3、将输入发送到数据库

4、将数据库输出映射为应用程序格式

5、返回输出

持久性适配器通过一个端口接口接收输入。输入模型可以是域实体或专用于特定数据库操作的对象。然后,它将输入模型映射到一种它可以用来修改或查询数据库的格式。在Java项目中,我们通常使用Java持久性API(JPA)与数据库进行通信,因此我们可以映射 输入到反映数据库表结构的JPA实体对象中。根据上下文的不同,将输入模型映射到JPA实体可能需要做很多工作,但并没有什么好处,所以我们将在第8章“边界之间的映射”中讨论没有映射的策略。

我们可以不使用JPA或其他对象关系映射框架,而是使用任何其他的技术来与数据库对话。我们可以将输入模型映射成普通的SQL语句,并将这些语句发送给数据库。或者我们可以将输入的数据序列化到文件中,然后从文件中读取它们从那里读回来。

重要的部分是,持久性适配器的输入模型位于应用程序核心( the application core)中,而不是持久性适配器本身中,因此持久性适配器中的更改不会 影响核心。

接下来,持久性适配器将查询数据库并接收查询结果。

最后,它将数据库的答案映射到端口所期望的输出模型中,并返回它。再说一遍。 重要的是,输出模型位于应用核心内,而不是在持久化适配器内。

除了输入和输出模型位于应用核心而不是持久化适配器本身这一事实之外 ,持久化适配器本身,其职责与传统的持久化层没有什么不同。

切片端口接口

接口隔离原则就解决了这个问题。它指出,宽的接口应该被划分为特定的接口,以便客户端只知道他们需要的方法。

应用接口隔离原则可以消除不必要的依赖关系,并使现有的依赖关系更加可见。

每个服务现在只依赖于它实际需要的方法。更重要的是,这些端口的名称清楚地说明了它们的作用。在测试中,我们不再需要考虑哪些方法需要模拟,因为大多数情况下,每个端口只有一个方法。

当然,“每个端口都有一个方法”的方法可能并不适用于所有情况。可能有一组数据库操作非常内连贯,经常在一起使用,我们可能想要在一个接口中展开它们。

切片持久性适配器

在上面的数字中,我们看到了一个单一的持久化适配器类,它实现了所有的持久化 端口。然而,没有任何规则禁止我们创建一个以上的类,只要所有的持久性 端口被实现。

例如,我们可以选择在每个域类中实现一个持久性适配器,为此我们需要持久性操作(或DDD术语中的“聚合”),如图19所示。

我们可能会把持久化适配器分成更多的类,例如,当我们想用JPA或另一个OR-Mapper实现几个持久化端口,而用普通SQL实现其他一些端口以获得更好的性能。我们可以创建一个JPA适配器和一个普通SQL适配器,每个适配器实现一个持久化端口的子集。

请记住,我们的领域代码并不关心哪个类最终履行了由持久化端口定义的契约。我们可以自由地在持久化层中做我们认为合适的事情,只要所有的端口被实现。

使用SpringData JPA的示例

那么数据库事务处理呢?

事务应该将在某个用例中执行的所有写操作跨到数据库,以便如果其中一个操作失败,所有这些操作都可以回滚。

由于持久性适配器不知道哪些其他数据库操作是同一用例的一部分,因此它无法决定何时打开和关闭事务。我们必须将此责任委托给协调对持久性适配器的调用的服务。

使用Java和Spring实现这一点的最简单方法是添加应用程序服务类的@Transactional注释,这样Spring将用事务包装所有公共方法:

如果我们希望我们的服务保持纯粹,不被@Transactional注释所玷污,我们可以使用面向方面的编程(例如使用AspectJ),以便将事务边界编织到我们的代码库中。

这将如何帮助我构建可维护的软件呢?

构建一个作为域代码插件的持久性适配器可以将域代码从持久性细节中解放出来,这样我们就可以构建一个丰富的域模型。

使用狭窄的端口接口,我们可以灵活地以这种方式实现一个端口,以这种方式实现另一个端口,甚至可能使用不同的持久性技术,应用程序不会注意到。我们的甚至可以关闭完整的持久性层,只要遵守端口契约。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值