我正在编写我的第一个CQRS应用程序,假设我的系统派发以下命令:
- CreateContingent (Id, Name)
- CreateTeam (Id, Name)
- AssignTeamToContingent (TeamId, ContingentId)
- CreateParticipant (Id, Name)
- AssignParticipantToTeam (ParticipantId, TeamId)
目前,这些命令会产生相同的事件,只是用过去式表述(例如ContingentCreated,TeamCreated等),但它们包含相同的属性。(我不确定这是否正确,这是我的疑问之一)
我的问题在于读模型。
我有一个Contingents读模型,订阅了ContingentCreated事件,并具有以下方法:
List<ContingentQueries.Contingent> GetContingents();
ContingentQueries.Contingent GetContingent(System.Guid id);
ContingentQueries.Contingent GetContingent(string province);
通过这些方法返回的 Contingent 类的样式如下:
public class Contingent
{
public Guid Id { get; internal set; }
public string Province { get; internal set; }
}
这对于列表工作得很好,但是当我开始考虑用于查看单个队伍及其所有成员的视图的读模型时,情况开始变得混乱起来。鉴于这些方法,似乎微不足道:
ContingentQueries.Contingent GetContingent(System.Guid id);
ContingentQueries.Contingent GetContingent(string province);
我为这些查询创建了一个团队集合的Contingent类:
public class Contingent
{
public Guid Id { get; internal set; }
public string Province { get; internal set; }
public IList<Team> Teams { get; internal set; }
}
public class Team
{
public Guid Id { get; internal set; }
public string Name { get; internal set; }
}
理论上,我只需要订阅ContingentCreated和TeamAssignedToContingent事件,但是TeamAssignedToContingent事件只有团队和协调员id,所以我无法设置此读取模型的Team.Name属性。我是否还应该订阅TeamCreated?添加另一个团队副本供此处使用吗?
或者当引发事件时,它们是否应该具有更多信息? AddTeamToContingent命令处理程序是否应查询团队信息以添加到事件中?这可能尚不存在,因为读取模型可能尚未更新团队。
如果我想在这个Contingent视图中显示团队名称和已分配给团队并命名为队长的参与者怎么办?我也要在阅读模型中存储参与者吗?这似乎是大量重复的工作。
对于一些额外的背景信息。参与者不一定是任何协调员或团队的一部分,他们可能只是来宾;或协调员的代表和/或备用。然而,角色可能会转移。一个不是团队成员的代表也可以是替补,并由于受伤被分配到一个团队,但他们仍然是代表。这就是为什么使用通用的“参与者”的原因。
我明白我希望读取模型不要比必要的重,但我很难处理这样一个事实,即我可能需要来自我没有订阅的事件(TeamCreated)的数据,理论上不应该,但由于未来的事件(TeamAssignedToContingent),我确实需要那些信息,我该怎么办?
更新:经过一夜的思考,似乎我想到的所有选项都不好。必须还有其他方法。
选项1:向引发的事件添加更多数据
命令处理程序是否开始使用尚未编写的读取模型,以便获取数据?
如果未来的订户需要更多信息怎么办?我不能添加它!
选项2:使事件处理程序订阅更多事件?
这导致读取模型存储它可能不关心的数据吗?
例如:在ContingentView读取模型中存储参与者,以便当一个人被分配到一个团队并标记为船长时,我们知道他们的名字。
选项3:使事件处理程序查询其他读取模型?
这感觉是最好的方法,但感觉不对。协调员视图是否应查询参与者视图以获取名称(如果需要)?它消除了1和2的缺点。
选项4:...?