假设 Stack Overflow 域问题和事件定义如下:

UserRegistered(UserId, Name, Email)
UserNameChanged(UserId, Name)
QuestionAsked(UserId, QuestionId, Title, Question)

假设事件存储的状态如下(按出现顺序):

1) UserRegistered(1, "John", "john@gmail.com")
2) UserNameChanged(1, "SuperJohn")
3) UserNameChanged(1, "John007")
4) QuestionAsked(1, 1, "Help!", "Please!")

假设问题列表采用以下非规范化读取模型(针对 SO 的第一页):

QuestionItem(UserId, QuestionId, QuestionTitle, Question, UserName)

以及以下事件处理程序(构建非规范化读取模型):

public class QuestionEventsHandler
{
    public void Handle(QuestionAsked question)
    {
        var item = new QuestionItem(
            question.UserId, 
            question.QuestionId, 
            question.Title, 
            question.Question, 
            ??? /* how should i get name of the user? */);
        ...
    }
}

我的问题是 我怎样才能找到用户的名字 谁问了一个问题?或者更常见:如果我的非规范化读取模型需要特定事件中不存在的附加数据,我应该如何处理事件?

我检查了 CQRS 的现有样本,包括 简单SQRS 格雷格·杨和 佛金 马克·尼霍夫的样本。但在我看来,它们仅对事件中包含的数据进行操作。

有帮助吗?

解决方案

只需使用所有必要的信息来丰富活动即可。

我记得格雷格的方法是——在以这种方式创建和存储/发布活动的同时丰富活动。

其他提示

我个人认为从事件处理程序中查找用户名没有任何问题。但是,如果您无法从用户的读取模型中查询名称,那么我将向 QuestionEventsHandler 引入一个额外的事件处理程序,以处理 UserRegistered 事件。

这样,QuestionEventsHandler 就可以维护自己的用户名存储库(您不需要存储用户电子邮件)。然后,QuestionAsked 处理程序可以直接从其自己的存储库查询用户的姓名(正如 Rinat Abdullin 所说,存储很便宜!)。

此外,由于您的 QuestionItem 读取模型保存用户的姓名,因此您还需要处理 QuestionEventsHandler 中的 UserNameChanged 事件,以确保 QuestionItem 中的名称字段是最新的。

对我来说,这似乎比“丰富活动”更省力,并且有以下好处: 不是 建立对系统其他部分及其读取模型的依赖关系。

从 EventStore 中提取事件。

请记住 - 您的读取模型需要已经具有对 EventStore 的只读访问权限。阅读模型是一次性的。它们只是缓存的视图。您应该能够随时删除/过期您的读取模型 - 并从 EventStore 自动重建您的读取模型。因此 - 您的 ReadModelBuilders 必须已经能够查询过去的事件。

public class QuestionEventsHandler
{
    public void Handle(QuestionAsked question)
    {
        // Get Name of User
        var nameChangedEvent = eventRepository.GetLastEventByAggregateId<UserNameChanged>(question.UserId);

        var item = new QuestionItem(
            question.UserId, 
            question.QuestionId, 
            question.Title, 
            question.Question, 

            nameChangedEvent.Name
    }
}

还要认识到 - EventStore 存储库不一定是真正的 EventStore,尽管它肯定可以是。分布式系统的优点是,如果需要,您可以轻松复制 EventStore,使其更靠近 ReadModel。

我遇到了完全相同的场景......我需要的数据多于单个事件中可用的数据。对于需要用初始状态填充新的 ReadModel 的 Create 类型事件尤其如此。

从读取模型:您可以从其他读取模型中提取。但我真的不推荐这样做,因为你会引入一个大的依赖泥球,其中视图依赖于视图依赖于视图。

事件中的附加数据:您确实不想使用视图所需的所有额外数据来使您的事件膨胀。当您的域发生变化并且您需要迁移事件时,这确实会对您造成伤害。领域事件有特定的目的——它们代表状态变化。不查看数据。

希望这可以帮助 -

瑞安

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top