根据http://cre8ivethought.com/blog/2009/11/12/cqrs--la-greg-young,负责使用事件发布者发布事件的组件是存储库.
我的问题很简单:为什么?
在这篇博文中,我们被告知:
域存储库负责发布事件,这通常在单个事务内部,同时将事件存储在事件存储中.
我希望这可以作为事件存储的任务:一旦存储了一个事件(或多个事件),它就会被发布.
那为什么它在存储库中呢?
我现在正在学习微服务数据复制,而我遇到的一件事就是提出了确保事件原子性的正确架构.我理解它的方式,基本流程是:
但是,如果在步骤1和2之间发生断电,该怎么办?在一个天真的系统中,这意味着更改仍然存在,但详细说明它们的事件将永远不会发布.我考虑过以下想法来创造更好的保证,但我不太清楚每种方法的优点和缺点:
答:在我的微服务实例中使用嵌入式数据库(如SQLite)来跟踪从提交到主数据库到事件发布的完整事务.
B:在我的主数据库中创建一个事件表,使用数据库事务插入事件并同时提交相关更改.然后,该服务将事件推送到总线,然后再次提交到主数据库以将事件标记为已发布.
C:如上所述,在我的主数据库中创建一个Events表,使用数据库事务插入Event并同时提交相关更改.然后,通过(通过服务中的REST /消息或通过数据库挂钩手动)通知已附加新事件的专用EventPusher服务.EventPusher服务将查询Events表并将事件推送到总线,将每个事件标记为已发布确认.如果在没有任何通知的情况下经过一定的时间,EventPusher将进行手动查询.
以上每个选择的优缺点是什么?我还有其他优势吗?
几天以来,我一直试图弄清楚如何告知其余的微服务,在微服务A中创建一个新实体,将该实体存储在MongoDB中.
我想要:
微服务之间的耦合很低
避免像两阶段提交(2PC)这样的微服务之间的分布式事务
起初像RabbitMQ这样的消息代理似乎是一个很好的工具,但后来我看到了在MongoDB 中提交新文档并在代理中发布消息而不是原子的问题.
为何选择活动?by eventuate.io:

解决此问题的一种方法意味着通过添加一个标记来说明文档的模式有点脏,该标记表明文档是否已在代理中发布,并且具有在MongoDB中搜索未发布文档的预定后台进程,并使用这些文档将这些文档发布到代理确认,当确认到达时,文档将被标记为已发布(使用at-least-once和idempotency语义).这个解决方案在此提出并且这个答案.
阅读Chris Richardson 的微服务简介我最后在这个关于开发功能域模型的伟大演示中,其中一个幻灯片询问:
如何在没有2PC的情况下自动更新数据库并发布事件和发布事件?(双写问题).
答案很简单(在下一张幻灯片中)
更新数据库并发布事件
这是一种不同的方式来这一个是基于CQRS一拉Greg Young的.
域存储库负责发布事件,这通常在单个事务内部,同时将事件存储在事件存储中.
我认为委托将事件存储和发布到事件存储的责任是一件好事,因为避免了2PC或后台进程的需要.
然而,以某种方式它的真实是:
如果您依赖事件存储来发布事件,那么您将与存储机制紧密耦合.
但是,如果我们采用消息代理来进行微服务的交互,我们可以说同样的话.
令我担心的是,事件存储似乎成为单点故障.
如果我们从eventuate.io看这个例子

我们可以看到,如果事件存储已关闭,我们无法创建帐户或资金转移,失去了微服务的优势之一.(虽然系统将继续响应查询).
因此,确认eventuate示例中使用的事件存储是单点故障是正确的吗?
我一直在研究域驱动设计和域事件.我非常喜欢这些事件提供的关注点的分离.我遇到了持久化域对象和提升域事件的问题.我想在域对象中引发事件,但我希望它们是持久性无知的.
我ShoppingCartService用这种Checkout方法创建了一个基本的:
public void Checkout(IEnumerable<ShoppingCartItem> cart, Customer customer)
{
var order = new Order(cart, customer);
_orderRepositorty.Add(order);
_unitOfWork.Commit();
}
Run Code Online (Sandbox Code Playgroud)
在此示例中,构造函数Order将引发OrderCreated可由某些处理程序处理的事件.但是,我不希望在实体尚未持久化或持久化以某种方式失败时引发这些事件.
为了解决这个问题,我找到了几个解决方案:
1.提升服务中的事件:
我可以在服务中引发事件,而不是在域对象中引发事件.在这种情况下,该Checkout方法会引发OrderCreated事件.这种方法的缺点之一是,通过查看Order域对象,不清楚哪些事件是由哪些方法引发的.此外,开发人员必须记住在其他地方创建订单时提出事件.感觉不对劲.
另一个选项是对域事件进行排队,并在保持成功时引发它们.这可以通过一个using声明来实现,例如:
using (DomainEvents.QueueEvents<OrderCreated>())
{
var order = new Order(cart, customer);
_orderRepositorty.Add(order);
_unitOfWork.Commit();
}
Run Code Online (Sandbox Code Playgroud)
该QueueEvents<T>方法将布尔值设置为,true并且该DomainEvents.Raise<T>方法将对事件进行排队,而不是直接执行它.在dispose回调中QueueEvent<T>,执行排队事件,确保已经发生持久化.这似乎相当棘手,它需要服务知道域对象中引发了哪个事件.在我提供的示例中,它也只支持一种类型的事件,但是,这可以解决.
我可以使用域事件来持久化对象.这似乎没问题,除了持久化对象的事件处理程序应首先执行这一事实,但我在某处读到域事件不应该依赖于特定的执行顺序.也许这不是那么重要,域事件可能以某种方式知道处理程序应该执行的顺序.例如:假设我有一个定义域事件处理程序的接口,实现将如下所示:
public class NotifyCustomer : IDomainEventHandler<OrderCreated>
{
public void Handle(OrderCreated args)
{
// ... …Run Code Online (Sandbox Code Playgroud) c# asp.net-mvc persistence domain-driven-design domain-events