在DDD中进行数据访问?

时间:2020-03-05 18:57:13  来源:igfitidea点击:

在阅读了Evan和Nilsson的书之后,我仍然不确定如何在域驱动的项目中管理数据访问。 CRUD方法应该是存储库的一部分,即OrderRepository.GetOrdersByCustomer(customer),还是应该是实体的一部分:Customer.GetOrders()。后一种方法看起来更面向对象,但是它将在多个对象之间分配单个实体类型的数据访问权限,即Customer.GetOrders(),Invoice.GetOrders(),ShipmentBatch.GetOrders()等。插入和更新呢?

解决方案

回答

即使在DDD中,我也会将数据访问类和例程与实体分开。

原因是,

  • 可测性提高
  • 关注点分离和模块化设计
  • 从长远来看,当我们添加实体,例程时,更易于维护

我不是专家,只是我的意见。

回答

CRUD-ish方法应该是Repository ... ish的一部分。但我认为我们应该问为什么要使用一堆CRUD方法。他们到底在做什么?他们真正的目的是什么?如果我们实际调用应用程序使用的数据访问模式,我认为它使存储库更加有用,并且当域发生某些类型的更改时,我们就不必进行having弹枪手术了。

CustomerRepo.GetThoseWhoHaventPaidTheirBill()

// or

GetCustomer(new HaventPaidBillSpecification())

// is better than

foreach (var customer in GetCustomer()) {
    /* logic leaking all over the floor */
}

"保存"类型的方法也应该是存储库的一部分。

如果我们有聚合根,则可以避免存储库爆炸或者逻辑分散到各处:我们没有4 x实体数据访问模式,只有在聚合根上实际使用的数据访问模式。

那是我的$ .02.

回答

尼尔森(Nilsson)的Applying DDD&P令人讨厌的事情是,他总是以"我不会在现实世界中的应用程序中开始,而是..."开头,然后是他的示例。返回主题:我认为OrderRepository.GetOrdersByCustomer(customer)是必经之路,但是ALT.Net Mailing列表上也有讨论(http://tech.groups.yahoo.com/group/altdotnet/)关于DDD。

回答

我已经完成了我们所谈论的两种方法,我现在首选的方法是持久性的无知(或者PONO -Plain Ole的.Net对象)方法,其中域类只担心成为域类。他们对如何持久化甚至持久化一无所知。当然,我们有时必须对此务实,并允许使用诸如Id之类的东西(但即使那样,我也只使用具有Id的图层超类型,这样我就可以在其中保留默认值之类的东西)

这样做的主要原因是我努力遵循"单一责任"原则。通过遵循这一原则,我发现我的代码更具可测试性和可维护性。当需要更改时,进行更改也容易得多,因为我只需要考虑一件事。

要注意的一件事是存储库可能遭受的方法膨胀。 GetOrderbyCustomer..GetAllOrders..GetOrders30DaysOld..etc等。解决此问题的一个好方法是查看查询对象模式。然后,存储库可以只接受一个查询对象来执行。

我也强烈建议我们研究NHibernate之类的东西。它包含许多使存储库如此有用的概念(身份映射,缓存,查询对象..)

回答

与我们用Customer.Save提示的活动记录模式相比,DDD通常更喜欢存储库模式。

Active Record模型的一个缺点是它几乎假定一个单一的持久性模型,除非使用某些特别侵入性的代码(在大多数语言中)。

存储库接口在域层中定义,但是不知道数据是否存储在数据库中。使用存储库模式,我可以创建一个InMemoryRepository,以便可以隔离地测试域逻辑,并在应用程序中使用依赖项注入来使服务层实例化SqlRepository。

对很多人来说,拥有一个专门用于测试声音的特殊存储库是很愚蠢的,但是,如果使用存储库模型,我们可能会发现我们确实不需要特定应用程序的数据库。有时,一个简单的FileRepository就能解决问题。在我们知道自己需要数据库之前,先将自己嫁接到数据库上可能会受到限制。即使需要数据库,对InMemoryRepository运行测试也要快得多。

如果我们对域逻辑的了解不多,则可能不需要DDD。 ActiveRecord非常适合解决许多问题,特别是如果我们主要是数据并且只有一点逻辑的话。

回答

让我们退后一步。 Evans建议存储库返回聚合的根,而不仅仅是实体。因此,假设客户是一个包含订单的汇总根,那么当我们从其存储库中获取客户时,订单就会随之而来。我们可以通过将客户之间的关系导航到订单来访问订单。

customer.Orders;

因此,为回答问题,CRUD操作存在于汇总根存储库中。

CustomerRepository.Add(customer);
CustomerRepository.Get(customerID);
CustomerRepository.Save(customer);
CustomerRepository.Delete(customer);