SAP成都研究院的一个部门领导让我给他的团队做一个SAP CRM One Order框架的培训,这是我准备的培训内容。
在Jerry之前的文章 基于SAP Kyma的订单编排增强介绍,我表达了自己对SAP应用的理解:模型以及基于模型的增删改查。只是同我们大学专业课学习时完成的家庭作业相比,SAP模型的复杂程度增加了好几个数量级。
和传统的增删改查相比,以订单编排领域为例,SAP订单模型的"增",还需要考虑实际业务流程中各种类型的前置和后序订单,即SAP使用的术语 文档流(Document Flow)。
而"改", 除了订单自身状态的迁移外,还包括订单模型提供的各种可执行逻辑。这些逻辑既包括订单模型本身字段的更改,也可以包括订单与第三方系统的交互。在很多上下文里,我们称这些逻辑为Action。
如下图右下角所示:
既然订单模型复杂度如此之高,那么引入一种精良的能支持企业级订单编排应用的高质量建模方式,就显得至关重要。
随便看些例子,SAP CRM总共支持多少种标准的订单类型?下图中BUS2000开头的就是不同的订单类型,我没有具体数过,但是几十种总是有的。
而SAP Cloud for Customer,虽然位于CRM命名空间下面的Business Object的数量比SAP CRM要少一些,但是基本的用于实现销售自动化流程的订单模型仍然一应俱全。
我们先来看SAP CRM的订单模型。有没有可能用一套模型来描述SAP CRM支持的几十种订单类型呢?有,那就是SAP CRM One Order模型,其自描述的名称就体现了该模型的特色。
Jerry曾经试图搞清楚"One Order"这个称呼,是来自SAP官方,还是仅仅被SAP开发人员内部使用。
用搜索引擎根据关键字One Order搜索,得到的结果几乎全是Jerry写的博客,囧。不过进系统根据ONE ORDER为关键字还是能搜索出大把的代码。
我的文章 Jerry的WebClient UI 42篇原创文章合集里有这张架构图:
其中One Order框架从架构上讲,位于上图红色区域内,包括数据库表,ABAP结构体以及操作它们的API代码。
SAP One Order框架有多成功?搜索引擎输入关键字"SAP CRM ONE ORDER", 第一条搜索结果即Jerry写的一篇博客。其中第一段话就给大家做了详细的阐述:
尽管它如此成功,但当Jerry刚刚接触One Order的时候,吃惊地发现,竟然没有一个比较直观的图形化界面,能够显示出这个模型的全貌。不过瑕不掩瑜,对于一个诞生于20年前的框架来说,我们不应该用20年后的标准来苛求它。
我们想象一下,不同类型的订单,有什么共同点?无非每种订单都有抬头结构,行项目。有的结构,从业务上说可以同时出现在订单的抬头和行项目,比如参与订单的业务伙伴明细(Involved parties), 组织架构(Organization Unit)等等。有的字段只有行项目才能出现,比如卖出的产品信息(Product, Scheduled Line)。
SAP One Order建模的原理,类似我们小时候玩的积木。
组成One Order模型最小粒度的单元,就是一个个扮演积木作用的结构体,在事务码CRMC_OBJECTS里查看。
下图是这些结构体的列表,如果SAP标准的结构体不能满足需要,客户仍然可以自行创建新的结构体。
然后我们用搭积木的方式,将业务上具有关联关系的若干结构体组合起来,共同分配给某个订单类型,比如描述服务流程的订单类型BUS2000116,就由下列这些结构体组成:
有了模型之后,剩下的就是实现基于这些模型的增删改查操作,即ABAP编程。
One Order API的代码实现原理,实际上就是设计模式里的模板(Template)模式和观察-发布者模式的结合体。
我们学习模板模式的时候,有一个经典的例子,上帝通过模板模式主宰芸芸众生的生老病死。
我们每个人被父母实例化出来之后,只能被动地实现上帝在模板里定义好的四个方法:生,老,病,死,而不能够更改这个模板本身,比如调换这四个方法的顺序。即使是乔布斯,也没有办法给自己添加一个"永生"的方法。听起来很残酷,但这是事实。
那么,One Order框架里,作为One Order应用的上帝,定义了哪些模板方法?
事务码CRMV_EVENT,指定BUS2000116, 执行:
得到下图列表。红色的第一列,就是前文提到的组成One Order模型的积木。蓝色的第二列,是这些积木对发生在自己身上的感兴趣的事件列表。从图中可以看到这些事件名称都是自描述的,比如AFTER_CREATE, BEFORE_CHANGE, BEFORE_DELETE等等。
第三列黑色的ABAP函数,就是这些事件的监听函数。
这些监听函数的后缀EC代表Event Callback。
借助上述框架,One Order应用的开发人员的开发工作就变得无比轻松:
1. 通过搭积木的方式,定义出自己应用需要的One Order模型
2. 实现模型里需要关注的事件对应的监听函数。
至于这些监听函数什么时候被调用到?应用开发人员完全不用操心。
由此我们能发现,One Order框架的实现,把编程复杂度从应用开发人员身上转移到了框架实现身上。
One Order框架内部的实现比较复杂,一篇文章的篇幅无法讲述清楚。况且通常情况下,One Order框架的使用者只需要了解CRM_ORDER_READ, CRM_ORDER_MAINTAIN等API的用法即可。
如果想了解更多细节,可以参考我的SAP社区博客:
1. Buffer logic in One Order header extension Read
https://blogs.sap.com/2017/03/22/buffer-logic-in-one-order-header-extension-read/
2. Logic of FILL_OW function module in One Order
https://blogs.sap.com/2017/03/22/logic-of-fill_ow-function-module-in-one-order/
3. Logic of CHANGE_OW function module in One Order
https://blogs.sap.com/2017/03/23/logic-of-change_ow-function-module-in-one-order/
4. Logic of CREATE_OW function module in One Order
https://blogs.sap.com/2017/03/24/logic-of-create_ow-function-module-in-one-order/
5. Logic of SAVE_EC function module in One Order
https://blogs.sap.com/2017/03/23/logic-of-save_ec-function-module-in-one-order/
6. CHANGED_AT, HEAD_CHANGED_AT and CRM_CHANGED_AT in order header table
One Order的API之一,为消费者提供修改操作的CRM_ORDER_MAINTAIN, 所有SAP标准支持的结构体都作为输入参数之一出现在参数列表里:
这种设计方法虽然让参数列表显得有点冗长,但是从另一方面看,也起到了自描述的效果, 确保API的使用者即使不阅读文档,仅凭浏览这些参数本身,就能大概了解该API到底支持One Order哪些数据的修改。
这也符合那份著名的来自Google的API设计最佳实践文档里提到的,好的API应该满足的条件之一:易学易用,自描述,不易造成误解。
在我的另一篇文章 Hello World, S/4HANA for Customer Management 1.0 我曾经提到,SAP CRM的部分功能迁移到SAP S/4HANA后,部分实现做了一些改造,其中就包括One Order的改造。
Jerry是负责One Order改造设计的三位人员之一,详细的改造原理和实现我已经分享到SAP社区了,这里只简述一些核心概念。
为什么要改造?因为SAP CRM搬到了S/4HANA上,而S/4HANA的一个强大之处,在我同事Zhang Sean的文章 S/4HANA业务角色概览之订单到收款篇 里也提到了,那就是S/4HANA在SAP历史上第一次实现了OLTP和OLAP的完美结合,即一套系统的唯一数据源,可以同时满足Transaction事务型应用和Analytics分析报表型应用的需要。
而SAP CRM One Order没有改造之前的模型是无法和S/4HANA的上述特性匹配的。
改造之前,每个组成One Order模型最小粒度的结构体,都有自己独立的一张专属数据库表,命名规范一般是CRMD_加上结构体名。
这套底层存储模型如果原封不动地搬到S/4HANA里,在运行报表统计等应用时会出现性能问题——为了取出报表结果,后台需要在很多个结构体的存储表中做各种数据库表的内外连接操作。当参与连接操作的数据库表尺寸增长到一定数量级后,整个应用的性能表现不佳。Jerry也参与了性能评测,最后我们决定对One Order的底层数据模型做改造。
因为留给我们从调研到改造的原型开发,再到正式开发一共只有八个月的时间,因此我们选择了一种代价最小,对One Order框架改动最小的方式。
首先我们抛弃了之前每个结构体拥有一张专属数据库表的做法,在S/4HANA里,每种订单类型只拥有两张表,一张存储抬头级别的数据,另一张存放行项目数据。之前散落在不同结构体表中的字段,如今统一维护在这两张表里。由于所有的字段都平铺在这两张表里,我们内部形象地称其为平坦表(Flattened Table)。
存储模型大大简化之后,我们基于这两张表再创建CDS view,让上层的报表应用消费。这样改造后简化的模型,能满足S/4HANA中OLAP应用的需求。
针对S/4HANA OLTP应用的改造,用一句话概括,就是我们采用设计模式里的适配器模式(Adapter), 在API与简化后的数据库表之间引入一个微型的中间件,扮演Adapter的角色。
当消费者通过One Order API进行读操作时,中间件负责把存储在简化后的数据表中的数据进行还原,再填充到One Order API上层的缓存中。对后者来说,它对底层存储模型发生的变化毫不知情,因为Adapter封装了底层数据读取的逻辑并做了格式转换,所以One Order API上层不需要做任何改动,也完全能够像在SAP CRM里一样正常运行。
而当消费者调用One Order API进行写操作时,在存储于各个结构体对应的缓存中的数据持久化到数据库之前,同样是Adapter负责把这些分散在不同缓存结构中的数据做一个合并,合并后的结构体再写入平坦表。
讲完了CRM One Order订单模型的设计,我们再来简单看看SAP Cloud for Customer的订单模型设计。
虽然SAP Cloud for Customer的后台对客户和Partners不可见,但我们仍然可以从合法渠道获得一些其订单模型的设计信息。
https://archive.sap.com/discussions/thread/3602400
从SAP社区上这位SAP员工的回复,我们得知ESF2和BOPF有很多相似之处,设计理念类似,但ESF2主要用于部署在云端的产品,比如SAP Cloud for Customer上Business Object的开发,而后者主要服务于On Premises解决方案比如S/4HANA。
因为Jerry不能够把C4C后台ESF2的界面给大家看,所以我选择了展示S/4HANA的Business Object开发框架BOPF,因为前面说了,二者很多方面都非常相似。
同之前介绍的SAP CRM One Order框架一样,通过BOPF实现的订单模型,同样由若干个结构体通过搭积木的方式组成,这些结构体如上图红色高亮区域所示,每个结构体也有自己的专属存储数据库表。而SAP CRM One Order里每个结构体的事件监听函数,采取的是ABAP传统的面向过程的函数实现,而BOPF则采取了实现指定接口的ABAP类,二者原理相同,只是实现细节有差异。
SAP C4C的订单模型,虽然和SAP CRM传统的One Order模型一样,每个结构体拥有一张专属的数据库表,但是在运行报表程序时并不会出现性能问题,这是怎么做到的?
答案是采用了TREX,一个专为只读报表应用优化过的存储仓库。换句话说,SAP C4C的事务处理和报表处理使用的是两套不同的存储系统,这一点和S/4HANA不同。
SAP Cloud for Customer的订单模型,在Cloud Application Studio里对客户和Partners是可见的,大家感兴趣的可以自行去查看。
希望这篇文章能让大家对SAP CRM两款产品中的订单模型设计有最基础的认识,感谢阅读。
相关阅读
要获取更多Jerry的原创文章,请关注公众号"汪子熙":
来源:oschina
链接:https://my.oschina.net/u/3771578/blog/3006965