通过父子与子孙...关系进行事件采购时,CQRS阅读模型设计 [英] CQRS Read Model Design when Event Sourcing with a Parent-Child-GrandChild... relationship

查看:113
本文介绍了通过父子与子孙...关系进行事件采购时,CQRS阅读模型设计的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写第一个CQRS应用程序,假设我的系统调度了以下命令:




  • CreateContingent(Id ,名称)

  • CreateTeam(标识,名称)

  • AssignTeamToContingent(团队标识,ContingentId)

  • CreateParticipant( ID,名称)

  • AssignParticipantToTeam(参与者ID,团队ID)



当前,这些结果相同事件,以过去时(ContingentCreated,TeamCreated等)来表示,但它们包含相同的属性。 (我不太确定那是正确的,这是我的问题之一)



我的问题在于读取的模型。



我有一个Contingents读取模型,该模型订阅ContingentCreated,并具有以下方法:

  List< ContingentQueries .Contingent> GetContingents(); 
ContingentQueries.Contingent GetContingent(System.Guid id);
ContingentQueries.Contingent GetContingent(字符串省);

这些返回的Contingent类如下:

 公共类或有
{
public Guid Id {get;内部设置}
public string Province {get;内部设置}
}

这对于列表很有效,但是当我开始考虑阅读时一个单一的队伍及其所有团队的视图模型,事情开始变得混乱。鉴于这些方法似乎很简单:

  ContingentQueries.Contingent GetContingent(System.Guid id); 
ContingentQueries.Contingent GetContingent(字符串省);

然后我用一组团队为这些查询创建Contingent类:

 公共类或有
{
public Guid Id {get;内部设置}
public string Province {get;内部设置}
public IList< Team>团队{内部设置}
}

公开课团队
{
公开Guid ID {get;内部设置}
公共字符串Name {get;内部设置}
}

理论上,我只需要订阅ContingentCreated和TeamAssignedToContingent,但是TeamAssignedToContingent事件只有团队ID和临时ID ...因此我无法设置此读取模型的Team.Name属性。



我也订阅吗团队创建?



或者在引发事件时,是否应该在其中包含更多信息? AddTeamToContingent命令处理程序是否应该查询要添加到事件的团队信息?可能还不存在,因为读取模型可能尚未与团队一起更新。



如果我想显示团队名称和已分配给团队并在此Contingent视图中命名为队长的参与者,该怎么办?我也将参与者存储在阅读模型中吗?似乎有很多重复。



需要一些额外的说明。参加者不一定是任何特遣队或团队的一部分,他们可以只是客人。或特遣队的代表和/或备件。但是,角色可以转移。非团队成员的代表也可以是空缺,并且由于受伤而分配给团队,但是他们仍然是代表。这就是为什么使用通用的参与者的原因。



我知道我希望读取的模型不会比需要的重,但是我很难理解可能需要我未订阅的事件(TeamCreated)的数据,理论上应该不是,但由于将来发生的事件(TeamAssignedToContingent),我确实需要该信息,我该怎么办?



更新:一夜之间想到了它,看来我想到的所有选择都是不好的。必须存在另一个。



选项1 :向引发的事件添加更多数据




  • 命令处理程序是否开始使用可能尚未编写的读取模型,以便它们可以获取该数据?

  • 如果将来该事件的订户需要更多的内容怎么办?信息?我无法添加它!



