如何有效的建模聚合(三之大结局)

时间:2022-08-31 12:59:08

你们最尊敬的翻译官:

当然在此声明,由于翻译的这篇文章,已经被作者收录进IDDD的第十章:聚合篇,所以有书的同学还是看书比较好,这部分翻译一是纠正我在中文版中的一些不理解,二是通过翻译加深对聚合与建模的理解,三呢也是对DDD思想的宣传吧,希望更多的开发可以意识到自己的狭隘思想,作为一个引路人,希望这篇聚合,可以让你信服。

3.Gaining Insight Through Discovery

第2部分讨论了DDD中聚合如何引用其他聚合,以及如何利用最终的一致性来使分离的聚合实例保持一致。在第3部分中,我们将看到遵守聚合规则如何对Scrum模型的设计产生影响。我们将看到项目团队如何重新考虑他们的设计,并应用新技术。这一努力导致了对该模型的新的认识。他们的各种想法被尝试,然后被取代。

3.1重新考虑我们的设计,再试一次

在重构迭代之后,我们分解了大型集群的聚合Product,现在BacklogItem单独作为它自己的聚合。它反映了图7所示的模型。团队在BacklogItem聚合中组合了一个Task实例集合。每一个BacklogItem都有一个全局的唯一标识,即它的BacklogItemId。所有的到其他聚合的关联都是以标识符的方式来实现的。这意味着它的父对象Product,它被安排在内部的Realise,以及它所提交到的Sprint,都是通过标识符进行引用的。这看起来相当小。现在,他的团队对设计小的聚合体很有兴趣,可能会在这个方向上做得过火。
如何有效的建模聚合(三之大结局)

尽管在之前的迭代中出现了良好的感觉,但仍然存在一些问题。例如,story属性允许出现大量的文本。开发敏捷故事的团队不会写冗长的文章。即便如此,还是有一个可选的编辑器组件,它支持编写丰富的用例定义。这些可能是数千字节。这部分可能的开销被考虑是值得的。

考虑到这种潜在的开销,以及在第1部分中图1和图3的大型集群的Product时所犯的错误,团队现在正在执行一项任务,即在限界上下文中减小每个聚合的大小。现在出现了至关重要的问题啦。在这个关系必须维护的BacklogItem和Task之间是否有一个真正的不变量?或者,这又是一个可以进一步分裂,并安全的形成两个独立的聚合的例子?保持当前设计的总成本是多少?

要做出正确的决定,关键在于统一语言。下面是一个变量开始的地方:

当在待办事项的任务(backlog item task)中取得进展时,团队成员将会估算剩余的任务时间。
当一个团队成员估算在一个特定的任务上剩余的时间是0小时,那么待办事项就会检查所有剩余的工作时间。如果任何任务的剩余时间都是0小时的话,那么这个待定事项的状态会被自定的改编为已完成。
当一个团队的成员估算一个特定任务的剩余时间是以小时或者更多的小时(反正不是0小时),然而此时的待定事项的状态已经被设置为已完成了,那么这个状态是会自动的回退的。

这看起来像是一个真的不变量。待办事项列表的正确状态会自动调整,并且完全依赖于所有任务剩余的小时数。如果任务剩余时间的总和与待办事项列表的状态保持一致,这似乎是图7所示的那样,确实规定了正确的聚合一致性边界。

但是,团队仍然应该确定当前的集群在性能和可伸缩性方面的成本。

有些人会认为这是一个使用最终一致性的典型机会,但我们不会马上得出这个结论。让我们分析一个事务一致性方法,然后研究如何使用最终的一致性来完成。然后,我们可以各自得出自己的结论,即哪种方法更好。

3.2 Estimating Aggregate Cost(估算聚合成本)

正如图7所示, 每一个Task都持有一系列的EstimationLogEntry的集合。这些日志为特定的场合建模,当一个团队成员键入一个新的估算时间的时候。在实际操作中,每个BacklogItem将包含多少个Task元素,以及一个给定的Task将会包含多少个EstimationLogEntry(评估日志)元素?很难准确地说出来。它很大程度上是衡量一个Task有多复杂,以及一个sprint持续多长时间的。但是,粗略的计算(BOTE)可能对你有帮助。

