基本综合问题
是否允许客户端代码引用不是根的聚合中的实体? 我有一个Story
(根), Team
(实体)和TeamMember
(实体)。 我试图决定AddTeamMember
方法是否属于Team
或Story
。
我想我的例子有点误导。 我真正的问题是客户端代码可以引用聚合中的非根实体吗?
我的意见 – 它不应该。 引用属于某个聚合的实体意味着您可以在没有完整聚合上下文的情况下调用该实体上的方法,如果允许,您永远无法确定整个聚合是否有效且一致。
简单的例子:
public class MyAggregateRoot { protected MyEntity entity; public void BuildUpAggregate() { ValidateSomeRule(); LoadEntityFromDatabase(); } public MyEntity MyEntity { get { VerifySomeOtherRule(); return entity; } } }
如您所见,在通过聚合根构建和检索MyEntity时,我们检查了两个validation规则 – 如果您允许客户端直接引用MyEntity,则聚合可能会在客户端检索实体并对其执行操作之间及时更改,因此检查将不再有效,但您不会意识到这一事实。 换句话说,您的聚合将不一致且可能无效。
通常,在DDD中有一个强有力的规则,规定聚合中对实体的所有访问都应该通过遍历聚合根来执行 – 这个规则正是为了聚合的一致性。
也就是说,您的客户可以引用的是对实体的投影 – 一种只读副本,其中仅包含显示和决定当前上下文中是否有某个操作所需的数据。 您可以更进一步,将一组实体中的数据聚合到一个投影中,稍微调整一下,这样就可以根据UI的要求进行调整。 我发现这种技术对于不允许我的UI决定如何建模业务域非常有用。
如果不知道你的架构应该如何工作,听起来它应该是Team
而不是Story
。 这假设您从Team
Team Member
到Team Member
有1对多,并且该Team Member
是Team
对象的子项,而不是Story
。
我不确定“root”或“聚合”规则,但我认为故事不应该知道如何管理团队成员资格。
团队应该与团队成员“有”关系。 故事和团队成员彼此之间没有直接关系。
你的class级应该是这样的:
class team { list teamMembers; void AddTeamMember(TeamMember member){} }
Team必须添加团队成员方法的另一个原因是,如果该方法是故事的一部分,那么您必须明确提及要添加成员的团队。
addteamMember(Team T, TeamMember member);
Root应该提供访问非root元素的正确方法(第一级嵌套)。 在你的情况下,团队应该有addTeamMember方法,故事应该有一个方法(AddMemberToTeam(team t,teammember m)),它调用指定团队的addTeamMember方法。