选项2 :事件处理程序是否订阅了更多事件?


  • 这会导致读取的模型存储可能可能不关心的数据?

  • 示例:将参与者存储在ContingentView中以读取模型,这样当一个人被分配到团队中并标记为队长时,我们就知道他们的名字。 $ b

    选项3 :让事件处理程序查询其他读取模型吗?




    • 这感觉是最好的的方法,但感觉不对。 Contingent视图是否应查询Participant视图以在需要时和需要时获取名称?它确实消除了1和2中的断线。



    选项4 :...?

    解决方案

    您最终可能遇到的是1和2的组合。增强或丰富事件绝对没有问题。了解更多信息。




    • 如果将来的活动订户需要更多信息怎么办?我无法添加!



    您可以将其添加到即将进行的活动中。如果您需要历史数据,则必须在其他地方找到它。您无法利用将来可能需要的任何机会来利用世界上的所有数据来构建事件。甚至可以返回并将更多数据添加到历史事件中。有些人会因为重写历史而对此感到不满意,但事实并非如此,您只是在增强历史。只要您不更改现有数据,就可以了。



    我怀疑随着时间的推移,您的事件处理程序还需要订阅更多事件。例如,是否需要重命名团队?如果是这样,则您需要处理该事件。不要害怕在不同的阅读视图中拥有相同数据的多个副本。来自关系数据库背景的这种重复数据是最难使用的东西之一。



    最后要务实。如果您在使用CQRS时遇到麻烦,请更改其使用方式。


    I'm in the process of writing my first CQRS application, let's say my system dispatches the following commands:

    • CreateContingent (Id, Name)
    • CreateTeam (Id, Name)
    • AssignTeamToContingent (TeamId, ContingentId)
    • CreateParticipant (Id, Name)
    • AssignParticipantToTeam (ParticipantId, TeamId)

    Currently, these result in identical events, just worded in the past tense (ContingentCreated, TeamCreated, etc) but they contain the same properties. (I'm not so sure that is correct and is one of my questions)

    My issue lies with the read models.

    I have a Contingents read model, that subscribes to ContingentCreated, and has the following methods:

        List<ContingentQueries.Contingent> GetContingents();
        ContingentQueries.Contingent GetContingent(System.Guid id);
        ContingentQueries.Contingent GetContingent(string province);
    

    The Contingent class returned by these looks like this:

        public class Contingent
        {
            public Guid Id { get; internal set; }
            public string Province { get; internal set; }
        }
    

    This works fine for a listing, but when I start thinking about the read model for a view of a single contingent, and all it's teams, things start to get messy. Given the methods it seems trivial:

        ContingentQueries.Contingent GetContingent(System.Guid id);
        ContingentQueries.Contingent GetContingent(string province);
    

    And I create the Contingent class for these queries with a collection of teams:

        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; }
        }
    

    In theory, I should only need to subscribe to ContingentCreated, and TeamAssignedToContingent, but the TeamAssignedToContingent event only has the team and contingent ids... so I can't set the Team.Name property of this read model.

    Do I also subscribe to TeamCreated? Add keep another copy of teams for use here?

    Or when events are raised, should they have more information in them? Should the AddTeamToContingent command handler be querying for team information to add to the event? Which may not yet exist, because the read model might not have been updated with the team yet.

    What if I wanted to show the team name and the participant that has been assigned to the team, and named the captain in this Contingent view? Do I also store the participants in the read model? This seems like a ton of duplication.

    For some extra context. Participants are not necessaily part of any contingent, or a team, they could just be guests; or delegates and/or spares of a contingent. However, roles can shift. A delegate who is not a team member, could also be a spare, and due to injury be assigned to a team, however they are still a delegate. Which is why the generic "Participant" is used.

    I understand I want read models to not be heavier than necessary, but I'm having difficulty with the fact that I may need data from events that I'm not subscribed to (TeamCreated), and in theory shouldn't be, but because of a future event (TeamAssignedToContingent) I do need that info, what do I do?

    UPDATE: Thought about it overnight, and it seems like all the options I have thought of are bad. There must be another.

    Option 1: Add more data to raised events

    • Do Command Handlers start using read models that may not yet be written so they can get that data?
    • What if a future subscriber to the event needs even more info? I can't add it!

    Option 2: Have Event Handlers subscribe to more events?

    • This results in the read model storing data it might not care about?
    • Example: Storing participants in the ContingentView read model so when a person is assigned to a team, and marked as captain, we have know their name.

    Option 3: Have Event Handlers query other Read Models?

    • This feels like the best approach, but feels wrong. Should the Contingent view query the Participant view to get the name if and when it needs it? It does eliminate the drabacks in 1 and 2.

    Options 4: ...?

    解决方案

    What you're probably going to end up with is a combination of 1 and 2. There is absolutely no problem with enhancing or enriching events to have more information in them. It is actually quite helpful.

    • What if a future subscriber to the event needs even more info? I can't add it!

    You can add it to the events going forward. If you need historical data you'll have to find it somewhere else. You can't build events with all the data in the world on them just on the off chance that you'll need it in the future. It is even allowable to go back and add more data to your historical events. Some people would frown on this as rewriting history but you're not, you're simply enhancing history. As long as you don't change the existing data you should be fine.

    I suspect that your event handlers are also going to need to subscribe to more events as time continues. For instance are teams going to need to be renamed? If so then you'll need to handle that event. Don't be afraid to have multiple copies of the same data in different read views. Coming from a relational database background this duplication of data is one of the hardest things to get use to.

    In the end be pragmatic. If you're encountering pain in applying CQRS then change how you're applying it.

    这篇关于通过父子与子孙...关系进行事件采购时,CQRS阅读模型设计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