任务时间通常是由在给定任务上工作的团队成员每天都会重估的。

在一个团队成员在一个给定的任务上工作之后,每天的任务时间通常被重新估计。假设大多数的sprint(冲刺)都是两到三个星期。当然可能会有时间跨度更大的sprint,但是2~3周的时间一般是足够的。所以,让我们在10天到15天之间选择一个日子。如果不太精确的话,12天可以很好地工作,因为实际上两周时间的冲刺会明显比三周的要多。

接下来考虑分配给每个任务的小时数。记住,由于任务必须分解成可管理的单元,所以我们通常在4到16小时之间使用某个小时。通常,如果一个任务超过12个小时的估算,Scrum专家会建议将其进一步分解。但是,使用12个小时作为第一个测试,可以更容易地模拟工作。我们可以说,在sprint的12天的每一天里,每个任务都要进行一小时的工作。这样做有利于更复杂的任务。因此,我们每一个任务我们都将计算12次估算,假设每个任务从12个小时开始,假设每个任务都被分配了12个小时。(注意:作者这里与我们平常开发的计算不太一样,它是假设一个任务需要12小时做完,而一个冲刺是12天,那么一天干1/12,每天等于干一小时,每天都估算一下剩余时间。)

问题仍然存在:每个待办事项中需要多少个任务呢?这也是一个很难回答的问题。如果我们假设给定的功能模块是分层架构的某一层或六角形架构的端口适配器,每一个都需要两到三种任务,那该怎么办呢?举个栗子,我们可以将三个用于用户界面层,两个用于应用程序层,三个用于领域层,三个用于基础设施层。那么最终就给我们带来了11个任务。当然这个栗子可能正好是对的,但也许还小了点,但是我们已经在许多任务估计的方面犯了错误。让我们将每个待办事项中的任务增加到12个任务,这样就更加*了。这样,我们就可以使用12个任务,每个任务都有12个评估日志,或者可以说是待定事项里有144个评估日志。虽然这可能超出了常规,但它给我们提供了一个很粗的估算方法。

这里还需要考虑另一个变量。如果Scrum专家建议定义更小的任务,那么它将会有所改变。将任务数量加倍(24),并将估算日志条目的数量减半(6),这样的话,每一个待定事项对象仍然会产生144个估算日志对象。但是,在所有的评估请求中,它会导致更多的任务被加载(现在是24个任务啦,是以前12个任务的两倍呢!!),在每个请求中消耗更多的内存。团队将尝试不同的组合,以查看是否对他们的性能测试有重大影响,但是开始时他们将使用12个任务和12个小时。

3.3 常见的使用场景

现在,考虑常见的使用场景是很重要的。像需要一次性加载所有的144个对象进内存的用户请求的情况出现的多嘛?或者说这种情形会发生吗?似乎没有,但他们需要检查一下。如果不是的话,那么对象的高端数是多少??此外,通常会有多客户端使用并导致待定项(backlog item)的并发性争用吗?让我们来看看。

下面的场景是基于Hibernate做持久化的使用。另外,每个实体类型都有自己的乐观并发版本属性(我们第一章节有讲,采用乐观锁的方式,即使用版本号,每次命令方式的操作都会对版本做递增)。这是可行的,因为变更状态的不变量是由待办项(backlog item)这个根实体进行管理的。当状态自动的改变的时候(比如说已完成状态或者回退已提交状态),根的版本号也会相应的跳到下一个版本上。因此,对任务的更改可以独立地发生,而不会在每次修改时候影响到根,除非它导致根的状态更改(如果使用的是基于文档的存储,那么接下来的分析可能需要重新访问,因为每次所包含的部分被修改的时候,根就会得到有效的修改。)。

当一个待办项(Backlog Item)第一次被创建时,它所包含的任务数是0个。