Outside-in modelling – a practical guide (RFC Rehab 1)
Following on from this blog, Outside-in modelling (or “rehab” for RFC addicts), which urges you all to keep away from RFC based modelling (or at least be aware of your sins), here is the first part of the promised practical demonstration of the outside-in approach.
The “Customer Industry” scenario and service
Within CRM, a customer (represented by “business partner”) can have an industry segment assigned. Each partner can have more than one industry assigned. This tends to be mostly relevant for “Organisation” partner types.
I wish to create a service whereby I can query, add and delete the industry assignments of the customer.
The service only needs query and read operations for the customer entity, which is straightforward if I only require basic properties. It needs to provide a collection of some customers; I don’t want all customers as some of them may not be in the country that I am wishing to analyse. This will require a customer search function that can filter by country.
The industry information cannot be reliably modelled in the flat entity representation of the customer.
If 0:1 industries were the norm, the industry information could simply be a property of the customer entity. However, the cardinality is 0:n; the industry needs to be a separate entity that has a relation to the customer, e.g. “/Customers(‘1’)/Industries”.
I will also need a feed containing all industry keys that are available for assignment to customers so that I know which keys are valid for POSTing when I add an industry.
The landscape involves a BEP-enabled CRM instance with embedded GW components.
On the outside, looking in…
I start by drawing up an abstract data model in the Eclipse OData Modeler. I’m not a CRM functional consultant so I have to do a bit of code diving to find out the pertinent parts. The key thing here to note is, if I had the business knowledge and wasn’t a developer, I could do this job without any reference to function modules. The technical details are not important if you know that the backend system can do what you want in some way. Since the partner maintenance tools do exactly this, it’s a good bet we can pull it off once it goes to a developer.
N.B. during the abstract design, I actually went through two major iterations in a short amount of time. My final service is a lot leaner than the one I started with as a first draft – this step is very important as it can significantly improve the build time!
There are three business partner types in CRM. I only want my service to relate to “Organisation” type (B2B), so I call the customer data “Organisation”, which will hopefully clarify that it won’t ever be “Consumer” data (B2C).
The IndustrySystem entity is not related to the Organisation – this is the master data set of available industries created in CRM. When an Industry entry is added to ‘Organisations(x)/Industries’, the industry key must be taken from that master data collection.
I now export this model to an OData ‘.xml’ and import it into the Service Builder in the CRM backend.
I tweak some of the settings so that the metadata reports the service usage more accurately – it won’t actually affect way the service provides data but I will design my provisioning to match this.
I generate the service and activate it. Note that the proposed names have been shortened by me – I certainly don’t need two ‘Z’s in each class and service name.
Now I have the service metadata done, within 2 hours (including blog authoring time). After 2 hours of struggling with the RFC importer on my aborted blog, I was nowhere near as far as this.
Moving on to the data provisioning, the first thing I need to do is formulate the query – as stated above, this is going to be done “by country”.
When I started my inside-out design experiment, I picked the function BAPI_BUPA_SEARCH as the template function. It can do what I want – find business partners by country – but trying to formulate a sensible service model by RFC import was a nightmare!
I’ll stick with the same function but wrap it in my own code. All I need as an input is a country code, which is supplied as a filter, e.g. Organisations?$filter=Country eq ‘GB’. One behaviour of this function that I don’t want to surface in my service is the fact that the partner type won’t be differentiated. It would return all “organisation”, “consumer” and “contact” type partners within the filtered country. I could add another filter criterion but that might not be as understandable as using an entityset name to describe what it contains; thus, I can use a fully descriptive entityset called Organisations and make the logic carry out the necessary alterations to only return organisations.
Let’s look at ORGANISATIONS_GET_ENTITYSET in the DPC_EXT and how it is redefined to fit the request.
Plus the subroutines:
This method extracts the expected filter for country. If the request does not contain one, it raises a runtime business exception.
This method is where the so-called “RFC” is buried. I say “so-called” because…
IMPORTANT POINT ONE
…the function used does not have to be RFC enabled.
Referring back to the end of ORGANISATIONS_GET_ENTITYSET, the data obtained is looped and converted into entity format…
IMPORTANT POINT TWO
The data going out is made to conform to the model that has been established without rigid reference to the internal data source. Additionally, not all of the data required by the model was obtained with BAPI_BUPA_SEARCH, the Name value was post-populated by getting it from BUT000. If this had been an RFC inside-out design, there would not have been a Name property at all.
IMPORTANT POINT THREE
Extrapolating from point 2, note some things about this service.
- It does not use any ABAP DDIC structure base.
- It certainly does not use any RFC interface signatures.
- Despite the lack of “SAP integration”, we have a model and a service that works.
The reason it works? It’s an OData model and a true OData model should be agnostic of its data provision details. As long as it obtains data that can be mapped to Edm types, it’s happy.
IMPORTANT POINT FOUR
OData is not exclusive to SAP. It’s an open protocol so it does not make sense to tightly couple any of it to SAP technology. Creating models from RFC imports is very wrong if only for this fundamental reason.
At the risk of repeating myself, I tried to do all of the above with RFC importing and it was (a) not a nice experience and (b) a failure. Considering that this is only the upper level of the service, it would have been foolhardy to continue in that direction when I know it can be done another way.
I’m still convinced that the final model would not have been possible via RFC wizard without a lot of additional code effort, which we can do anyway without the obfuscation that the wizard puts in our way!
If anyone is not convinced and wishes to produce the inside-out equivalent of the above and can honestly state they found it quick and simple, please let us know. I’ll be expecting you to do the same for part two ;).
This concludes part one, in part two I’ll move onto the navigation to industry, which is also based on FM data provision.
Congratulations, you have completed step one of RFC Rehab.