标签:
之前,在用ENode开发forum案例时,遇到了关于如何实现论坛帖子的回复的统计信息如何更新的问题。后来找到了自己认为比较合理的解决方案,分享给大家。也希望能和大家交流,擦出更多的火花。
论坛领域的核心概念是:帖子、回复。大家都知道,一个帖子可以有零个或多个回复。对同一个帖子,不同的人可以并行发表回复。回复发表后,查看帖子详情时,可以根据回复的发表时间排序显示;此外,我们还关心某个帖子的最新发表的回复、最新回复的作者、最新回复时间,以及总回复数。
我们设计的系统,应该在实现上述的领域问题的前提下,尽量做到发表回复时要快,且能保证帖子能对它的所有回复的统计信息能正确的统计出来。
把帖子设计为聚合根,回复设计为帖子的子实体。然后发表回复就是在帖子聚合根里添加一个回复实体。
优点:
缺点:
这种方案,大部分情况下都没问题。因为大部分论坛,对一个帖子的回复的并发都不会太高。所以,我觉得设计总体来说是可行的。
把帖子和回复都设计为聚合根,回复不再是帖子内的子实体。发表回复就是新增了一个回复聚合根。
优点:
缺点:
这种方案,模型层面做了一些变化,DB层面,引入了异步更新统计信息的思想,但要求技术上需要处理EDA架构所带来的典型问题。如果论坛的并发问题确实影响了用户的体验,则可以尝试考虑次方案;
模型层面,设计为两个聚合根还是一个聚合根无所谓。然后统计信息决定不保存,也就是不冗余存储统计信息了。大家知道,统计信息,一般只是用来展示数据使用,并不会参与到业务逻辑中去。所以,理论上我们不保存统计信息也可以,因为我们总是可以在需要的时候动态查询统计出所需要的信息,SQL的统计功能是很强大的,呵呵。
优点:
缺点:
这种方案,我觉得需要业务人员和设计人员仔细评估考量。大家觉得如何呢?
基于ENode框架实现,采用CQRS架构。领域模型的设计,也是设计为帖子和回复两个聚合根。不同的是帖子中聚合了回复的统计信息,设计一个值对象,表示帖子的当前回复的统计信息。包括:最近回复的ID、作者、回复时间,以及总回复数。为什么要这样设计?因为经过对领域进一步的分析和思考,发现了领域内的一个潜在的业务规则。就是我们关心帖子的“最后”的回复信息。关键问题就是在这个“最后”两个字上。既然是最后,那就必须要知道哪个是最后,根据什么判断?就是根据回复的创建时间。然后,在高并发创建回复的时候,我们需要有一个地方,可以准确的统计出这个最后的回复是哪个。前面几个方案,要么通过DB的强一致性事务保证,要么依赖消息队列的顺序消息处理(必须依靠开发人员要处理好各种EDA的问题才行)。这些方案虽然最终却是能统计出这个最后的回复信息;但我认为,这个是属于领域内的一个业务规则;这个规则是由于我们所关心的统计信息而必须引入的。所以,更好的方案应该是用聚合根来维护这个业务规则。
所以,帖子本身可以不用聚合所有的回复信息,因为帖子本身确实不关心回复信息本身,它只是关心回复的统计信息(最后一个回复的信息以及总回复数);因此,我们设计帖子聚合时,只需要再让其内聚一个包含回复统计信息的值对象即可。
当有一个新的回复产生后,发送一个命令,通知回复的帖子更新其统计信息;然后帖子处理这个命令时,内部判断当前回复的时间是否是最新时间,如果是,则更新最后回复信息和总回复数;如果不是,则只更新回复数。然后帖子的统计信息更新后,产生领域事件,表示帖子的统计信息有更新,然后CQRS的event handler,根据领域事件,更新读库帖子表的那几个统计字段即可;
这种方案,通过ENode提供的技术保证,可以确保消息至少投递一次,确保消息的幂等处理,以及消息(领域事件)的顺序处理,以及最重要的一点,最同一个聚合根,确保尽量做到了无并发操作;就算出现并发,也能框架层面自动重试。所以,开发人员就不用再关心这些技术相关的问题了。
个人认为,这种方案在基于ENode框架引入CQRS架构的异步思路,解决高并发的问题的同时,进一步挖掘了业务需求,分析出了潜在的业务规则,通过用聚合根来维护这个业务规则,最终确保了数据的最终一致性;缺点是,依赖了ENode框架。对我个人来说,肯定是最喜欢这种方式,呵呵。目前enode forum案例,就是采用这种方式来实现帖子的回复统计信息的维护和存储。
不知不觉又下半夜了,眼睛酸,不写了,基本把能想到的都写了,呵呵。
DDD实践问题之 - 关于论坛的帖子回复统计信息的更新的思考
标签:
原文地址:http://www.cnblogs.com/netfocus/p/4480760.html