Domain driven design ABAP DDD如何通过RFC正确实现从聚合中添加/更新/删除子级?

Domain driven design ABAP DDD如何通过RFC正确实现从聚合中添加/更新/删除子级?,domain-driven-design,abap,Domain Driven Design,Abap,我正在努力为我当前的项目遵循DDD原则。不幸的是,由于技术限制,我不得不使用RFC,因此没有OData和REST。这是一个很长的问题,我希望可以在Stackoverflow中问这个问题 在任何情况下,我都有一个实体类WorkOrder,其中包含一个操作对象列表 我有一个带有SAVE方法的WorkOrderRepository类,该类只接收一个WorkOrder对象,能够一次性保存所有标题数据、地址等。无论是创建、更新还是删除。存储库对其余部分隐藏BAPI调用 现在,我想实现向work order

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

在任何情况下,我都有一个实体类WorkOrder,其中包含一个操作对象列表

我有一个带有SAVE方法的WorkOrderRepository类,该类只接收一个WorkOrder对象,能够一次性保存所有标题数据、地址等。无论是创建、更新还是删除。存储库对其余部分隐藏BAPI调用

现在,我想实现向work order对象添加/更新/删除操作的逻辑,但我不确定我给方法的名称是否正确。也许它们应该被插入/编辑/删除。。。我对此感到很困惑,因为在我看到的每个地方,他们都使用不同的名字

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

我是否应该只有1个RFC接收对工作订单实体的所有更新,包括标题、操作?或者我应该为每次只处理一个操作的每个操作创建一个RFC吗?请记住,UI实体模型期望用户在单击保存按钮之前可以添加/删除多个操作,并且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.
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

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.
我的Workorder方法应该如何处理这个问题?我对update方法特别感到困惑,因为我不确定是应该先获取现有操作,然后更新它,还是让父类来完成它。但也许我的方法从根本上是完全错误的。 备选案文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.
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 ).

最简单的解决方案永远是最好的解决方案和原则。创建1个或3个启用RFC的功能模块并不重要,因此,如果您可以用一个功能模块实现目标,那么就用一个模块实现

我认为您需要有两个启用RFC的功能模块。一个用于验证维护的操作,尽可能地进行验证,但不应将任何内容保存到数据库,另一个在用户单击“保存”按钮后调用,以保存整个WorkOrder,包括此时维护的操作,还将进行完整的验证

如果您现在不需要为其他对象定义操作类,那么请保持简单,无需实例化对象。请注意,您可以使用私有静态方法创建操作类,并且作为workorder类的朋友,只有该类可以使用操作类,以便更好地组织代码


PS:虽然我不知道什么是域驱动设计,但我不知道你的问题与它有什么关系,因为它看起来只是简单的程序设计。

我们也使用DDD,尽管幸运的是不是使用RFCs,而是使用Gateway/OData。由于REST按定义是无状态的,所以我们总是在应用程序服务层中提交

我们要做的是有三个DDD实体 app_服务、domain_服务、repository以及一些数据容器(如聚合),在您的案例中,app_服务将公开创建、更新和删除的方法,可能还会验证这些方法

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

至于能够在每次保存中处理多个更新,我们总是根据UI要求对OData your RFC进行建模,然后从系统角度理解数据是应用程序服务的任务

我们大量使用Abap干净的代码,这甚至是我们ATC检查的一部分,并且它们清楚地声明您应该有单独的方法,这也是OO的最佳实践


谢谢你的回答。事实上,我删除了我的问题中解释DDD部分的部分,因为我最终意识到它不相关。但DDD只是指我的WO代表我的特定业务,并隐藏所有BAPI/ECC表的详细信息,即一些合作伙伴只是我WO中的单个工单标题字段。无论如何,我想创建一个操作对象来处理更复杂的问题,比如基于业务规则的状态更改,我不太喜欢静态方法,所以我想我会选择更新选项2,首先获取引用。你认为呢?在两个非面向对象的概念UI和BAPI之间添加一些复杂的面向对象代码可能会适得其反。这只是根据你提供的信息我的意见。如果你有其他理由,我可以改变主意。例如,如果您想要自动化单元测试,那么就需要使用实例成员。无论如何,您仍然可以采用一种中间方法,比如使用一个操作管理器单例,只需实例化一次即可管理所有操作。注意,我的答案更多的是关于使用KISS和YAGNI原则进行编码,我不知道最好的解决方案是什么。