DDD 关于设计决策

DDD About a design decision

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

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

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

我有一个创建流程的用例,其逻辑是创建一个有效的流程实例并使用 ProcessRepository 持久化它。嗯,我想我有两个选项来应用进程设置:

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

您认为使用哪种方法最正确?或者您是否以其他方式实施?

提前致谢!

Our product owner told us that if the client paid for some settings in a moment and created a process that settings will be valid for that process if the client does not update it. If the client leave to paid some settings then, when the client want to update that process our system will not allow update it because the actual settings will not be fit to the process data

鉴于基于流程设置的验证仅需在流程 creation/update 场景中发生,这使得实施变得更加容易。此外,我猜竞态条件也与业务无关,例如,如果在进程获得 created/updated.

的同时更改设置

鉴于此,我们可以假设 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来封装creation/update逻辑或者你甚至可以认为查找逻辑不够复杂而直接放在应用层.

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

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