我想我理解了ES + CQRS背景下读取模型的思想(如果不理解请纠正我)。然而,我仍然对在"严肃"报道中使用它有一些疑问。假设我使用关系数据库和一些ORM来简化我的读模型。一个基本的"summary stats read model"可能是这样的:
class SummaryStats1
{
public Guid TypeId { get; set; }
public string TypeName { get; set; }
public Guid SubTypeId { get; set; }
public string SubTypeName { get; set; }
public int Count { get; set; }
}
给定一个事件:
TypeId = 3acf7d6f-4565-4672-985d-a748b7706a3e
TypeName = Bla1
SubTypeId = 41532aa1-f5d1-4ec4-896b-807ad66f75fc
SubTypeName = Bla2
归一化器将:
(1)检查是否存在上述组合的实例(由TypeId, TypeName, SubTypeId, SubTypeName定义)(2)如果没有实例,它将创建一个实例并设置Count为1。如果有,则将Count增加1。
这是可接受的报告方法吗?我想可以对这种非规范化的数据结构(用于过滤和其他sql"投影")运行非常有效的选择:
SELECT TypeName, Sum(Count) FROM SummaryStats1 GROUP BY TypeName
CQRS/ES专家会同意这一点吗?这是做事的"方式"吗(即创建这些专用的报告读取模型)?任何参考源代码/真实的例子将非常感激。
这是可接受的报告方法吗?
还是报告方法当然取决于您的需求,但总体思路是正确的。
总结:
您根据来自您的领域的事件生成读取模型(有时使用的官方术语是Eager read Derivation)。
读取模型可以是任何你想要的(sql, redis, mongo等)。无论什么都可以使您的查询具有高性能。例如,在你的例子中,没有理由你不能有两个读模型来更有效地执行你的查询(尽管你所描述的对于大多数情况可能已经足够了):
- 描述的sql视图
- 一个由
typeName
分组的预聚合视图,这样你就不必在每次查询时都进行分组(而是在规范化器中计算分组)。
简而言之,在如何构建读模型上没有正确或错误的方法。其美妙之处在于,您可以完全自由地以任何您想要的方式(基于您设想的查询模式和性能瓶颈)对您的读模型进行建模,而不必考虑这些模型如何影响写(仅仅因为它们不会,因为cqrs将读和写分开)
将事件源与CQRS结合使用提供了更好的可能性,即通过重播事件源中的过去事件来创建新的readmodel并填充数据。
只是一些额外的例子,可能被认为是你的数据的'读取模型':
- 使用Redis的INCR视图(这是您似乎描述的替代方案)
- 一个Elasticsearch/Solr搜索索引
- 用于按键快速查找的KV-store/索引。
想法是,这些"读模型"/视图总是保持最新(最终一致)推送更新事件给他们(通常是通过pubsub)
更多好的阅读,请看这个问题的答案和链接:使用CQRS的读端实现方法