Domain driven design 如何在CQR中处理摘要/报告?

Domain driven design 如何在CQR中处理摘要/报告?,domain-driven-design,cqrs,idempotent,Domain Driven Design,Cqrs,Idempotent,我在做一个酒店预订项目。一项新功能要求客户可以对酒店发表评论。每个注释都有一个评级(0-->5)。在“搜索酒店”页面上,每个酒店都显示其平均评级 添加评论非常简单,可以发布一个CommentAddedEvent: class CommentAddedEvent { private String hotelId; private double rating; //other attributes omitted } 在查询端,将在此事件上保留注释行: create t

我在做一个酒店预订项目。一项新功能要求客户可以对酒店发表评论。每个注释都有一个评级(0-->5)。在“搜索酒店”页面上,每个酒店都显示其平均评级

添加评论非常简单,可以发布一个CommentAddedEvent:

class CommentAddedEvent {
    private String hotelId;
    private double rating;
    //other attributes omitted
} 
在查询端,将在此事件上保留注释行:

 create table t_comment {
    hotel_id number,
    rating   number(2,1)
}
但我们在如何显示酒店的平均评级方面进展缓慢。以下是我们的潜在解决方案:

a)数据库集成

在搜索酒店查询端,使用sql函数获取平均值:

select h.id, h.name,....,select avg(c.rating) from t_commentc , t_hotel 
where.... group by c.hotel_id.....
这看起来很容易,但我们认为这会在测试中引入更多的工作,并可能会导致一些性能问题

b)EventHandler计算平均值

添加订阅CommentAddEvent的事件处理程序并计算平均值:

public void on(CommentAddedEvent event) {
    Hotel hotel = ....
    double total = hotel.rating * hotel.comments;
    double average = (total + event.rating)/(hotel.comments + 1)
    //update hotel
}
这个测试很容易,但这个解决方案似乎不是幂等的。当某个事件失败时,事件处理程序可能会处理重复事件

c)计划任务

添加计划任务,总结每个酒店的评论。但这是低效的,因为自上一个任务以来,一些酒店没有评论

d)与事件处理程序混合的计划任务

使用事件处理程序标记自上次任务以来发生的事件:

public void on(CommentAddedEvent event) {
    int count = uncalculatedCommentsOnHotel(...);
    count++;
    //update count
}
并根据计数大于零的酒店安排汇总任务


解D是幂等的,似乎更有效。是否存在我们制造的缺陷或任何其他解决方案?

我会选择实现一个计算平均值的事件处理程序(选项b)。您有一些克服幂等性问题的策略

  • 通过使用相关标识符在处理程序中添加条件检查,使消息处理程序幂等。在这种情况下,您可以跟踪每个酒店已处理的评论Id,以防止并仅处理新评论

    public void on(CommentAddedEvent event) 
    {       
        Hotel hotel = ....
    
        if (hotel.CommentIds.Contains(event.CommentId))
            return;  // comment has already been processed
    
        double total = hotel.rating * hotel.comments;
        double average = (total + event.rating)/(hotel.comments + 1)
    
        hotel.CommentIds.Add(event.CommentId);
        //update hotel
    }
    
  • 在基础架构级别对消息进行重复数据消除。当接收到事件时,可以根据事件存储进行检查。如果消息已出现在事件存储中,则可以安全地将其丢弃。这将确保每个事件处理程序最多接收一次事件

  • 通过将重复数据消除责任转移到基础架构,这意味着您不需要使每个事件处理程序都是幂等的。这将允许原始选项b处理程序按原样工作

    进一步阅读

    • 吉米·博加德
    • 乔纳森·奥利弗

    对于每家酒店,保持阅读模式如何?它得到的评论数和分数(星级等)的总和。也就是说,在每个CommentAddEvent上,处理程序只需增加comments计数器,并向分数总和中添加一些内容。然后,在查询读取模型时。。。您只需读取特定酒店的值。当然,在read model表/视图上,您可以添加一个“computed”列,average=your formula:)您认为如何?@andrei但是在处理程序中保留一个计数器将使处理程序成为有状态bean?我想到了一些类似于从read model检索当前计数器并增加它们的事情。然后,我想阅读模型可以在必要时从事件中简单地重建。有关于它的完整示例代码吗?很好的建议和文章。我能想到的唯一风险是高争议,尽管在我的酒店评论案例中不可能。但是经常总结一下,比如页面访问次数之类的东西怎么样?