天天看点

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

笔者在 SAP 领域工作多年,对 SAP 应用的理解就是:模型以及基于模型的增删改查。

只是同我们大学专业课学习时完成的家庭作业相比,SAP 模型的复杂程度增加了好几个数量级。

和传统的增删改查相比,以订单编排领域为例,SAP订单模型的"增",还需要考虑实际业务流程中各种类型的前置和后序订单,即 SAP 使用的术语文档流(Document Flow)。

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

而"改", 除了订单自身状态的迁移外,还包括订单模型提供的各种可执行逻辑。这些逻辑既包括订单模型本身字段的更改,也可以包括订单与第三方系统的交互。在很多上下文里,我们称这些逻辑为 Action。

如下图右下角所示:

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

既然订单模型复杂度如此之高,那么引入一种精良的能支持企业级订单编排应用的高质量建模方式,就显得至关重要。

随便看些例子,SAP CRM 总共支持多少种标准的订单类型?下图中 BUS2000 开头的就是不同的订单类型,我没有具体数过,但是几十种总是有的。

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

而SAP Cloud for Customer,虽然位于 CRM 命名空间下面的 Business Object 的数量比 SAP CRM 要少一些,但是基本的用于实现销售自动化流程的订单模型仍然一应俱全。

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

我们先来看 SAP CRM 的订单模型。有没有可能用一套模型来描述SAP CRM支持的几十种订单类型呢?有,那就是SAP CRM One Order模型,其自描述的名称就体现了该模型的特色。

下面是 SAP CRM 应用的架构图:

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

其中 One Order框架从架构上讲,位于上图红色区域内,包括数据库表,ABAP结构体以及操作它们的API代码。

笔者刚刚接触 One Order 模型的时候,吃惊地发现,竟然没有一个比较直观的图形化界面,能够显示出这个模型的全貌。不过瑕不掩瑜,对于一个诞生于 20 年前的框架来说,我们不应该用20年后的标准来苛求它。

我们想象一下,不同类型的订单,有什么共同点?无非每种订单都有抬头结构,行项目。有的结构,从业务上说可以同时出现在订单的抬头和行项目,比如参与订单的业务伙伴明细(Involved parties), 组织架构(Organization Unit)等等。有的字段只有行项目才能出现,比如卖出的产品信息(Product, Scheduled Line)。

SAP One Order建模的原理,类似我们小时候玩的积木。

组成One Order模型最小粒度的单元,就是一个个扮演积木作用的结构体,在事务码CRMC_OBJECTS里查看。

下图是这些结构体的列表,如果 SAP 标准的结构体不能满足需要,客户仍然可以自行创建新的结构体。

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

然后我们用搭积木的方式,将业务上具有关联关系的若干结构体组合起来,共同分配给某个订单类型,比如描述服务流程的订单类型 BUS2000116,就由下列这些结构体组成:

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

有了模型之后,剩下的就是实现基于这些模型的增删改查操作,即 ABAP 编程。

One Order API 的代码实现原理,实际上就是设计模式里的模板(Template)模式和观察-发布者模式的结合体。

我们学习模板模式的时候,有一个经典的例子,上帝通过模板模式主宰芸芸众生的生老病死。

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

我们每个人被父母实例化出来之后,只能被动地实现上帝在模板里定义好的四个方法:生,老,病,死,而不能够更改这个模板本身,比如调换这四个方法的顺序。即使是乔布斯,也没有办法给自己添加一个"永生"的方法。听起来很残酷,但这是事实。

那么,One Order框架里,作为One Order应用的上帝,定义了哪些模板方法?

事务码CRMV_EVENT,指定 BUS2000116, 执行:

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

得到下图列表。红色的第一列,就是前文提到的组成One Order模型的积木。蓝色的第二列,是这些积木对发生在自己身上的感兴趣的事件列表。从图中可以看到这些事件名称都是自描述的,比如AFTER_CREATE, BEFORE_CHANGE, BEFORE_DELETE等等。

第三列黑色的ABAP函数,就是这些事件的监听函数。

