Background
我有一个使用CQRS ES的系统,在这个系统中有一些聚合,例如博客文章或事件存储中存在的问题,并将事件发送到查询端以通过投影保持读取模型 .
In the case of a an issue or post being created it is a fairly straight forward
-
客户端创建一个命令来创建新问题
-
命令处理程序创建新的问题聚合并将更改保存在事件存储中
-
当聚合应用更改时,它会触发IssueCreatedEvent或类似的
-
读取端的投影将监听此事并创建问题模型和它想要的任何其他非规范化数据(例如用于查询所有问题列表的缩减IssueListItem)
如果对问题进行了更改,则会在写入端引发相应的事件,例如IssueStatusChanged,并在读取端进行相应的处理 . 在读取端加载两个非规范化模型,更新事件中的状态并保存 . 简单 .
How do you handle relations such as comments?
我正在实施一个评论系统,用户可以在问题或博客文章上发表评论 . 我的第一个想法是将这些注释添加到问题中或在写入端发布聚合以保持一致性 . 当我想到这一点时,虽然我意识到这可能会引入许多不必要的并发问题,例如当有人更新问题而其他人已经发布并发布新评论时 .
这让我认为我应该将评论自己建模为自己的聚合根 . 这样,发布到博客帖子或问题的评论不会导致与问题本身发生冲突 .
因此,假设我以这种方式将写入方面的注释建模为聚合,我有两个问题;
1) Does the issue or post aggregate on the write side still need to store this relationship? 注释聚合本身已经存储了哪个项目也带有id引用 .
如果是这样,我正在考虑让问题聚合订阅注释创建事件并添加自己的引用 .
public class Issue : AggregateRoot, IEventHandler<CommentCreatedEvent>
{
private ICollection<Guid> _Comments;
public void Handle(CommentCreatedEvent @event)
{
_Comments.Add(@event.AggregateId)
}
}
这是否足够或不需要,因为评论已经存储了对其父项的引用?写入端并不真正需要这些数据,并且当它是加载了所有注释的父项时,在读取方面更为重要 .
2) On the read side what is the best way to store this data?
具体来说,为了使这些数据易于更新,我需要在另一个表中添加注释,并将它们连接到相应的帖子或问题 . 完成评论后,我将实施以下系统,用户可以按照项目接收更新 . 然而,沿着这条路走下去将很快让我回到读取端的高度规范化架构,这违背了优化的非规范化读取模型的目的 .
我在那里考虑在问题表中添加一个列,例如将所有注释存储为序列化的json clob或其他东西 . 这样,当更改进入注释时,我仍然可以提取一条记录来加载问题,对注释进行适当的更改(例如更新现有注释,添加新注释或删除注释)并重新保存记录 . 从阅读的角度来看,仍然可以一次性检索整个问题 .
我用这种方法看到的问题是,如果用户更改其 Profiles 图片或 Profiles 名称,我将不得不加载每个问题和/或发布,加载评论并在评论信息中进行适当的更改 .
我也想知道文档数据库(我在阅读方面一直在考虑的其他方面)如何解决这个更新嵌套数据的问题?
3 回答
问题1:无需在问题中 Build 关系 . 这里没有特别的一致性保护 .
问题2:我最近在读NoSQL . 像Casandra这样的Column-Family数据库似乎适合评论 .
您可以使用Casandra api或Casandra查询语言来检索注释的子集或整个注释列 .
UPDATE
如果我没有弄错的话,你可以单独在Casandra中获取和设置任何评论 . 在这种情况下,您可以更新任何一条评论 . 或者您可以获取评论列以检索所有评论 .
我在派对上有点晚了,这是我对2号的看法 .
存储读取模型的最佳方式是 very easy to query . 文档数据库可以是一个很好的技术解决方案,但它也适用于rdbms,只要您定义了相关的读取模型模式 .
您可以将所有评论与帖子一起存储,但情况并非总是如此,因为高流量网站通过ajax将帖子与帖子分开加载 . 所以它真的取决于阅读模型用例 .
问题1:您不能处理Aggregate root中的事件 . 打破DDD原则是一个坏主意 . 如果Comments位于不同的聚合中,则问题聚合中的任何后果必须最终由您域中的某种流程管理器,域服务或Saga处理 .
如果可能在您的域中,您必须声明问题不知道评论(我猜这是一种自然的思考方式),因此您不应该保留任何类型的参考 .
另一方面的评论可以引用与其相关的问题 .
问题2:为什么不在“评论”表中保留发布/发布所需的所有字段(处理问题/发布更新)?在查询读取模型时,这使您无需在这两个表之间加入 .