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.
Luís Pérez Grau just pointed me to the following blog by Jerry Wang:
Webclient UI vs ABAP webdynpro: performance loss in BOL / Genil Layer discussion
It discusses a quite similar topic. I would recommend anyone interesting in BOL performance to also read this blog.
Excellent job Christian, It's true that we already have one analisys, but having another source is allways good, even better if we consider that you weren't infuencied by the first one and both arrived to very similar conclusions.
I agree in most of the points and I would like to add my 2 cents:
- Huge load of data? XIF interface even better if you use SXDA and LSMW to take advantage of the parallel processing
- WebUI development? Of course BOL programming
- BADI enhancements? If is a badi placed on the WebClient, BOL, if is on the API level use the corresponding API, XIF only would be justified if you are using parallel updates (TRFC or QRFC)
- Actions: I would use the APIs maybe XIF can be justified but as far as I know woulnd't use BOL
- Developments from scratch (none WebUI stuff)? BOL is a good option for the reasons alread state in the blog, I wouldn't use BOL at least in one scenario, pricing conditions which is a pain as has it's own interpretation of how model de APIs should fit in the BOL framework.
you are right. It is always important to use the API suitable to the layer in which you are working. It's definitely no good idea to e.g. implement a BADI in CRM_ORDER_MAINTAIN using the BOL. Thanks for adding the clarification.
The only problem with the business partner comparison is that you are not using the SAP function module code sequence that SAP uses in implementing the BOL 🙂 . That could explain the differences. The other issue is your sample size is too small. N=50 is not large enough to prove or disprove exponential growth issues via actual testing. I think if you set N at around 100K updates or more(perhaps even 1,000,000), we see the small amount of overhead that BOL introduces.
At the end of the day we know the BOL calls all the underlying function modules. SAP has optimized it heavily so that overhead of the BOL does not add any unnecessary overhead. The good news here is that if you follow either route with proper coding it should be fine.
Yep you really need to learn what the function modules that the BOL are using, if you want to understand your SAP CRM system deeply. BOL is a fine place to start learning and you can code against, but a deeper knowledge comes from learning the "timeless api" that has served as the foundation for SAP CRM for almost the last 15 years.
That being said, a very nice write up and would love see your high-volume results after correcting your BP FM code 🙂 . Now I must go back outside the SAP universe again where I have been working.
I agree that the results with very high volume of data might be different. I guess I'll give it a try with 1000 BPs and orders and see what the results are. Will post them here of cause.
What I don't get is your comment about the wrong sequence of function module class to create a business partner. I'm only using one function module, BAPI_BUPA_CREATE_FROM_DATA, to create a BP. How could I call this in the wrong order? Or are you suggesting not to use the BAPIs but the underlying function modules?
Yes you need to code the function module identical to how SAP wrote the code beneath the BOL layer to fully understand how much, if any overhead the BOL abstraction adds to your issue.
In addition the BAPI_BUPA_CREATE_FROM_DATA although being a public API is "heavy" which means it's not fully optimized. I also believe that SAP might be in the BOL layer calling some additional function modules which suppress validation calls during the update process. I know these exist, because I had to adjust my load programs to turn off certain validations in order to improve insert performance.
I do believe once you switch the from the BAPI to identical code that SAP uses and then switch to a larger dataset it will produce a more accurate picture of the performance results.
for a massive parallel in use mobile scenario (target scenario is about 100.000 users in parallel, not joking) we decided for building the customer APIs on top of the SAP CRM system using mainly the basic function modules and customer APIs which aren't covered by BOL. I do not have any load information yet, but we abstracted everything using interfaces - so it would be easy to replace an implementation using FM API with a BOL access. Mainly one of our reasons to abstract everything using interfaces was also to be flexible in order to be able to test different approaches.
In general I would also agree that there is no general "this or that direction", my own experience says if there are a lot homegrown "APIs" available (like for Pricing, determinations and validations) then I would tend to go for FM API again.
If just pure standard functionality is exposed then have a look at the delivered OData implementations 🙂
Great blog, by the way (this was want I wanted to say 😉 )
Sounds like an interesting scenario and an interesting approach.
You could still build a BOL implementation for this functionality if you decided to use the BOL approach in the future.
I am not able to access the code from the link #10751310 – Pastie. Can you please check.
I also can't access the pastie.org web site. It seems the service has unfortunately been discontinued. However, using the screenshots in the post you should be able to build the test functionality yourself.
Let me know if you have any further questions.
Great Blog Christian! The efforts you have put on proving this is fabulous!
However, I am in dilemma in using BOL APIs Vs Select queries on DB table for one of the custom web service to display invoice list (not details though) for a dealer.
Do you prefer using BOL APIs or DB table. I didn't find any major performance issues when using direct hits to DB tables. Any suggestions would be appreciated. Thanks!
I'm glad you found the information in the blog useful.
Regarding you questions. I would always recommend using a API to fetch data. IMHO there are only very few scenarios where using SELECT queries to directly read data form DB tables is a good choice. The main reason for this is the additional abstraction layer. With a SELECT you tie your program very closely to the current DB design. If this design changes you will have to change your program. I contrast, if you use a public API (like BOL) chances are good that the API is not changed an your program continues to work without any changes. Also the scenario mentioned above is not unlikely. If you would like to run the same program in a S/4HANA system with the CRM add on it is very likely that the DB tables differ.
Of cause there are a lot of additional benefit of choosing the API approach. One major advantage is the possibility to unit test your development.
In short: use the BOL APIs ?
Thanks for the reply Christian!
I understand there are benefits of using APIs. But I am referring a scenario where I need just few columns/fields from the table and not all the data which we generally fetch using BOL.
Also, if I look from Change management/ future implementation perspective, in S4 CRM whatever information I have till date, even the APIs would be changed when I refer to various blogs on SCN - for eg -https://blogs.sap.com/2018/03/02/crm-one-order-model-redesign-in-s4hana-for-customer-management-1.0-part-1/
Having said this, where I need some help is - for the scenario, where we need to fetch only few fields and from few DB tables, Is it worth using select queries or still use BOL...Sorry for countering you again (I am just trying to discuss) , but still seek some acknowledgement regarding this topic.
i would always go for the BOL API first. Don‘t do a premature optimization. Once you application runs measure the performance. If you really have performance issues measure and optimize where necessary.
Don’t go for the direct SELECT just because you expect possible performance issues in the future.
I like this excellent blog! But it seems the links on Pastie don't work anymore. I would like to read your source code and would you find a place, say github, to host your source code to us again.
Thanks a lot!
it seems like the website http://pastie.org doesn't exist anymore. Unfortunately, I wrote the code on the system of my former employer to which i have no access anymore.
However, most of the code is still visible in the screenshots. The only thins missing is the report the was used as a test harness. This report was pretty simply. It only enabled to enter the number of BP to be created and the selection of the class to run for the creation.
Furthermore, there is now a better alternative to create test data: the abapFaker (https://github.com/se38/abapFaker) by Uwe Fetzer
Thanks for your reply! I like your article and I think the layer really matters when considering which API to use in the development. For a higher level, like UI or layer above CRM object layer, it shouldn't use 1Order function modules.