【问题标题】:DDD About a design decisionDDD 关于设计决策
【发布时间】:2019-03-19 22:06:20
【问题描述】:

我必须解决一个域问题,我对什么是更好的解决方案有些疑问。我将提出问题:

我有应用程序,每个应用程序都有许多进程Application 也有一些 ProcessSettings。当我必须创建一个流程时,我有一些业务规则,例如,基于应用程序的流程设置,我必须在一些流程属性上应用一些规则。

我将 Application 视为聚合根,将 Process 视为其他聚合根,并将 ProcessSettings 视为 Application 聚合内的值对象。

我有一个创建流程的用例,其逻辑是创建一个有效的流程实例并使用 ProcessRepository 将其持久化。好吧,我想我有两个选项可以应用流程设置:

  1. 在用例中,通过Application聚合中的域服务通过ApplicationId从Application聚合中获取进程设置,并将ProcessSettings传递给进程创建方法。
  2. 在用例中,创建流程并通过应用程序聚合中的域服务传递流程副本(值对象)以应用流程设置。

您认为哪种方法最适合使用?还是以其他方式实施?

提前致谢!

【问题讨论】:

  • 当进程设置以一种不再有效的方式更改或当进程以一种在创建后不再有效的方式更改时,现有进程会发生什么?
  • 好问题,你是对的,但我不想介绍所有要求,但现在我要这样做。好吧,客户购买了一个应用程序,他们为在您的应用程序中配置流程设置付费。我们的产品负责人告诉我们,如果客户立即支付了一些设置并创建了一个流程,那么如果客户不更新它,这些设置将对该流程有效。如果客户离开支付一些设置,那么当客户想要更新该流程时,我们的系统将不允许更新它,因为实际设置将不适合流程数据。

标签: domain-driven-design aggregateroot


【解决方案1】:

我们的产品负责人告诉我们,如果客户在 片刻,并创建了一个设置将对此有效的过程 如果客户端不更新它,则处理它。如果客户离开支付 然后进行一些设置,当客户想要更新我们的流程时 系统将不允许更新它,因为实际设置不会 适合过程数据

这使得实施更加容易,因为基于流程设置的验证只需要在流程创建/更新场景中进行。此外,我猜想竞争条件也与业务无关,例如如果在创建/更新流程的同时更改设置。

有鉴于此,我们可以假设ProcessSettingsProcess 可以处于不同的一致性边界。换句话说,两者都可以是单独聚合根的一部分。

此外,重要的是要认识到基于设置的验证不是Process 不变量,这意味着Process 本身不应该负责执行这些规则。由于这些不是不变量,因此您也不应该争取始终有效的策略,而应使用延迟验证策略。

从那时起,有很多很好的方法可以对这个用例进行建模,这些都可以归结为:

//Application layer service
void createProcess(processId, applicationId, data) {
    application = applicationRepository.applicationOfId(applicationId);
    process = application.createProcess(processId, data);
    processRepository.add(process);
}

//Application AR
Process createProcess(processId, data) {
    process = new Process(processId, this.id, data);
    this.processSettings.ensureRespectedBy(process);
    return process;
}

如果ProcessSettingsApplication AR 的一部分,那么在Application 上放置一个工厂方法来创建进程可能是有意义的,因为它拥有执行验证所需的状态,就像上面的例子一样。这样就无需为任务引入专用域服务,例如独立工厂。

如果ProcessSettings 可以是它自己的聚合根,您总是可以这样做,但为设置引入查找域服务:

//Application AR
Process createProcess(processId, data, settingsLookupService) {
    process = new Process(processId, this.id, data);
    processSettings = settingsLookupService.findByApplicationId(this.id);
    processSettings.ensureRespectedBy(process);
    return process;
}

有些人可能会说您的聚合不再是纯粹的,因为它通过调用settingsLookupService 执行间接 IO。如果你想避免这种依赖,可以引入ProcessDomainService之类的域服务来封装创建/更新逻辑,甚至可以考虑查找逻辑不够复杂,直接放在应用层。

//Application layer service
void createProcess(processId, applicationId, data) {
    processSettings = processRepository.findByApplicationId(applicationId);
    process = application.createProcess(processId, data, processSettings);
    processRepository.add(process);
}

我们无法判断哪种方法在您的特定情况下更好,有时甚至没有完美的方法,而且许多不同的方法可能同样好。根据经验,保持聚合纯粹是一个好主意,因为它更容易进行单元测试(更少模拟)。

【讨论】:

  • 感谢您的时间和解决方案。我同意,但现在我有一些问题。我非常喜欢你的第一种方法。在这种情况下,Process 将成为Application AR 中的一个实体,不是吗?如果是,如果一个应用程序有数千个进程会怎样?Process 应该是一个新的独立 AR 吗?
  • 另一个问题如下。在第二种方法(域服务)中,在Application 用例中找到ProcessSettings 聚合是否正确?我不会同时耦合两个聚合吗?
  • @user1129875 不,在第一种方法中,Process 仍然是它自己的聚合,但是您可以在 Application AR 上使用工厂方法来创建它,因为 Application 包含状态执行创建所必需的,例如将其自己的 ID 传递给 Process 并使用其设置进行验证。正如您在示例中看到的,Application.createProcess 返回一个 Process 实例,然后将其添加到应用层的存储库中。
  • @user1129875 是的,它会在两个 AR 之间引入某种程度的耦合,就像 Application 也耦合到 Process,因为它参与了它的创建。在保持务实和富有表现力的同时,很难不引入相关 AR 之间的耦合。我们密切关注普遍存在的语言(例如,“为应用程序创建进程”-> process = application.createProcess(...)),但代价是使用 process = new Process(...) 可以避免的耦合。很难说哪个最好,但 DDD 的重点通常是语言。
  • 好,那么,第一种方法,Application AR 不包含它创建的进程,不是吗?你只会使用那个 AR 来创建和更新进程,不是吗?吗?其他关于进程的操作直接在ProcessAR上,不是吗?
猜你喜欢
  • 1970-01-01
  • 2011-03-04
  • 2011-04-18
  • 1970-01-01
  • 1970-01-01
  • 2017-07-04
  • 2014-02-25
  • 2015-04-16
  • 2020-02-04
相关资源
最近更新 更多