【问题标题】:How does persistence ignorance work with references to (non-root) aggregates?持久性无知如何处理对(非根)聚合的引用?
【发布时间】:2015-09-04 04:01:49
【问题描述】:

我们有几个聚合根,它们有两种主要的识别方式:

  • 一个整数“键”,在数据库中用作主键(通过引用聚合用作外键),在应用程序内部,不可访问公共网络 API。
  • 一个基于字符串的“id”,它也唯一地标识聚合根,并且可以被公共网络 API 访问。

有一个基于整数的私有标识符和一个基于字符串的公共标识符有几个原因 - 例如,数据库性能更好(8 字节整数而不是可变长度字符串)并且公共标识符难以猜测。

但是,类在内部使用基于整数的标识符相互引用,如果基于整数的标识符为 0,则表示对象尚未存储到数据库中。这会产生一个问题,因为实体在保存之后之前无法引用其他聚合根。

如何解决这个问题,或者我对持久性无知的理解存在缺陷?

编辑基于字符串的标识符

基于字符串的标识符由存储库生成,连接到 PostgreSQL 数据库,该数据库生成标识符以确保它不会与数据库中当前的任何内容发生冲突。例如:

class Customer {
    public function __construct($customerKey, $customerId, $name) {
        $this->customerKey = $customerKey;
        $this->customerId = $customerId;
        $this->name = $name;
    }
}

function test(Repository $repository, UnitOfWork $unitOfWork) {
    $customer = new Customer(0, $repository->generateCustomerId(), "John Doe");
    // $customer->customerKey == 0
    $unitOfWork->saveCustomer($customer);
    // $customer->customerKey != 0
}

我假设可以使用相同的概念来创建一个基于整数的键为非 0 的实体,并且工作单元可以使用它在数据库中不存在的事实作为插入的理由而不是更新。上面的test() 函数会变成:

function test(Repository $repository, UnitOfWork $unitOfWork) {
    $customer = new Customer($repository->generateCustomerKey(), $repository->generateCustomerId(), "John Doe");
    // $customer->customerKey != 0
    $unitOfWork->saveCustomer($customer);
    // $customer->customerKey still != 0
}

但是,鉴于上述情况,如果工作单元未按正确顺序保存数据库对象,则可能会发生错误。解决这个问题的方法是确保工作单元以正确的顺序保存实体吗?

我希望上述编辑能澄清我的情况。

【问题讨论】:

  • “实体在保存之前无法引用其他聚合根”-我可以用几种不同的方式解释,您可能违反了聚合根的边界吗?此外,为什么不将您的关系建模为引用 - 那么无论是否保存 id,您的对象图都是有效的。另见:lostechies.com/jimmybogard/2008/05/21/…
  • “为什么不将您的关系建模为引用”,您的意思是(C 样式指针类型)引用吗?例如,不是存储基于整数的键,而是存储对象的指针/引用?如果是这种情况,那么加载单个聚合将意味着加载“对象图”的很大一部分,这出于性能原因是不切实际的,并且会模糊所有权。

标签: entity domain-driven-design foreign-key-relationship aggregateroot persistence-ignorance


【解决方案1】:

这是一种将聚合视为一致性边界的好方法。换句话说,两个不同的聚合具有不同的生命周期,您应该避免将它们的命运捆绑在同一个事务中。根据该公理,您可以安全地声明,从另一个聚合 B 的角度来看,聚合 A 的 ID 永远不会为 0,因为创建 A 的事务尚未完成并且 B 不可见,或者它已经完成A 有一个 ID。

关于双重身份,我宁愿让语言生成字符串 ID 而不是数据库,因为我想提出一个唯一 ID 将意味着一个事务,可能跨越多个表。语言通常可以生成具有良好熵的唯一字符串。

【讨论】:

  • 根据我对 DDD 的理解,这个答案很有意义,但我目前从事的工作涉及基于大数据批量创建和更新大量聚合和关系文件。有两个阶段 - 验证和提交。验证规则要求关系到位,但如果数据无效,更新将不会继续。因为聚合关系依赖于非 0 键,而非 0 键依赖于数据库持久性,所以我不知道如何“避免将它们的命运捆绑在同一个事务中”。
  • 不会在你的 UoW 过程中通过中介 Save()'s 解决问题吗?
  • @user1420752 如果您解释了业务问题,而不是将自己限制在特定的技术方法上,也许我们可以为您提供更好的帮助。
猜你喜欢
  • 1970-01-01
  • 2014-08-11
  • 2013-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多