DDD – 如何对数据库进行验证

DDD – How to implement validation against database

我正在努力解决一个基本问题。项目是用C#写的,但是问题很一般

我遵循始终有效的对象原则。作为一个例子,我有一个产品实体,它有一个强制性的 属性“ProductCategory”。允许的产品类别是用户定义的,因此会保留(在数据库中)。

为了类型安全和更好的阅读,我为各种属性定义了值对象来封装这些类型的业务规则。所以有一个 ProductCategory class。创建 ProductCategory 实例时,工厂方法会检查例如在创建实例之前传递给工厂的字符串的最大长度。这确保了 ProdcutCategory 的每个实例都是有效的。

长度等简单参数检查简单直接。我的问题是在哪里实施针对持久存在的可能值的验证检查。允许值的存储库隐藏了持久性技术并具有方法 Exists 或 IsValid。

选项 1) 在调用工厂之前从应用层调用存储库。

这里的域层与许多人提倡的 repository/infrastructure 层没有依赖关系。但是工厂不能再确保一个有效的对象。业务规则的实现依赖于应用层。此外,每个需要 ProductCategory 的命令都必须检查存储库,这违反了 DRY。

选项 2) 从值对象工厂调用存储库

由于工厂是域层的一部分,该层必须能够访问存储库,这会引入额外的耦合。好处是ProductCategory的业务合法性由对象自身保证,无法规避。

对于这个困境是否有另一种解决方案,或者是否有任何特定的标准可以鼓励一个或另一个选择? 如果工厂是针对存储在域层中的存储库接口进行编程并且仅由基础设施层中的存储库实现的,是否可以? 我试图理解不同的方法,但在这种情况下我感到迷茫。

如果我理解正确,您的 ProductCategory 值对象会处理其自身的不变量,但是特定 ProductCategory 是否可以分配给特定 Product 是用户定义的并存储在数据库中。

您可以使用 DomainEvents:

  1. 您将类别分配给域模型中的产品。
  2. 产品添加了 DomainEvent“ProductCategoryAssigned”。
  3. 在提交您的工作单元之前,DomainEvent 处理程序会使用数据库检查特定类别是否可以分配给产品,如果没有则抛出。

这将防止事务以无效状态完成。

您可以使用 ValidatorAggregate:

如果您真的希望您的产品“始终有效”,甚至在提交工作单元之前,那么您可以选择:

  1. 创建一个 ValidCategoriesAggregate 和 Repository,以根据产品属性加载有效类别。
  2. 您的产品的“SetCategory”方法(或工厂方法)将 ValidCategoriesAggregate 声明为必需参数,强制您的应用层在调用工厂方法之前获取一个。
  3. 在您的工厂方法中,您可以调用“ValidCategoriesAggregate.HasCategory(category)”并在创建 Product 之前抛出(如果不存在)。

尽量避免将存储库注入域模型。

选项 2 似乎更好。
在这种情况下,耦合不是问题,您将工厂耦合到检查验证所需的查询,使这种依赖关系显式。

请注意,不让此检查最终保持一致的唯一方法是将所有数据放在一个聚合中。否则无法阻止在您进行检查之后和提交交易之前创建相同类别的事实(这是聚合的真正原因)。

只有在存在实际问题时才考虑最终一致性,创建类别的频率如何?产品的创建频率是多少?发生冲突的可能性有多大?如果发生这种情况,是否存在真正的问题?

大多数时候完全一致比接受最终一致性要昂贵得多。