这些监听函数的后缀 EC 代表 Event Callback.

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

借助上述框架,One Order应用的开发人员的开发工作就变得无比轻松:

(1) 通过搭积木的方式,定义出自己应用需要的One Order模型

(2) 实现模型里需要关注的事件对应的监听函数。

至于这些监听函数什么时候被调用到?应用开发人员完全不用操心。

由此我们能发现,One Order框架的实现,把编程复杂度从应用开发人员身上转移到了框架实现身上。

One Order框架内部的实现比较复杂。通常情况下,One Order框架的使用者只需要了解CRM_ORDER_READ, CRM_ORDER_MAINTAIN等API的用法即可。

One Order的API之一,为消费者提供修改操作的CRM_ORDER_MAINTAIN, 所有SAP标准支持的结构体都作为输入参数之一出现在参数列表里:

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

这种设计方法虽然让参数列表显得有点冗长,但是从另一方面看,也起到了自描述的效果, 确保API的使用者即使不阅读文档,仅凭浏览这些参数本身,就能大概了解该API到底支持One Order哪些数据的修改。

这也符合那份著名的来自 Google 的API设计最佳实践文档里提到的,好的API应该满足的条件之一:易学易用,自描述,不易造成误解。

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

SAP CRM 的部分功能迁移到 SAP S/4HANA后,部分实现做了一些改造,其中就包括One Order的改造。

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

为什么要改造?因为SAP CRM搬到了S/4HANA上,而S/4HANA的一个强大之处,即 S/4HANA在 SAP 历史上第一次实现了 OLTP 和 OLAP 的完美结合,即一套系统的唯一数据源,可以同时满足Transaction事务型应用和Analytics分析报表型应用的需要。

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

而SAP CRM One Order没有改造之前的模型是无法和S/4HANA的上述特性匹配的。

改造之前,每个组成One Order模型最小粒度的结构体,都有自己独立的一张专属数据库表,命名规范一般是CRMD_加上结构体名。

这套底层存储模型如果原封不动地搬到S/4HANA里,在运行报表统计等应用时会出现性能问题——为了取出报表结果,后台需要在很多个结构体的存储表中做各种数据库表的内外连接操作。当参与连接操作的数据库表尺寸增长到一定数量级后,整个应用的性能表现不佳。笔者也参与了性能评测,最后我们决定对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里一样正常运行。

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

而当消费者调用One Order API进行写操作时,在存储于各个结构体对应的缓存中的数据持久化到数据库之前,同样是Adapter负责把这些分散在不同缓存结构中的数据做一个合并,合并后的结构体再写入平坦表。

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

讲完了CRM One Order订单模型的设计,我们再来简单看看SAP Cloud for Customer的订单模型设计。

虽然SAP Cloud for Customer的后台对客户和Partners不可见,但我们仍然可以从合法渠道获得一些其订单模型的设计信息。

从SAP社区上这位SAP员工的​​回复​​,我们得知ESF2和BOPF有很多相似之处,设计理念类似,但ESF2主要用于部署在云端的产品,比如SAP Cloud for Customer上Business Object的开发,而后者主要服务于On Premises解决方案比如S/4HANA。

同之前介绍的SAP CRM One Order框架一样,通过BOPF实现的订单模型,同样由若干个结构体通过搭积木的方式组成,这些结构体如上图红色高亮区域所示,每个结构体也有自己的专属存储数据库表。而SAP CRM One Order里每个结构体的事件监听函数,采取的是 ABAP 传统的面向过程的函数实现,而BOPF则采取了实现指定接口的ABAP类,二者原理相同,只是实现细节有差异。

SAP C4C的订单模型,虽然和SAP CRM传统的One Order模型一样,每个结构体拥有一张专属的数据库表,但是在运行报表程序时并不会出现性能问题,这是怎么做到的?

答案是采用了TREX,一个专为只读报表应用优化过的存储仓库。换句话说,SAP C4C的事务处理和报表处理使用的是两套不同的存储系统,这一点和S/4HANA不同。

一种经典的客户关系管理系统(CRM)订单模型的设计与实现

总结