仓储模式 vs. 透明持久化

发布者:    |      

我最近阅读了《领域驱动设计》这本书,这本书似乎成为了一些“完全以对象思考”的人的新圣经。我甚至在HiA和JPwH中提到了它——不幸的是,我之前没有读过。

现在我认为,这本书所推崇的许多模式实际上是一种倒退。我还缺少每个模式的优缺点列表(就像GoF书中那样)。所以读者必须猜测何时以及何时一个模式可能适用,何时不适用。或者,当缺点列表会超过优点列表时。

一个例子是/Repository/模式。如果我的理解正确,这是它的工作方式(DDD作者说这个类似的总结是正确的)

  • 开发者不想决定他们是否应该调用order.getLineItems()还是应该调用lineItemDAO.findAllForOrderId(order.getId())如果他们需要从数据存储中获取一串行项。(我完全同意这是一个典型且常见的问题——然而,我不同意提出的解决方案。)
  • 我们可以限制这一点,并通过强制访问始终通过一个“仓储”来实现这一点。例如,只有一个OrderRepository而且获取行项的唯一方式是OrderRepository.getLineItems(order)。这个仓储事物内部使用DAO来实际获取数据。所以我们在仓储中封装DAO,创建了一个额外的层。你不应该再调用DAO了。
  • 现在,我们将OrderRepository通用于一个接口,并将DAO访问放入OrderRepositoryImpl.
  • 最后:然后我们将这个具体的OrderRepository实例注入到Order类中,这样当你调用order.getLineItems()时,它将内部访问仓储。这需要拦截领域模型实例的创建,但在这些天到处都是容器/工厂/服务,这很容易做到。没有人再创建一个“新”的实例,对吧?

其优势似乎在于存储库使用领域语言(嗯,什么?这似乎与将ID作为DAO查找器参数传递有关...)以及,嗯,当你想要访问数据存储时,你不再需要做出这些艰难的选择,因为只有一种方式可以获取这些内容。

我认为这种模式是倒退的,不应该推荐

  1. 如果你不想在order.getLineItems()lineItemDAO.findAllForOrderId(order.getId())之间做出选择删除其中一个方法!没有人强迫你在你的类上映射持久化lineItemsOrder集合。你之所以这样做,是因为你喜欢以这种方式加载数据时获得的功能(在Hibernate中:使用批次或子选择透明预取其他删除其中一个方法!没有人强迫你在你的类上映射持久化集合,在DAO查找器中不能很好地实现)。如果你不需要这些,就不要创建它。对于你的领域模型中的任何其他关联也是如此。或者反过来,如果没有DAO查找器方法和领域模型遍历之间的区别,就删除DAO方法。
  2. 如果确实存在区别order.getLineItems()lineItemDAO.findAllForOrderId(order.getId())- 例如,一个返回更多的关联内容,如每个line item的产品 - 保持两者都存在。我经常这样做。我会适当地记录它,说这个查找器方法对每个line item的产品进行了预加载,并在getter方法上写这返回line items,产品是未加载的代理。当然,我两者都需要。实际上,我把DAO查找器重命名为lineItemDAO.findAllForOrderIdWithProducts(order.getId())或者,如果我想说领域语言,没有任何阻止我将其定义为lineItemDAO.findAllForOrderWithProducts(order)。因为我不喜欢重复,所以我最终得到lineItemDAO.findWithProducts(order)。现在很容易决定我是调用这个,还是order.getLineItems()- 它们有不同的预取策略和预取计划。
  3. 你可以争论,如果一切看起来都一样会很好。也就是说,遵循这个存储库模式,我会得到一个这样的APIorder.getLineItems()order.getLineItemsWithProducts()。但我必须付出的代价是A)在应用程序中引入一个至少与DAO层一样复杂的新层(在概念上,它们之间实际上没有太大的区别)和B)将我的领域模型耦合到这些存储库。因为我认为通用、基于接口的DAO架构和这些存储库之间没有真正的区别,这意味着在运行时将DAO注入到我的领域模型实例中。这意味着它们没有DAO(模拟或真实)就不能运行。反复包装DAO并没有改变这一点。
  4. 如果你没有在领域模型类中注入和调用存储库,你什么也没得到。你只是在DAO之上创建了一个看起来像DAO的新层,尽管名字很奇怪。

当您必须决定持久化层和服务如何与系统其余部分集成时,我的个人最佳实践是这些

  • 通过仔细设计领域模型关联和持久化层API(后者遵循一些好的DAO模式和相关的最佳实践)来优化访问路径并简化数据访问。使用你透明的持久化服务的功能来确保所有这些访问路径都做正确的事,取决于你需要什么。
  • 适当地记录访问路径,并说明何时应该使用。我采用了一些简单的约定,例如:除非绝对必要,否则不要映射持久化集合——这已经使事情变得容易多了。
  • 保持领域模型实现干净、可测试和可重用:在任何这些类中都没有安全检查、事务划分或明确的数据存储访问。

返回顶部