如何处理CQRS中的汇总/报告?

时间:2014-01-16 03:45:05

标签: domain-driven-design cqrs idempotent

我正在开展酒店预订项目。新功能要求客户可以评论酒店。每个评论都有一个评级(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计算平均值

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

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是幂等的,似乎更有效。我们是否存在一些缺陷或任何其他解决方案?

1 个答案:

答案 0 :(得分:3)

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

  1. 通过使用相关标识符在处理程序中添加条件检查,使消息处理程序具有幂等性。在这种情况下,您可以跟踪已为每个酒店处理的评论的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
    }
    
  2. 在基础架构级别重复删除消息。收到事件后,可以针对事件存储进行检查。如果消息已经出现在事件存储中,则可以安全地丢弃它。这将确保每个事件处理程序最多只接收一次事件。

  3. 通过将重复数据删除责任移到基础结构上,这意味着您不需要使每个事件处理程序具有幂等性。这将允许原始选项b处理程序按原样工作。

    进一步阅读