BOL or Function Modules – Which API to use in CRM Development?
- Introduction to the function module APIs and the BOL
- Performance Comparison Framework
- Scenario 1: Business Partner Creation
- Scenario 2: One Order Creation
If you have been following the CRM space on SCN a little bit you might have noticed that there are quite often questions on how to create of update data using a certain function module or BAPI. Whenever these kinds of questions are asked I usually answer that in my opinion it is better to use the BOL to update or create data in CRM instead of the function modules. Also quite regularly Stephen Johannes and I disagree whether this is the right approach (cf. for example this discussion Use CRM_ORDER_MAINTAIN FM to change CREATED_BY field into an order).
Basically Stephen argues (@Stephen: please correct me if I paraphrase your opinion incorrectly) that the function module based APIs have been around long before the BOL and that the BOL uses these function modules underneath. Consequently, the function modules should offer better performance than the BOL. Furthermore, as the function modules is the underlying API it is better to learn and use them in the first place. In contrast I usually argue that in my opinion the BOL is the cleaner API and that clean and succinct code should be a higher priority than performance in almost all scenarios.
After the last discussion I had with Stephen via private messages I decided to write a blog which tries to compare the two approaches side by side. Using two typical scenarios, the creation of business partner data and the creation of orders, this blog shows the difference in the resulting program code when using function modules in contrast to BOL. Furthermore, the performance of the two approaches in the context of a mass creation of the respective objects is compared. As a result of this blog I hope to provide a suitable basis for the decision between the different API options for different usage scenarios.
The blog is organized as follows. Firstly, a brief introduction to the function modules based APIs and the BOL, mainly providing references to existing documents, is given. Next, the mini-framework used to implement the different scenarios for performance comparison is introduced. The main part of the blog is the comparison of the business partner and order creation using function modules and BOL. The blog closes by providing a short summary of the results.
Introduction to the function module APIs and the BOL
In the context of these blog two different function module based APIs are used. To create business partner data the business partner BAPIs are used. According to the definition in the SAP Library a BAPI is “A Business Application Programming Interface (BAPI) is a precisely defined interface providing access to processes and data in business application systems such as R/3.” While a BAPI is supposed to be a stable interface to some defined business process or data it is implemented using plain function modules. As an example consider the BAPI method ChangeAddress of the business object BusinessPartner. It is implemented by the function module BAPI_BUPA_ADDRESS_CHANGE (see screenshot of transaction BAPI below). Therefore, this blog will refer to function module based APIs to subsume BAPIs as well as function modules not available via BAPIs.
The nice feature of the BAPIs in contrast to some other function modules is that most of the BAPI function modules contain extensive documentation. This is quite helpful in understanding how to use them. Note also, that the BAPI are usually implemented using some internal function module. For example, the function module BAPI_BUPA_CREATE_FROM_DATA is implemented using the function module BUPA_CREATE_FROM_DATA.
The second function module based API used in this blog is CRM_ORDER_MAINTAIN. It is the function module to create and updates orders in SAP CRM. Unfortunately, it is not released for customer usage. But that usually doesn’t stop customer form using function modules 😉 . In contrast to the business partner BAPIs there is also no documentation available for this function module. This is most likely one of the reasons why there is such a large number of discussions on the topic in SCN. For an example of how to use CRM_ORDER_MAINTAIN see CREATE using CRM_ORDER_MAINTAIN simple example (for those new to CRM and/or ABAP) or the code examples below.
In contrast to the function module APIs the BOL is an object oriented programming and data model that “stores the data of the business objects together with a defined set of attributes and relationships during runtime of a CRM WebClient UI session.” (Business Object Layer – Web Services – SAP Library). In the BOL the relations between business objects are explicitly modelled. As an example consider the screen shot of the BP component in transaction GENIL_MODEL_BROWSER below. It shows the relation between a business partner header object and its addresses.
The BOL programming model allows to work with all business objects in CRM using a unified API. A nice introduction if given in the following blog: http://www.abaplog.com/Icerik/27/introduction-to-bol-programming. For further details regarding the BOL programming model see How To Guide Business Object Layer Programming. The implementation of the BOL is based on existing APIs for the different business objects. The business partner BOL objects are implemented using the same function modules used to implement the business partner BAPIs. The same is true for the one order BOL objects. These are implemented using CRM_ORDER_MAINTAIN.
Note that for the purpose of this blog I omitted a discussion of the XIF interface (cf. e.g. External Data Loads for CRM 4.0 using XIF adapter | SCN). The reason is that the XIF interface is used to create BDocs CRM that are processes asynchronously. Therefore a direct comparison of the XIF interface and the other approaches would have been quite difficult.
Performance Comparison Framework
In order to compare the performance of the different programming model I created a simple performance testing framework (see UML diagram below, getting this created in SE80 would deserve a blog on it own 😥 ). It basically consists of two parts. A random test data generator consisting of the interface ZCD_IF_PERF_TEST_DATA_GEN and the two implementation ZCD_CL_PERFORMANCE_GEN_1O_DATA and ZCD_CL_PERFORMANCE_GEN_BP_DATA. This is used to generate random test data for the different performance tests. The second part consists of the interface ZCD_IF_PERFORMANCE_TEST and its implementations ZCD_CL_PERF_CREATE_BP_BAPI, ZCD_CL_PERF_CREATE_BP_BOL, ZCD_CL_PERF_CREATE_ORDER_BAPI and ZCD_CL_PERF_CREATE_ORDER_BOL. These are the implementation of the different test scenarios using the functions modules and the BOL respectively.
In order to execute the different performance test I also implemented a small report that allows to select the different scenarios, the number of objects to create and whether or not to execute a commit after creation of an object.
The main part of this report is shown in the following code snippet. In order to ensure that the performance measurement are not falsified by the need for compilation or uncached data a warm up is performed prior to the actual performance measurement (lines 64-68, full source code available at #10751306 – Pastie). Note, that in this warm up code single commits are executed to also ensure that the commit code and data is cached.
Scenario 1: Business Partner Creation
The first scenario to compare the performance of function module based APIs and the BOL is business partner creation. The scenario for this test is the creation of a business parter of type person with an standard address and an email as address independent communication data.
The implementation of this scenario using function module BAPI_BUPA_CREATE_FROM_DATA is given in the following code snippet (full source code available at #10751310 – Pastie).
The same scenario implemented using the BOL is shown in the following snippet (#10751311 – Pastie)
Both implementations are similar in length. In my opinion the BOL implementation is a little cleaner as different “parts” (e.g. address, email) of the business partner are nicely separated into different objects.
In order to test this scenario the class ZCD_CL_PERFORMANCE_GEN_BP_DATA is implemented to return test data in the structure given in the below. First and last name consist of a fixed prefix concatenated with a timestamp. A random street and house number is selected in Aachen as the default address. Finally the email address is simply the concatenation of the first an last name followed by a constant.
In order to compare the performance of the two APIs the following performance tests where performed:
- creation of 50 business partners without a commit
- creation of 50 business partners with a bulk commit after the creation.
Creation of 50 business partners without a commit
The results of executing the performance test without a commit a given in the following screen shots. The first screen shot shows the performance of the business partner creation using the using function module BAPI_BUPA_CREATE_FROM_DATA, the second using the BOL.The first call to ZCD_CL_PERF_CREATE_BP_BOL->ZCD_IF_PERFORMANCE_TEST~RUN in both traces is the creation of 10 business partners to warm up before the performance test, the second call is the actual performance test creating 50 business partners.
The surprising result of these test is that the BOL API is more the 5 times as fast (1.2s vs. 5.3s) as the function module based API when it comes to just creating objects in memory. I haven’t investigated the underlying reason for this behaviour yet.
Creation of 50 business partners with a bulk commit after the creation.
The results of executing the performance test using a bulk commit is given in the following screen shots. Again, the first screen shot shows the performance of BAPI_BUPA_CREATE_FROM_DATA, the second of the BOL. Like in the traces before the first call to ZCD_CL_PERF_CREATE_BP_BOL->ZCD_IF_PERFORMANCE_TEST~RUN in both traces is the creation of 10 business partners to warm up before the performance test, the second call is the actual performance test.
In this case the result are quite interesting. On first sight the BAPI implementation provides better performance then the BOL (10.9s vs. 12.4s). However, when viewed in detail the reason for this is that the war up phase is much faster in the BAPI compared to the BOL (2.1s vs 6.5s). In contrast the bulk creation of the 50 business partners is faster in the BOL (5.7s vs 8.6s). This again is a quite surprising result. There reason is, that in the warm up phase a single commit strategy was used (cf. previous section). It seems to be the case that the BAPI_BUPA_CREATE_FROM_DATA works very well in the single commit scenario. In contrast to that the BOL performs much better in the bulk commit scenario.
Scenario 2: One Order Creation
The second scenario to compare the performance of function module based APIs and the BOL is the creation of an order. The scenario for this test is the creation of an activity for a given business parter. For this activity the description, category code group and code are set.
An excerpt of the implementation of this scenario using the CRM_ORDER_MAINTAIN function module is give in the following code snippet. The actual implementation is about 150 lines of code and available at #10751374 – Pastie.
In contrast to that the implementation of the same scenation using the BOL is very concise (available at #10751380 – Pastie).
It is obvious from these code snippets that the BOL API for creating orders in CRM is much easier to use. First, the code is much more readable. Only about 30 lines of code are required to create an activity using the BOL compare to 150 using the function module API. Second, the objects are again nicely structured (partner set, activity extension). Third, the is no need to fill input field tables or use magic constants.
In order to test this scenario the class ZCD_CL_PERFORMANCE_GEN_1O_DATA is implemented to return test data in the structure given in the below. The partner and process type are constants. The description consists of a fixed prefix concatenated with a timestamp. The category and code group are again constants. Finally, the code is selected at random from the code group.
|11172404||ZA01||Test Activity 20160307203338.8610000815||202||ZK000007||Z005|
|11172404||ZA01||Test Activity 20160307203338.8610000136||202||ZK000007||Z001|
In order to compare the performance of the two APIs the following performance tests where performed:
- creation of 50 orders without a commit
- creation of 50 orders with a bulk commit after the creation.
Creation of 50 orders without a commit
The results of executing the performance test without a commit a given in the following screen shots. The first screen shot shows the performance of the order creation using the using function module CRM_ORDER_MAINTAIN, the second using the BOL. As in the business partner scenario the first call to ZCD_CL_PERF_CREATE_BP_BOL->ZCD_IF_PERFORMANCE_TEST~RUN in both traces is the warm up before the performance test, the second call is the actual performance test. Again the result of this test is that the BOL API is faster hen function module based API when it comes to just creating objects in memory. However, with 14.6s vs. 13.6s the difference is not as big as in the business partner case.
Creation of 50 orders with a bulk commit
The results of executing the performance test using a bulk commit is given in the following screen shots. Again, the first screen shot shows the performance of CRM_ORDER_MAINTAIN, the second of the BOL. Also the first call to ZCD_CL_PERF_CREATE_BP_BOL->ZCD_IF_PERFORMANCE_TEST~RUN in both traces is the warm up before the performance test, the second call is the actual performance test.
The creation of 50 order using bulk commit is the first scenario in which the function module API (i.e. CRM_ORDER_MAINTAIN)outperforms the BOL API (13.1 vs. 14.5s).
So what is the conclusion of these test? First, it is save to say the the general assumption of the BOL being slower then the function module based APIs is wrong. In contrast, there are scenarios in which the BOL APIs significantly outperform the corresponding function module APIs. Second, in the scenarios in which the BOL APIs are slower, the margin is not very big. In contrast, the complexity of the code is in the case of CRM_ORDER_MAINTAIN much higher. Which also accounts to lower readability and reduce maintainability. So starting with the simpler and cleaner programming model of BOL should always be the first choice. Starting with the function module APIs is in my opinion a clear case of premature optimization. Or as Donald Knuth put it: premature optimization is the root of all evil (or at least most of it) in programming (https://en.wikiquote.org/wiki/Donald_Knuth). Third, if in the case of a data migration hundreds of thousands of objects need to be created using the XIF interface might be the better approach. The XIF interface creates BDocs. Using the standard CRM middleware features those can be processed in parallel, reprocessed and so on.
In summary, there are only very view scenarios in which using the function module based APIs make sense.