ABAP DDD 如何通过 RFC 从聚合中正确实现 add/update/remove child?

ABAP DDD how to correctly implement add/update/remove child from aggregate via RFC?

我正在努力为我当前的项目遵循 DDD 原则。不幸的是,由于技术限制,我不得不使用 RFC,所以没有 OData 也没有 REST。这是一个很长的问题,我希望可以在 Whosebug 中提出这个问题。

无论如何,我有一个实体 class WorkOrder,其中包含操作列表 objects。

我有一个带有 SAVE 方法的 WorkOrderRepository class,它只接收一个 WorkOrder object 并且能够一次性保存所有内容(header 数据、地址等)。无论是创建、更新还是删除。存储库对其余部分隐藏了 BAPI 调用。

现在我想实现逻辑 add/update/remove 对工单 object 的操作,我不确定我给方法的名称是否正确。也许他们应该是insert/edit/delete...我对此很困惑,因为在我看到的每个地方他们都使用不同的名字。

但最重要的是我的 2 个具体疑问:

  1. 我应该只有 1 个 RFC 来接收 WorkOrder 实体的所有更新,包括 header 操作吗?或者我应该为每次只处理一个操作的操作创建 1 个 RFC 吗?请记住,UI 模型期望用户在单击“保存”按钮之前可以进行 add/delete 多项操作,并且 RFC 具有隐式提交,据我所知,DDD 实体应该始终在一次调用中更新。

选项 1:

FUNCTION ZWORKORDER_HDR_UPD
  IMPORTING
    VALUE(I_WORKORDER_ID) TYPE AUFNR
    VALUE(I_WORKORDER_HDR_CHG) TYPE ZWORKORDER_HDR_CHG
    VALUE(I_WORKORDER_HDR_UPD) TYPE ZWORKORDER_HDR_UPD "X structure for the BAPI
    VALUE(I_OPERATIONS_CHG) TYPE ZOPERATIONS_CHG
    VALUE(I_OPERATIONS_UPD) TYPE ZOPERATIONS_UPD
    VALUE(I_OPERATIONS_DEL) TYPE ZOPERATIONS_DEL
  EXPORTING
    VALUE(E_ERRORS) TYPE BAPIRET2_T.

选项 2

FUNCTION ZWORKORDER_OPERATION_CRT
  IMPORTING
    VALUE(I_WORKORDER_ID) TYPE AUFNR
    VALUE(I_OPERATION) TYPE ZOPERATION_CHG
  EXPORTING
    VALUE(E_ERRORS) TYPE BAPIRET2_T.

FUNCTION ZWORKORDER_OPERATION_UPD
  IMPORTING
    VALUE(I_WORKORDER_ID) TYPE AUFNR
    VALUE(I_OPERATION_CHG) TYPE ZOPERATION_CHG
    VALUE(I_OPERATION_UPD) TYPE ZOPERATION_UPD
  EXPORTING
    VALUE(E_ERRORS) TYPE BAPIRET2_T.

  FUNCTION ZWORKORDER_OPERATION_DEL
      IMPORTING
        VALUE(I_WORKORDER_ID) TYPE AUFNR
        VALUE(I_OPERATION_ID) TYPE ZOPERATION_ID
      EXPORTING
        VALUE(E_ERRORS) TYPE BAPIRET2_T.
  1. 我的 Workorder 方法应该如何处理这个问题?我对 update 方法特别困惑,因为我不确定我是应该先获取现有操作然后更新它,还是让 parent class 来做。但也许我的做法从根本上是完全错误的。

选项 1:

workorder->add_operation( i_operation ). "Pass flat structure from RFC? Or first create object?
workorder->update_operation( i_operation_chg
                             i_operation_upd ).
workorder->delete_operation( i_operation_id ).

选项 2:

workorder->add_operation( ).
operation = workorder->get_operation(i_operation_chg->get_id())
operation->update( i_operation_chg
                   i_operation_upd ).
operation->delete_operation( i_operation_id ).

最简单的解决方案总是最好的(KISS and YAGNI原则)。创建 1 个或 3 个启用 RFC 的功能模块并不重要,所以如果您可以使用一个功能模块实现您的目标,那么就使用一个。

  1. 我想你需要有两个支持RFC的功能模块。一个验证维护的操作(尽可能进行验证),但不应将任何内容保存到数据库中,另一个在用户单击“保存”按钮后调用,以保存整个 "WorkOrder",包括维护的操作(此时,也会有完整的验证)。

  2. 如果您不需要为其他内容定义 "operation" class,立即,然后保持简单,无需实例化对象。请注意,您可以使用私有静态方法创建 "operation" class,并成为 "workorder" class 的好友(只有此 class 可以使用操作 class), 只是为了更好地组织你的代码。

PS:虽然我不知道什么是"Domain-Driven Design",但我看不出你的问题与它有什么关系,因为它看起来只是简单的程序设计。

我们也使用 DDD,不过幸运的是没有使用 RFC,而是 Gateway/OData。由于 REST 根据定义是无状态的,因此我们始终在应用程序服务层中提交。

我们所做的是拥有三个 "DDD" 实体 app_Service、domain_service、存储库(以及一些数据容器,如聚合),其中 app_service 在您的案例中将公开方法创建、更新和删除,并且可能还验证。

然后我会编写四个简单的 RFC(crud + 验证),基本上将数据传递给应用程序服务。

至于能够在每次保存中处理多个更新,我们总是根据 UI 要求对我们的 OData(您的 RFC)进行建模,然后是 app_service 任务来理解数据从系统的角度来看。

我们大量使用 Abap Clean Code(它甚至是我们 ATC 检查的一部分),并且他们明确指出您应该有单独的方法,这也是面向对象的最佳实践。

https://github.com/SAP/styleguides/blob/master/clean-abap/CleanABAP.md#split-methods-instead-of-adding-optional-parameters