在另一个聚合根

时间:2017-03-31 08:23:04

标签: java repository domain-driven-design aggregateroot

我目前正在努力在ddd环境中创建实例。 我已经阅读和搜索了很多,有时候认为我找到答案只是意识到编程时感觉不对。

这是我的情况:

  • 我有两个聚合根ScenarioStep。我做了那些AR 因为它们封装了域和每个AR的相关元素 应该处于一致的状态。
  • 多个Steps可以存在于 Scenario的上下文。它们不能独立存在。
  • Step上下文中每个Scenario的“名称/自然ID”必须是唯一的。 Scenario中的更改不会自动影响其Steps和 反之亦然(例如Step并不关心Scenario是否会改变一些 描述或图像)。
  • Steps的{​​{1}}可以同时使用,编辑等。

目前,每个Scenario都通过相应的自然标识符保存对其Step的引用。 Scenario类对其Scenario一无所知,因此它不会包含Steps引用的集合。

如何为给定的Step创建新的Step

  1. 我应该加载场景并调用类似的东西 Scenario就可以了?这不会强制实现独特性 约束(实际上是业务约束而不是 技术一),因为createNewStep(...)不知道它的Scenario。我可能不得不使用某种“断开连接的域模型”,或者将一个repsoitory或服务传递给该方法来执行检查。
  2. 我是否应该使用强制执行约束的域服务,查询存储库,最后创建并返回Steps
  3. Step应该只知道其Scenario吗?我想我想避免这个,因为这会产生难以维持的双向关系。
  4. 可以想象,Steps之类的其他用例应按特定Step提供的选项进行分类。在这种情况下,如果对Scenario的“集合”没有约束,我可能会选择第一个“解决方案”。然后再次:如果之后更改了分类,则需要访问该方案以检查允许的分类。这让我想到了一个可能的第四个解决方案:

    1. 使用某种可能解决方案的某种“组合”。创建域服务(访问所需的所有内容)并将其用作需要它的方法的参数是否是一个好主意?然后,该方法将在需要时调用服务,并且“域逻辑”保留在实体/模型中。
    2. 提前谢谢!

      我只是编辑而不是复制粘贴回答;)

      谢谢大家的回复! :)

      将步骤推回到场景中会导致一些非常大的对象,我试图避免(当前正在运行的应用程序真的受此影响)。它似乎与Scrum-Vaughns“有效聚合设计”的例子非常相似,他使用DomainServices来获得更小的聚合(我真的不知道为什么我对使用域服务这么不确定)。看起来我必须使用域服务或将聚合分解为“StepName”和“StepDetails”。

1 个答案:

答案 0 :(得分:1)

有关背景知识,您应该阅读Greg Young对set validation所说的内容(通过WaybackMachine)。特别是,您确实需要在解决方案的上下文中评估发生故障会对业务产生什么影响?

接受失败并升级是迄今为止最简单的选择。在下文中,我假设失败的业务影响很大,因此我们需要防止它发生。

  

"名称/自然ID"在其场景的上下文中的每个步骤必须是唯一的

这是经典的验证问题。

首先要做的是挑战模型中的假设

您的模型是" name"的记录簿?如果您的模型不是权限,则必须非常谨慎地引入约束。了解模型权限的界限非常重要。

是否存在将步骤的名称与其状态的任何其他部分耦合的不变量?聚合设计规则表示由不变量耦合的两个状态需要在同一个聚合中,但它对没有参与不变的属性保持沉默。

在接受对步骤的其他更改时拒绝更改名称是否合理?这实际上是以前的变体 - 可以将任务拆分为两个不同的命令(一个涉及名称,一个不能独立成功或失败?

简而言之,不变量可能会告诉您"步骤名称"作为一个状态,属于场景聚合而不是步骤聚合。

如果从关系模型的角度考虑问题,我们会查看元组(scenarioId,name,stepId),约束表示(scenarioId,name)形成唯一键。这暗示了步骤名称属于该场景。在代码中,该签名看起来像包含Map<ScenarioName, ScenarioId>的方案数据结构。

当然,这不一定能解决您的所有问题,但这是将模型与您的实际业务保持一致的一步。

什么时候不起作用......

&#34;真实&#34;答案是将步骤实体移回场景聚合。考虑它的一种方法是 - 所有实体一起形成&#34;模型&#34;我们保持一致。这些总量本身并不属于业务的一部分;它们是模型中的人工,独立细分 - 我们将聚合识别并隔离为性能优化;我们可以执行并发编辑,并在加载更小的数据集时评估命令的有效性。

如果失败使性能优化过于昂贵,那么就把它拿出来。所以你可以看到我们有各种各样的估计,这意味着业务影响是大的&#34 ;;它需要大于我们在快乐的道路上使用聚合所带来的节省。

另一种可能性是转移你执行不变量的地方。在集验证中,关系数据库非常好。因此,正确的答案可能是分解执行问题:将不变量作为约束放入模式中,并忽略代码中的约束。

出于多种原因,这不是理想的选择 - 您已经有效地&#34;隐藏了&#34;约束,您已经对用于聚合的数据存储类型引入了约束,您引入了一个约束,要求您将步骤聚合存储在与它们所属的方案相同的数据库中, 等等。如果你眯着眼睛,你会发现这只是&#34;使步骤实体成为场景的一部分&#34;解决方案,但是变相。

但请记住:的一部分原因是,当代码告诉我们业务模型本身是错误的时,我们可以推迟业务。成本效益分析在哪里?

这里有关于唯一性约束的事情:模型强制执行唯一性,而不是正确性。想象一下数据竞赛,两个不同的命令,每个命令都声称相同&#34;名称&#34;对于场景中的不同步骤 - 可能是由数据输入错误引起的。据推测,该模型无法判断哪个命令是&#34;对,&#34;所以它会进行一些任意猜测(最有可能的是,第一个命令获胜)。如果模型猜错了,它有效地阻止了提供正确数据的客户端!

如果模型是权限,唯一性约束可能有意义 - SeatMap聚合可以强制执行约束,即在任何给定时间只能将一个票证分配给一个席位,因为它是 转让权。