Hi SCN community,
This blog post is a logical follow-up from this blog post of mine, where I share my design for a region specific implementation framework using ABAP OO and the factory pattern strategy design (what a fancy name!). From the discussion that followed the idea came up to try and make use of the BAdI technology available in SAP, and this is my proposal for a design using this technology!
This proposal is based in a lot of other blog posts, like this one by Andrea Olivieri, this one by Thomas Weiss, and many others. Check my bookmarks, I usually bookmark interesting stuff. These blogs show a more or less static view over BAdIs. What I found out while designing and implementing this approach, is that actually the BAdI technology is amazingly flexible and powerful. Hopefully you’ll share my point of view after reading this post.
Thanks to Debopriyo Mallick and Suhas Saha and their valuable contributions in the comments I have revised my design and, consequently, this blog post. I believe the design is now one step closer to that “idyllic”, probably non-existent, perfect solution. By the way, if you’re looking for an interesting way to activate/deactivate several BAdI implementations at once on different levels you should check this blog post that Suhas shared in the comments.
The premise remains pretty much the same as before, so I’m not going to go into it again in great detail. If you’re working in a system shared internationally, or by different regions, eventually you’ll get user-exits with so many IF and CASE statements, and shared by so many developers, that you will definitely want to have a framework put in place to cope with the specific requirements for each region/country/sales organization/whatever.
In my previous blog, the solution presented doesn’t have any flaws “per se” (not that I can think of), but it does represent a very strict and formal solution. If you want more flexibility and freedom, while retaining the same advantages, I think this BAdI design might be the way to go. It also doesn’t feel like you’re redesigning the wheel 🙂
Let’s get to it! I’m going to showcase this design with the user-exit INCLUDE MV45AFZZ. Anyone that has ever developed anything for SD should probably know this user-exit, so I think it’s the perfect candidate.
Oh, by the way, this is NOT meant to teach you how to create a BAdI. If you are unfamiliar with BAdIs, please take a look into the blog posts I mentioned above.
We start by creating the BAdI definition. The multiple use definition is arguable. For this example I will leave it on, and I guess this would be pretty much standard unless you want to make sure that ONLY ONE implementation of the BAdI is executed. You should keep in mind that when using this option you cannot have parameters defined as exporting/returning, as this would be against the idea behind it. Read in the comments for a more detailed explanation, or press F1 on it 😉 . Developers implementing this (and any multiple use) BAdI should pay attention to the fact that other implementations could also be executed and affect variables they are trying to determine, so proper documentation and descriptions of the BAdIs is not a bad idea. Also, in my first version of this blog post, I had defined one BAdI for the entire MV45AFZZ include, with one method per routine. After Debopriyo pointed out, and rightfully so, that this would mean many unimplemented methods in the BAdI implementations, I decided to revise this and have one BAdI definition per routine. I agree this makes more sense. Now, nabheet madan asked in the comments if it would also be a good idea to define one BAdI per method when applying this design on a standard BAdI. Quite honestly I’m not sure. I guess that would depend on the BAdI… if the BAdI has methods that are related to each other and should all be implemented, I guess one BAdI definition with the same interface as the standard BAdI would make more sense. Otherwise, to avoid having several unimplemented methods, it might not be a bad idea to define one BAdI per method. If you have something to say about this leave a comment below!
Figure 1 – BAdI definition
Now, in my situation, we have different clients per region, so a filter that will come very handy for sure will be the client filter. So I set it up straigh from the start.
Figure 2 – BAdI filter definition
In the discussion in the comments it was also pointed out that a BAdI with many parameters in the interface was a BAdI poorly designed, and this could be a problem in user-exits from include MV45AFZZ, since there is no formally defined interface. To “solve” this problem, I thought of defining one structure for the BAdI’s interface, like this.
Figure 3 – Defining a structure to use in the BAdI’s interface
In this case I’ve already defined internal item table XVBAP, but if you don’t think you’re going to need anything special for now, you can just declare some dummy field, or don’t create this structure at all and create it only when you need it. It will not cause you any pain afterwards, even if you already have BAdI implementations created, as they will simply not use the newly created parameters. So, after revising the design thanks to the discussion in the comments, the BAdI interface now has only one method (in this example I’m showing the method for userexit_move_field_to_vbak).
Figure 4 – BAdI method definition
And that’s it for the BAdI definition (for now)! It wasn’t that difficult. Now all you have to do is call it from your routine.
Figure 5 – Calling the BAdI from your user-exit
Ok so now we implement it! For this implementation, the requirement is specific to client 077. Couldn’t be easier.
Figure 6 – Implementing a BAdI with a filter value
The rest of it is standard, yes? All you have to do is implement the method with the requirement you want. Personally I think the best would be one implementation per requirement. Major advantage is total independence per requirement. You can have one developer per requirement working at the same time on as many requirements as you’d like, no problems with object locking. The disadvantage could be low-awareness between developments. Meaning that a developer implementing a new requirement should take a look at already existing implementations to check if there’s not going to be some conflicting implementations (like a field being overwritten or something).
Now comes the really interesting part for me. What if a new requirement comes along which needs a new parameter? At first glance I would think this would mean a lot of trouble. Going through every implementation and adjusting. Well, not really… or at least I could do it without much trouble (but I guess you’d better not change the parameters already existing, just add new ones)! You change the structure we created earlier and add your new parameters:
Figure 7 – Adjusting the BAdI’s interface
Now we need to populate this variable in the code and that’s it. The interface will update every implementations method signature, and if the parameters aren’t used… well… they’re not used, no problems there.
Figure 8 – Adjusted BAdI call
You can now implement your second requirement easily. So, we now have implemented two BAdIs, filtered to be executed only for client 077. What if now we get a third requirement, which is to be executed globally? A core requirement? It also couldn’t be easier, we implement a filterless BAdI!
Figure 9 – Implementing a filterless BAdI
The rest you already know. What happens in runtime? In runtime, regardless of which client you are on, the filterless BAdI is executed, and if you happen to be in client 077, the previous BAdIs get executed as well. As you should already know, there is no guarantee to which BAdI runs first (unless you implement a sorting mechanism), so make sure one implementation does not rely on a result from another implementation, and also try not to change the same fields, because only one value will prevail, and you have no idea which one. It means you can’t implement some default values for the “core” implementation and hope that the specific implementations will prevail, you have to implement this accordingly.
Actually, as Sougata Chatterjee pointed out in the comments, there is a way to sort BAdIs in the new Enhancement Framework. To do this you implement BAdI “BADI_SORTER”. You can find documentation on this here. Personally, I would try to avoid this. Even though it could be interesting to have a BAdI implementation for a global requirement being executed first, and then the local/specific requirements executed overriding the global requirements, I think this adds too much complexity and could be hard to maintain. I would rather have a requirement either global or specific, and if it’s not global, each region will have to implement it its own way. But now I know (and you know) this option is available.
There’s no such thing as too much flexibility, is there? 😛
Ok, last but not least, let’s say that even if we have successfully separated implementations per client, we’re still getting many conflicts. We want a new filter, per sales organization. That’s also not a problem, we change the BAdI’s definition! This is mostly valid if you are implementing multiple requirements in the same BAdI implementation, but I’ll keep this here for educational purposes.
Figure 10 – Adding a filter to the BAdI’s definition
The existing implementations will not care! They will keep being executed as long as the values for their filters match. How cool is that? But of course, you will have to adjust the BAdI’s call, otherwise you’ll get a nasty runtime error!
Figure 11 – Adjusting the BAdI’s instantiation for the new filter
Done properly, this will allow you to have a nice overview of your enhancements and requirements implemented in your system. You can also use the search feature in the enhancement spot implementation “implemented BAdIs” tab to search for filter value! Which is nice.
Figure 12 – Enhancements overview
Figure 13 – Checking BAdI implementations for a certain filter value combination
That’s it from me! I’ll admit, I think this approach is very elegant and powerful. Some care must be taken to make sure there are no conflicting implementations, but I don’t think there’s any way you can avoid that risk.
Let me know what you think and what you would do differently!
All my best,