导入数据和事件采购

时间:2017-04-26 16:54:44

标签: domain-driven-design cqrs event-sourcing

我目前正在开发一个单片系统,我想将其纳入现代,并采用DDD和CQRS。我收到了重新编写解决方案导入机制的请求,并认为这可能是开始重新构建过程的好机会。

目前的流程是:

  1. 用户上传CSV
  2. 系统解析CSV并显示屏幕上的每一行。对每行进行验证以及与每行相关的错误/警告
  3. 用户可以修改每一行并重新验证所有行
  4. 用户然后选择没有错误的行并提交导入
  5. 行导入和任何未选中的行或有错误的行进入保留区域,以便日后处理它们
  6. 其他详细信息是多行可能属于同一个实体(例如,2行可能是订单中的订单项,因此会有相同的订单参考号。)

    我正在考虑有一个导入传奇,它会生成一堆导入聚合(例如OrderImportAggregate),然后当提交导入时,这些将被转换为当前系统中使用的类,这有望成为聚合他们自己的权利,当重新架构进一步下线!因此,传奇过程将采取以下方式:

    1. [EntityType] FileImportUploaded - 存储CSV
    2. [EntityType] FileImportParsed - 生成n个[EntityType]导入聚合。[EntityType]引发/处理的ImportItemCreated事件
    3. 进程将调用当前实体经历的验证例程以生成错误列表(如果有),并针对每个项目进行存储。 [EntityType]引发/处理的ImportItemValidated事件
    4. 每次在屏幕上更改行时,它会调用saga的web api方法和项目ID来更新详细信息,并按照第3点重新验证行。
    5. 用户提交导入,服务组实体,例如,基于ref,它们被转换为当前系统实体并调用其导入/保存例程。 [EntityType]引发了ImportItemCompleted事件。
      1. 当所有聚合都处于ImportItemComplete状态时,Saga完成
    6. 由于这是我第一次实施CQRS /事件采购/ DDD,我想从正确的基础开始,所以想知道这是否是这种功能的理想方法?

3 个答案:

答案 0 :(得分:2)

我建议您将域名划分为两个单独的子域名,这些子域名实现为分隔有界上下文,一个有界上下文为Import bounded contextImportBC),另一个为receiving bounded contextReceivingBC,实际名称不为我所知,请相应更换。)

然后,在Import BC中,您应该使用CRUD样式实现,为每个导入文件创建一个实体,并使用持久性来记住验证的进度和导入过程(此实体包含尚未导入的项目列表)。在人员验证每个项目之后,可以将命令发送到ReceivindBC中的聚合,以根据业务规则测试聚合是否有效,但不将更改提交到存储库!这样做是为了让人类用户知道该项目是否确实有效并启用/禁用import button。这样,您就不会在两个有界上下文中复制验证逻辑。当用户实际按下import button时,将导入命令发送到ReceivingBC中的聚合,并实际将更改提交到存储库。另外,您从import file CRUD entity

中删除了导入项

这种发送命令但实际上没有持久存储到存储库的技术对于帮助UI中的用户体验(不在UI内部重复逻辑)非常有用如果您遵循DDD最佳实践并将您的聚合设计为纯粹的,无副作用的对象(与存储库无关,不知道它们的存在,根本不使用它们),则是可行的。)

答案 1 :(得分:1)

首先,您必须问问自己为什么使用CQRS。 CQRS是建筑中的重型18轮车。我知道尖叫CQRS的2个充分理由

1)您需要支持撤消功能

2)将来实施新要求时,您也希望将这些要求应用于过去的数据。

然而,您所描述的部分要求与crud非常相似。 (您导入一组行,列出一组行,编辑这些行,然后标记为已完成的行将从其输入状态中删除并转换为其他类型的实体。

如果您觉得描述特定实体和适用的验证规则有很多复杂性,那么DDD将是一个不错的选择。但仍然我会考虑缩小它并构建一个simle mvc风格的应用程序来实现这个(取决于该项目还需要什么)

即使这是一个更大的域的一部分我会建议一个微服务方法,这将是一个完全独立的导入应用程序(在这种情况下,你仍然可以提出一个ImportCompleted事件,并把它放在一个服务总线与多个其他听取那个事件的应用程序)

注意:CQRS不是事件源,cqrs将命令(更新)堆栈与查询堆栈分开。它经常与事件采购结合使用。但是,在任何地方突然出现的事件都可能是一种痛苦,特别是因为提升事件的事件往往不那么明显,以及事件是否有相互影响(如果订单完成和订单取消事件被提升,订单会发生什么? ,可能有时间问题,首先处理一个)

答案 2 :(得分:0)

我不是 DDD 专家,但这是我对此的看法。我不会使用单独的有界上下文,因为在我看来,域对象的导入理想情况下可以与它们所属的有界上下文处于相同的有界上下文中。很想听听专家的意见,为什么会出错!

  1. 将 csv 解析为代表数据导入的聚合并将其保留(到暂存区/表等)。我们将来可以从这里加载这个聚合。解析 CSV 文件以创建此聚合可以建模为命令“CreateDataImportFromCsvFile”等。
  2. 构建一个加载该聚合并显示它的 UI。聚合可以包含域对象“客户导入项”的列表,每个“客户导入项”可以包含“IsSelected”属性以及正在导入的域对象,即“客户”域对象本身。这意味着您在使用要导入的实际域对象时不会重复验证规则。您可以对这些对象进行水合并将它们显示在 UI 中。当用户单击导入按钮时,您会发出一个命令。您可以通过循环遍历聚合上每个选定且有效的“导入项”并在其域模型上调用 Save() 来处理该命令,然后将导入项标记为已处理。理想情况下,这一切都在外部事务范围内完成(取决于您是否想要原子性与最终一致性等)。然后,您的 UI 可以选择不显示已处理的导入项目,也可以将它们显示为禁用状态或其他任何内容,具体取决于用户是否还能够查看到目前为止实际已处理的内容与剩余的内容。