问题描述
我目前正在开发一个单片系统,我想将其引入现代并结合DDD和CQRS。有人向我提出了重新编写解决方案导入机制的请求,并认为这可能是启动此重新架构过程的好机会。
I am currently working on a monolithic system which I would like to bring into the modern day and incorporate DDD and CQRS. I have been presented with a request to re-write the importing mechanism for the solution and feel this could present a good opportunity to start this re-architecting process.
流程是:
- 用户上传CSV
- 系统解析CSV并在屏幕上显示每一行。验证发生在每一行,并且与每一行相关的错误/警告
- 用户可以修改每一行,然后重新验证所有行
- 然后用户选择没有错误并提交导入的行
- 行导入以及任何未选择的行,或者有错误的行进入保留区域,以便以后可以处理
- User uploads CSV
- System parses CSV and shows each row on screen. Validation takes place for each row and errors/warnings associated with each row
- User can modify each line an re-validate all rows
- User then selects rows that don't have errors and submits the import
- Rows import and any non-selected rows, or rows with errors go into a holding area so they can deal with them at a later date
此操作的其他详细信息是多行可能属于同一实体(例如,两行可能是订单中的订单项,因此
Additional details for this is that multiple rows could belong to the same entity (E.g. 2 rows could be line items in an order, so would have the same Order Ref).
我当时在考虑一个导入传奇,它会生成一堆导入聚合(例如OrderImportAggregate),然后在导入时提交的对象将被转换为当前在整个系统中使用的类,希望在以后进行重新架构时,它们本身将成为聚合!因此,传奇过程将遵循以下方式:
I was thinking of having an import saga that would generate a bunch of import aggregates (e.g. OrderImportAggregate), and then when the import is submitted those would get converted into the class used across the system currently, which would hopefully become aggregates in their own right when re-architected further down the line! So the saga process would take on something along the lines of:
- [EntityType] FileImportUploaded-存储CSV
- [EntityType] FileImportParsed-生成n个[EntityType] Import聚合。[EntityType] ImportItemCreated引发/处理的事件
- 流程将调用当前实体通过的验证例程生成错误列表(如果有),并针对每个项目进行存储。 [EntityType] ImportItemValidated引发/处理的事件
- 每次在屏幕上更改行时,它都会调用一个saga和item ID的Web api方法来更新详细信息并重新验证按点3行。
- 用户将导入,服务组实体一起提交,例如,基于ref,它们将转换为当前系统实体并调用其导入/保存例程。引发[EntityType] ImportItemCompleted事件。
- [EntityType]FileImportUploaded - Stores the CSV
- [EntityType]FileImportParsed - Generates n number of [EntityType]Import aggregates.[EntityType]ImportItemCreated events raised/handled
- Process would call the validation routine that the current entities go through to generate a list of errors, if any, and store against each item. [EntityType]ImportItemValidated events raised/handled
- Each time a row is changed on screen, it calls a web api method for the saga and and item id to update the details and re-validate the row as per point 3.
- User submits import, service groups entities together, based on ref for example, they get converted into the current system entity and calls their import/save routine. [EntityType]ImportItemCompleted event raised.
- 当所有聚合都处于ImportItemComplete状态时,佐贺完成
由于这是我的CQRS / Event Sourcing / DDD的第一个实现,因此我想以正确的基础开始,因此想知道这是否是理想的方法为了实现这一功能?
As this was my first implementation of CQRS/Event Sourcing/DDD, I wanted to start off on the right foundation, so was wondering if this is a desired approach for this functionaility?
推荐答案
我建议您将您的域分为两个单独的子域,分别实现为有界上下文有界上下文是导入有界上下文
( ImportBC
),另一个是接收有界上下文
( ReceivingBC
,我不知道实际名称,请相应地替换它。)
I suggest that you break your domain into two separate sub-domains implemented as to separate bounded context, one bounded context being the Import bounded context
(ImportBC
) and the other being the receiving bounded context
(ReceivingBC
, the actual name is not know to me, please replace it accordingly).
然后,在导入BC
中,您应该使用 CRUD
样式实现,为每个导入文件都有一个实体并使用持久性来记住验证和导入过程中的进度(此实体保存了一个列表尚未进口的商品)。在人员验证了每个项目之后,可以将一条命令发送到 ReceivindBC
中的集合,以根据业务规则测试该集合是否有效,但无需提交更改存储库!您这样做是为了使人类用户知道该项目是否确实有效,并启用/禁用导入按钮
。这样,您就不会在两个有界上下文中重复验证逻辑。当用户实际按下导入按钮
时,将导入命令发送到 ReceivingBC
中的集合中,而您实际上提交了更改到存储库。此外,您还可以从导入文件CRUD实体
中删除导入项目。
Then, in the Import BC
you should implement using the CRUD
style, having an entity for each import file and use a persistence to remember the progress on the validation and import process (this entity holds a list of not-yet imported items). After each item is validated by a human, a command could be sent to the aggregates in the ReceivindBC
to test if the aggregate is valid according to the business rules, but without committing the changes to the repository! You do this so that the human user would know if the item is indeed valid and to enable/disable an import button
. In this way you don't duplicate the validation logic inside the two bounded contexts. When the user actually presses the import button
send the import command to the aggregate in the ReceivingBC
and you actually commit the changes to the repository. Also, you remove the import item from the import file CRUD entity
.
这种发送命令的技术,但实际上并没有持久存储到存储库中有助于在 UI
中帮助用户体验(在 UI $内部没有重复的逻辑) c $ c>),并且如果您遵循
DDD
最佳做法并将聚合设计为纯的,无副作用的对象(与存储库无关,则不要这样做)是可行的知道它们的存在,根本不使用它们!)。
This technique of sending commands but without actually persisting into the repository is useful in helping the user experience in the UI
(without duplicating logic inside the UI
) and it is doable if you follow the DDD
best practices and design your aggregates to be pure, side-effect free objects (to be Repository agnostic, to not know of their existing, to not use them at all!).
这篇关于导入数据和事件来源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!