作者在SAP领域工作多年,对SAP应用的理解是:模型和基于模型的增删改查询问。只是SAP模型的复杂度比起我们大学专业课时做的作业,增加了几个数量级。相较于传统的增删改查,以订单排单领域为例,SAP订单模型的“增”还需要考虑实际业务中各种类型的前置订单和后置订单流程,即SAP使用的术语文档流(DocumentFlow)。而“变”,除了订单本身状态的迁移,还包括订单模型提供的各种可执行逻辑。这些逻辑既包括订单模型本身字段的变化,也包括订单与第三方系统的交互。在许多情况下,我们称这些逻辑为动作。如下图右下角所示:由于订单模型的复杂度如此之高,因此引入一种能够支持企业级订单编排应用的成熟、高质量的建模方法就显得非常重要。仅看一些示例,SAPCRM总共支持多少种标准订单类型?下图中以BUS2000开头的是不同的订单类型。我没有具体数过,但总有几十个。对于SAPCloudforCustomer,虽然CRM命名空间下的BusinessObjects数量比SAPCRM少,但实现销售自动化流程的基本订单模型还是一应俱全。我们先来看一下SAPCRM的订单模型。是不是可以用一套模型来描述SAPCRM支持的几十种订单类型?是的,这就是SAPCRMOneOrder模型,其特征在于其自我描述的名称。下面是SAPCRM应用的架构图:在架构方面,OneOrder框架位于上图中红色区域,包括数据库表、ABAP结构以及操作它们的API代码。当我第一次接触到OneOrder模型时,我很惊讶地发现它没有直观的图形界面可以展示模型的全貌。然而,缺陷并不能掩盖优点。一个20年前诞生的框架,我们不应该用20年后的标准来要求它。想象一下,不同类型的订单有什么共同点?无非是每个订单都有headerstructure,lineitems。一些结构,从业务的角度来看,可以同时出现在订单的表头和行项目中,例如参与订单的业务伙伴(Involvedparties)的详细信息,组织结构(OrganizationUnit)等.有些字段只能出现在行项目中,例如售出的产品信息(Product、ScheduledLine)。SAPOneOrder建模的原理类似于我们小时候玩的积木。构成OneOrder模型的最小粒度单元是一个结构,它充当积木,可以在事务代码CRMC_OBJECTS中查看。下图是这些结构的列表。如果SAP标准结构无法满足需求,客户仍可自行创建新结构。然后我们使用积木将业务中的一些相关结构组合起来,并将它们分配给某个订单类型。例如描述服务流程的订单类型BUS2000116,由以下结构组成:有了模型之后,剩下的就是基于这些模型实现增删改查操作,也就是ABAP编程。OneOrderAPI的代码实现原理实际上是在设计模式上结合了Template模式和Observer-Publisher模式。我们在学习模板模式的时候,有一个经典的例子,就是上帝用模板模式来掌管众生的生老病死。我们每个人被父母实例化后,只能被动的执行模板中上帝定义的四种方法:生、老、病、死,而不能改变模板本身,比如改变这四种的顺序方法。连乔布斯都没有办法给自己加一个“长生不老”的方法。听起来很残酷,但这是真的。那么,在OneOrder框架中,作为OneOrder应用之神,定义了哪些模板方法呢?事务码CRMV_EVENT,指定BUS2000116,执行:得到如下列表。红色的第一列是构成上述OneOrder模型的构建块。蓝色的第二列是这些块感兴趣的事件列表。从图中可以看出,这些事件名称是自描述的,比如AFTER_CREATE、BEFORE_CHANGE、BEFORE_DELETE等。第三列黑色的ABAP函数就是这些事件的监听函数。这些监听函数的后缀EC代表EventCallback。借助以上框架,OneOrder应用开发者的开发工作变得异常轻松:(1)通过构建块,定义自己应用所需的OneOrder模型(2)实现需要的事件对应的监听函数模型中的注意力。至于这些监听函数是什么时候调用的呢?应用程序开发人员根本不必担心。由此,我们可以发现,OneOrder框架的实现将编程的复杂性从应用开发者转移到了框架实现上。OneOrder框架内的实现更为复杂。通常情况下,OneOrder框架的用户只需要了解CRM_ORDER_READ、CRM_ORDER_MAINTAIN等API的用法即可。OneOrder的API之一,为消费者提供修改操作的CRM_ORDER_MAINTAIN,所有SAP标准支持的结构都作为输入参数之一出现在参数列表中:虽然这种设计方式让参数列表有点冗长,但从另一个角度来看一方面,它还具有自我描述的作用,确保API使用者即使不阅读文档,也可以通过简单地自行浏览参数来大致了解API支持哪些数据修改。这也符合Google著名的API设计最佳实践文档,一个好的API应该满足的条件之一:易学易用,自我描述,不易引起误解。SAPCRM的部分功能迁移到SAPS/4HANA后,做了一些改造,包括OneOrder的改造。为什么要改造?因为SAPCRM搬到了S/4HANA,而S/4HANA的一个优势就是S/4HANA在SAP历史上第一次实现了OLTP和OLAP的完美结合,也就是唯一的数据源一个系统,可以同时满足Transaction交易应用和Analytics分析报表应用的需求。而SAPCRMOneOrder未改造前的模型无法匹配S/4HANA的上述特点。改造前,构成OneOrder模型最小粒度的每个结构都有自己独立的专用数据库表,命名规则一般为CRMD_加结构名。如果将这种底层存储模型原封不动地迁移到S/4HANA中,那么在运行报表统计等应用时就会出现性能问题——后台为了检索报表结果,需要在很多结构的存储表中创建各种数据库。表的内连接和外连接操作。当连接操作涉及的数据库表的大小增加到一定数量级时,整个应用的性能就不好了。笔者也参与了性能评估,最终我们决定修改OneOrder的底层数据模型。因为从研究到修改原型开发,再到正式开发,只剩下八个月的时间,所以我们选择了一种成本最低、改动最小的方法,对OneOrder框架。首先,我们摒弃了之前每个结构都有一个专用数据库表的做法。在S/4HANA中,每个订单类型只有两张表,一张用于存储表头级数据,另一张用于存储行项目数据。之前分散在不同结构表中的字段,现在都维护在这两个表中。由于这两个表中的所有字段都被展平了,所以我们内部称之为展平表(FlattenedTable)。存储模型大大简化后,我们基于这两个表创建CDS视图,供上层报表应用消费。改造后的简化模型可以满足S/4HANA中OLAP应用的要求。对于S/4HANAOLTP应用的改造,一句话,我们在设计模式上采用了适配器模式(Adapter),在API和简化的数据库表之间引入了一个微型中间件来起到Adapter的作用。当消费者通过OneOrderAPI进行读操作时,中间件负责恢复简化数据表中存储的数据,然后填充到OneOrderAPI上层的缓存中。对于后者,它不知道底层存储模型的变化,因为Adapter封装了读取底层数据和进行格式转换的逻辑,所以OneOrderAPI的上层不需要做任何改变,而且它可以完全像它在SAPCRM中也能正常工作。当消费者调用OneOrderAPI进行写操作时,在将每个结构对应的缓存中存储的数据持久化到数据库之前,Adapter还负责合并分散在不同缓存结构中的数据。然后将最终结构写入平面表。说完CRMOneOrder订单模型的设计,我们再简单看一下SAPCloudforCustomer的订单模型设计。虽然SAPCloudforCustomer的后台对客户和合作伙伴来说是不可见的,但是我们还是可以从合法渠道获取到其订单模型的一些设计信息。从SAP社区SAP员工的回复中了解到,ESF2和BOPF有很多相似之处,设计理念相似,但ESF2主要用于部署在云端的产品,比如SAPCloudforCustomer上的BusinessObject开发,然后后者主要服务于S/4HANA等OnPremises解决方案。和前面介绍的SAPCRMOneOrder框架一样,BOPF实现的订单模型也是由若干个积木式结构组成。这些结构如上图红色高亮区域所示,每个结构也都有自己的数据库表的专用存储。SAPCRMOneOrder中各个结构的事件监控功能采用ABAP传统的面向流程的功能实现,而BOPF则采用实现指定接口的ABAP类。两者原理相同,只是实现细节不同。SAPC4C订单模型虽然和传统的SAPCRM的OneOrder模型一样,每个结构都有一个专用的数据库表,但是运行报表程序时不会有性能问题。这怎么可能?答案是TREX,这是一个为只读报告应用程序优化的存储库。也就是说,SAPC4C使用两种不同的存储系统进行交易处理和报表处理,这一点与S/4HANA不同。SAPCloudforCustomer的订单模型在CloudApplicationStudio中对客户和合作伙伴可见。有兴趣的可以自己查一下。总结本文首先详细介绍了SAPCRM系统中订单模型的演进历史和设计原则,以及消费订单模型的API,然后介绍了该模型迁移到S/4HANA时面临的挑战系统,以及作者的项目团队。拟议的迁移和重建计划。最后类比另一个云CRM系统的订单模型,希望对从事客户关系管理领域工作的读者有所帮助。
