我尝试使用dddd实现事件源系统。目前,我正在努力解决我的事件如何以及在哪里越过有界背景的边界。
想象一下有两个有界的背景:
产品管理拥有有关产品的所有知识。为了简化,它只是"名称"。物流系统也有产品,但不了解他们的元数据。对于他们来说,它主要只是一个带有Id的物理盒子。但是当有人扫描这个产品时,他们想要显示这个名字。因此,ProductManagement BC应通知物流BC,产品已注册且名称已更改。因此,我将最终得到ProductManagement中的事件,这些事件来自ProductAggregate:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream> // std::ifstream
#include <boost/program_options.hpp>
int main ()
{
boost::program_options::options_description desc("Allowed options");
desc.add_options()
("sign" , program_options::value<string>() -> default_value("gbm") ,"name of the input")
("week" , program_options::value<double>() -> default_value(1930) ,"number of the week")
("day" , program_options::value<double>() -> default_value(0) ,"number of the day in within the week")
("hour" , program_options::value<double>() -> default_value(0) ,"time in hour")
("minute", program_options::value<double>() -> default_value(0) ,"time in minute")
("second", program_options::value<double>() -> default_value(0) ,"time in second")
;
cout << "Done!" << endl;
return 0;
}
当我正确地得到它时,这些是我将保存到事件存储中的事件。这些也是将发布到消息总线中的事件。所以在物流方面,我会订阅这些活动。到目前为止一切都很好。
现在的问题是: 我将如何在物流方面处理此事件? Vaughn Vernon在一次演讲中说,最好在那里有一个事件处理程序,它位于应用程序层,因此它基本上是一个应用程序服务。他还说,最好将其转换为一个或几个命令。我是否会再次在物流方面保存所有收到的活动?我还保存命令吗?如果出现问题,我该如何重现当前状态?或者我怎么知道,这不是接收有界上下文中处理的错误,而是错误的事件。如果我的转换命令被拒绝,我该怎么办?
我知道物流方面的总量没有计算或变化。但我认为这对我的问题并不重要。
答案 0 :(得分:1)
这里有很多事情。
首先,您不必导入物流BC关于名称更改。您可以在需要时从客户端获取PM BC的信息。这通常通过某种复合UI来完成。 UI组合可以在客户端或(Web)服务器上完成。您可能需要查看Mauro Servienti撰写的文章The secret of better UI composition,并对其进行描述。
但总的来说,这通常是这样的:
domain event -> pub/sub -> message consumer -> command -> domain command handler
所以,
RegisterProduct
命令发送到同一BC Product
聚合
醇>
它不仅适用于事件源系统,而且适用于任何具有多种服务的系统,使用事件驱动架构。
答案 1 :(得分:0)
对于您描述的用例,您只需要物流系统使用的产品的某些属性。物流系统可以通过订阅您描述的事件来保留所需产品信息的本地缓存 - 这可能是一个简单的内存缓存。当你处理阅读模型时,他们不需要转换成命令或类似的东西。一个看法。只需要一个简单的事件处理程序来处理事件并在某处更新某个状态 - 无需在读取端事件源它。当物流系统需要产品的名称时,它只是从本地缓存中获取它。由于产品管理仍然是事实的来源,你还没有打破这两种情况的自主权。
如果您需要重建状态,只需擦除缓存并通过处理程序重播所有事件。但请记住,产品管理上下文拥有这些事件,因此它们只应保存在那里,而不是保存在后勤环境中 - 如果您想重建状态,则需要重新发布它们的方法
顺便说一下,这一系列博客文章描述了这个确切的用例:
(如果我没记错的话,在第5部分)
或者,您可以执行某种UI组合,其中名称来自“产品管理”上下文,而其他详细信息来自“物流”上下文(也在上面的博客文章中讨论过)
答案 2 :(得分:0)
[...]他还说,最好将其转换为一个或几个命令。我是否会再次在物流方面保存所有收到的活动?我还保存命令吗?
首先,您必须向域专家询问该事件是否会导致影响LS上下文的副作用。只有在这种情况下,您必须订阅此事件并将相关命令发送到将更改并提交其状态的LS Aggregate,或者,如果您选择事件源此聚合,则另一个事件。
如果出现问题,如何重现当前状态?或者我怎么知道,这不是接收有界上下文中处理的错误,而是错误的事件?如果我的转换命令被拒绝,我该怎么办?
事件是发生事件的表示,因此不能“错误”。无论如何,事件触发的命令可能会失败。您在谈论哪种类型的失败?技术或领域特定?在第一种情况下,源事件将保留在总线中以供将来重试(可能在修复一些错误之后)。在第二种情况下,如果需要告知PM聚合结果,LS聚合应该发出一个适当的事件,而这个事件将由PM聚合处理。