0

我正在构建的软件的中心原则是“工单”

在我看来,WorkOrder 将是一个“聚合根”,其中包含有关工作订单的基本信息,例如创建日期、型号/制造商、序列号、采购订单。

除了这些“价值”对象之外,还有子“实体”“聚合”,例如:

  • 序列
  • 返工
  • 方面
  • 报价单
  • 消耗品

如果没有相关的工单,上述任何一项都不能/不应该存在。在现有系统中,他们实际上偶尔会这样做,但这是因为缺乏事务或代码检查以确保完整性。它们是孤立的记录,并通过定期清理删除 - 这是我更多地了解 DDD 和 ORM 以加快我们的开发实践的众多原因之一。

注意:这可能是题外话,可能会在您的回复中被跳过

因为我们主要是使用extJS的基于web的界面,每个列表控件显示上面的每一个,我一直不愿意切换到ORM和DDD。每个列表都通过一个查询数据库的控制器:操作填充(即:当 JS 控件使用 GET 命令调用序列 REST URI 时填充序列列表)。此 GET 命令调用实例化序列对象的控制器并调用selectAllForWorkorderID方法

我对 ORM 的理解是我会使用存储库来查询这些项目。很好,但是如果这个序列对象(在 DDD 用语中)被认为是 WorkOrder 根的聚合 - 那么我必须首先找到工作订单并通过 WorkOrder 遍历序列。

在基于 AJAX Web 的上下文中,这对我来说很有趣 - 但在桌面环境甚至标准的基于 Web 的上下文中,这是可以接受的,因为每次在主列表中选择 WorkOrder 项目时,我只会查询一次 WorkOrder 对象。每个要填充的单独列表不是 6 或 8 次。

我现在可以看到,我们的系统实际上有几个聚合根对象,工单只是少数几个中更复杂的一个:

  1. 工作指示
  2. 保证
  3. 维修订单

是主根。保修取决于工作订单 ID,维修订单可以但并非总是如此。

忽略后者的根源 - 让我只关注 WorkOrder。

当我开始检查现有模型并尝试确定什么是业务逻辑和/或应用程序逻辑时,我有点困惑。“服务”“聚合根”的区别。

在当前模型中考虑一种这样的方法:

createWorkOrderFromRpi。

RPI 是经批准的文件,可作为 WorkOrder 的模板——它们规定了“可以”执行的顺序和执行顺序、尺寸、耗材清单等。这完全是一个单独的系统,我认为最好将其描述为“模块” “在 DDD 命名法中。

该方法需要查询RPI系统,获取工单头明细、序列表、耗材等。

一旦有了这些数据,它就会调用相关的对象和方法:

  1. WorkOrder.Create(标题详细信息)
  2. Sequence.Create(Sequence Details) - 循环完成 (1:m)
  3. Consumable.Create(Consumable Details) - 循环完成 (1:m)

在遵循 DDD 时,我很想让 WorkOrder “聚合根”提供一个具有相同签名的方法,但我不愿意这样做。

我相信作为 WorkOrder 聚合的每个“实体”都符合描述,并且不应该暴露于“根”之外的任何东西,除非遍历根本身。在某些情况下,情况并非如此。On second thought, the interface only ever exposes consumables and sequences and such when a work order is selected which would imply a work order must be loaded anyway?!?

此方法必须执行一些基本的业务规则:

  1. 具有相同序列号的工作订单尚未在系统中有效(除非已归档),除非它在分包合同中,在这种情况下不要创建新工作,而是接收此工作订单的维修订单。

还有一些“规则”,但为了简洁起见,我将它们排除在外。

各个实体执行微业务验证,例如序列号等某些字段具有特定格式,部件号和采购订单号也是如此。

上面给出了我的主要问题或担忧,这种方法最好在“聚合根”“服务”中实现吗?

更新 | 最后一个问题......如果聚合根是正确的概念......我需要访问序列,以便我可以更新我将在概念上访问的字段(忽略语法),例如:

WorkOrder.Sequences(0).moveToNext()

如果这个方法是在序列“实体”中实现的,那是有道理的。技术细节和业务逻辑的划分在哪里?例如,要将工作订单从一个序列移动到下一个序列,我们更新每个序列的三个时间戳:

date_entered date_started date_finished

设置最后一个时间戳后,下一个序列 date_entered 设置为与前一个序列 date_finished 相同的时间,系统现在知道这是活动序列。那是技术问题。

但是业务规则或约束是:

  1. 如果移入历史记录,请勿移动工单
  2. 返工时不要移动工单
  3. 如果在 subcon 中,请勿移动工单

这些是规则,我希望将它们分开和区分,以便我可以轻松地以规范文档的形式翻译成英文,我可以将管理呈现为动态文档和功能证明。我有点希望这就是 DDD 以干净的方式强制/促进的。这是独立于 DDD 处理的要求吗?这就是 CQS 的用武之地吗?将业务规则与与利益相关者零相关的技术问题分开?

亚历克斯

4

1 回答 1

0

我认为您的 createWorkOrderFromRpi() 方法应该在“服务”而不是 WorkOrder 聚合根上。然后,此服务方法将调用您的存储库或 DAO 上的方法来创建工作订单。聚合根通常结合实体,但在您的模型上,我认为 RPI 是工作订单聚合根之外的模板或规范。如果 RPI 是聚合的一部分,那么您应该直接将方法放在存储库中并在任何地方调用它,因为存储库也是 DDD 中的业务对象。

关于第二个问题,我相信 WorkOrder Aggregate Root 对于您列出的其他“依赖”实体是完全正确的,即 Sequences Reworks Dimensions QuoteItems Consumables

我很想知道你是如何实现这一点的。

于 2013-11-06T19:28:25.037 回答