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.
Regards
Ron.
Ron,
A very nice read!.
In case of Standard Objects (say, SAP CRM Sales Order) with little bit ( understated!) customization - how can we resist the temptation of wrapping the existing fms/methods into RFC and flying out with an RFC Import?.
note: the question is deliberately dumb 🙂
Many thanks, Suresh
Hi Suresh,
I'm not entirely sure I understand the question - are you saying you'd still be trying to use the wizard or going with the manual wrapping?
The CRM One Order concept is a good case in point anyway. This has good and bad points.
Before I go into any discussion about using FM's for this object, be aware that there are GENIL integration tools as well. I'm not a CRM Web guy so I'm not used to working with GENIL and haven't tried it out. That tool may be preferable or it may be just as much of a pain to use.
Back to One Order and RFC mapping, here is something for you to consider.
I've just looked at the Fiori service for My Leads. This conceptually a simple service, but being SAP CRM, is technically overcomplex 😉
You will find the same kind of architecture all through Fiori services, albeit fairly non-standard. I've not seen one yet that is obviously an RFC import.
So, if RFC importing is "best practice" or a simple solution for all service scenarios, why haven't SAP used it to expedite their delivery of Fiori Wave 2?
All that being said, you could use One Order functions as a service base, but they would be better wrapped within a model design provided from outside than trying to extrapolate a model from the RFC interface.
Ron.
Ron,
Thanks! ; I did not even have the slightest intention of dragging this conversation into GenIL 😀
I guess, the question was partly out-of-context in the sense, it was aimed at Inside-Out given the fact that your blogs point us towards Outside-In where possible.
In short : How do we balance?.
I did get your answer in the above reply
"So, if RFC importing is "best practice" or a simple solution for all service scenarios, why haven't SAP used it to expedite their delivery of Fiori Wave 2?"
Many thanks, Suresh
Ron Sargeant Thanks for sharing .
Regards
Virinchy
Thank you for pushing OData implementation in the right direction. RFC should not be the driver for an OData model.
Should we also consider the deployment of Gateway system? (Hub vs. Embedded scenario)? What is your recommendation when we have a gateway Hub system connected to ERP back end?
Best regards
Manu
Hi Manu,
The modelling and code is identical in both cases, just located in different aspects of the landscape. This was done on a CRM sandbox, our live system has separate GW server.
Regards
Ron.
Hi Ron,
Thanks for the reply.
Does that mean the Geatway objects (design time and run time) will be developed and deployed on hub system which will make BAPI/RFC calls to the ERP over RFC Destinations?
Regards
Manu
Manu, that's not how it works.
The most common scenario is for the runtime artifacts and model to be defined and implemented in the backend. There is very little development in "Gateway" when it's a hub. It merely exposes services.
In this scenario, your function is never connected to in a direct RFC call, because all BEP handling is done through a higher level generic RFC interface.
See IMPORTANT POINT 1 !!! (Another reason why I dislike the "RFC" moniker)
You can have services implemented on the GW hub but these are generally related to service aggregation, e.g. unified inbox.
Regards
Ron.
Hi Ron,
Totally agree with your points regarding Outside-in-Modelling.
But I have the following doubts regarding the same.
1 We know the customer requirements keep on changing. Now they ask you to add relationship for eg say Business Partner to Contract accounts . So you go to Odata modeler in Eclipse and add that entity . Now Can I do a delta import of that EDMX file to the GW builder without affecting the service implementations that I have already done.
2 Why do you need Industry system as an entity which just provides Key value pairs ? Why cant we simply model it as a Function Import .Because I have worked in a real time project which had 30-40 of this key value pairs (config table) and modeling it as entity types is very time consuming.?
Hi Arshad,
Regarding point 1, it depends on the context.
a) If the service is published and in use, this should be a new version. You would therefore be building a different service, regardless of any similarities. You have various options to do this, which depend on how you want to create the new version (which may not involve SEGW once you have the base code in place).
b) I don't think you should be in the position of having drastic changes applied to your model in the initial design phase. Sure, you may have several drafts but until you have it pinned down you should not commit to data provision build as that might be wasteful.
Regarding point 2, just because an "entity" looks like a key/value pair does not mean it should be published as one. What if I need to add more properties to this entity? Key/value pair is then not an option, I have to discard what was built and create an entity. In the long term, proper entity modelling is more robust.
The Industry is a proper entity in my view - it is not just an attribute of another entity.
You could use a function import but I avoid these if CRUDQ can be built and it can in this case.
Finally key/value pairing is a lazy data transport mechanism. It has no robust typing and doesn't communicate the data semantics very well, if at all. As part of a discoverable protocol like OData it's not good practice to use such methods in my opinion.
Overall, neither of these points is a negative to outside-in modelling, in fact I think they would be even harder to manage using inside-out; which cannot, by its nature, provide adequate pace-layering for data services.
Regards
Ron.