什么是DDD中的数据访问?

10

在阅读了Evan和Nilsson的书籍后,我仍然不确定如何管理领域驱动项目中的数据访问。CRUD方法应该是存储库的一部分,例如OrderRepository.GetOrdersByCustomer(customer),还是实体的一部分:Customer.GetOrders()?后者似乎更符合面向对象的思想,但它会把单个实体类型的数据访问分散到多个对象中,例如Customer.GetOrders()、Invoice.GetOrders()、ShipmentBatch.GetOrders()等。那插入和更新呢?

6个回答

15

CRUD-ish 方法应该是 Repository 的一部分...ish。但我认为你应该问问为什么有一堆 CRUD 方法。它们到底是做什么的?它们真正的作用是什么?如果你能明确指出应用程序使用的数据访问模式,我认为这会使存储库变得更加有用,并且避免在某些类型的更改发生时进行散弹手术。

CustomerRepo.GetThoseWhoHaventPaidTheirBill()

// or

GetCustomer(new HaventPaidBillSpecification())

// is better than

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

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

如果您有聚合根,这将防止您出现存储库爆炸或逻辑扩散:您不会有4 x实体数据访问模式的数量,只有在聚合根上实际使用的那些。

这是我的意见。

"

5

DDD通常更喜欢使用存储库模式而不是您在Customer.Save中暗示的Active Record模式。

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

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

对于许多人来说,专门为测试创建一个特殊的存储库听起来很傻,但如果您使用存储库模型,则可能会发现您的特定应用程序实际上不需要数据库;有时一个简单的FileRepository就足够了。在您知道需要它之前,将自己与数据库绑定可能具有潜在的限制。即使需要数据库,对于InMemoryRepository运行测试要快得多。

如果您没有太多领域逻辑,那么您可能不需要DDD。ActiveRecord非常适合许多问题,特别是如果您主要拥有数据并且只有少量逻辑。


4

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

customer.Orders;

因此,回答你的问题,CRUD操作存在于聚合根存储库中。
CustomerRepository.Add(customer);
CustomerRepository.Get(customerID);
CustomerRepository.Save(customer);
CustomerRepository.Delete(customer);

3
我已经尝试了你所说的两种方法,但我现在更喜欢持久化无知(或PONO——普通的.Net对象)方法,其中你的领域类只关心作为领域类。它们不知道如何持久化,甚至不知道是否持久化。当然,有时你必须实用主义一些,比如允许使用Id(但即使这样,我也只使用一个具有Id的层超类型,以便可以有一个单一点,其中诸如默认值之类的东西存在)。
这样做的主要原因是我努力遵循单一职责原则。通过遵循这个原则,我发现我的代码更易于测试和维护。当需要进行更改时,它也更容易,因为我只需要考虑一件事情。
需要注意的一件事是存储库可能会遭受的方法膨胀。GetOrderByCustomer.. GetAllOrders.. GetOrders30DaysOld..等等。解决这个问题的一个好方法是查看查询对象模式。然后你的存储库只需接受一个查询对象来执行。
我还强烈建议研究NHibernate之类的东西。它包括了使存储库如此有用的许多概念(标识映射,缓存,查询对象等)。

我在思考,在业务类中不要注入仓库,这样领域类就不用关心数据是否持久化,只需处理业务逻辑。但是,从数据库获取数据并保存的代码应该放在哪里?是在业务逻辑的服务中还是在应用层的服务中? - Álvaro García

2

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

原因如下:

  1. 测试性能得到提升
  2. 关注点分离和模块化设计
  3. 随着您添加实体、例程,更易于长期维护

我不是专家,这只是我的观点。


1
Nilsson的应用DDD&P让人烦恼的是他总是以“我不会在真实世界的应用中这样做,但是…”开始,然后跟着他的例子。回到主题:我认为OrderRepository.GetOrdersByCustomer(customer)是正确的方法,但是ALT.Net邮件列表(http://tech.groups.yahoo.com/group/altdotnet/)上也有关于DDD的讨论。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接