我正在设计一个公司业务的游戏。它是一个回合制游戏。
不变量:
a)游戏至少有两个玩家,一个开始日期和其他属性。
b)每个玩家在游戏中进行转弯。
c)加入游戏时的成员成为玩家。
d)成员可以成为0-n游戏中的玩家。
我的主要问题是如何聚合概念。
起初我认为该成员是它自己的聚合。因为其他人只保留对它的引用。
之后,Game可能是另一个聚合的根源,包括玩家和转弯。惠特,我可以保证:
我想听听你的方法,因为我真的被卡住了。
在基于回合的游戏中,如淘气和十字架,跳棋,国际象棋,步步高,我通常会期望“游戏”聚合包括移动和令牌/棋盘/画面的当前位置。
确定游戏历史是否内部一致的理由要求能够“一起”看到所有历史记录,以便您能够发现矛盾。
一种看待这种情况的方法是注意到两个不同的成员资格通常彼此之间没有任何依赖关系,因此将它们作为聚合的不同实例是有意义的。如果一个游戏由两个成员共享,那么它不能真正属于任何一个成员,所以它必须是单独跟踪的第三个聚合。
你做对了,有2个Aggregates,但现在你需要了解原因。
Member Aggregate拥有成员的数据和行为。成员意味着什么,如何成为成员,何时可以更改其名称,何时可以解锁等等。
Game Aggregate拥有游戏规则和两名玩家。它具有保护其不变量并在UI中显示所需的所有数据。它保护的不变量是:
为了保护第二个不变量,游戏有一个turn
状态,它是指向下一个玩家的指针。但是这里有一个有趣的问题:什么是播放器,如何在代码中表示?
玩家是指向其中一个成员的指针。它具有Game聚合或UI所需的所有属性。游戏聚合需要其ID以保护第一个不变量(以检测重复的ID),以便加载成员所玩的所有游戏,并且为了在UI中显示玩家的名称,以便其他玩家将知道他们在玩谁。
播放器似乎是一个实体,因为它有一个ID和另一个属性,它的名称,但它不是因为它不拥有该名称或其行为:成员何时以及如何更改它的名称是受控制的通过Member Aggregate,播放器的名称在没有播放器的情况下更新,可能在后台。
从游戏的角度来看,玩家只是一个普通的对象,不可变,有一个ID和一个名字作为string
。这是Value对象的完美候选者。
因此,虽然成员是聚合根(它是一种实体),但是具有与成员相同的ID和名称的Player不是实体而是Value对象。