Probleme bei BEx Customer Exit Variablen

Probleme bei BEx Customer Exit Variablen

Veröffentlicht am 5. Januar 2021 von

Jörg Brandeis

| ABAP | BEx Query Variables |

Der Quellcode der Implementierung des BAdIs für Customer Exit Variablen ist für qualitätsbewusste Entwickler eine Katastrophe, weil gegen mehrere Clean Code Prinzipien verstoßen wird. Das BAdI Konzept im SAP BW/4HANA macht es einem hier nicht leicht.

Dieser Artikel ist Teil einer Serie zu dem Thema "Ein Entwurfsmuster zur Implementierung von Customer Exit Variablen".

  • zunächst möchte ich in diesem Artikel analysieren, wo die Probleme herkommen
  • Dann mache ich Vorschläge, wie man ein sauberes Entwurfsmuster aussehen kann
  • Zuletzt möchte ich dann auch konkretes Entwurfsmuster implementieren

Wo ist das Problem?

Tatsächlich gibt es im Bereich der BEx Customer Exit Variablen Implementierungen mehrere Probleme, die sich gegenseitig verstärken. Ich habe selten ein gewachsenes BW-System gesehen, bei dem in diesem Bereich alles sauber, übersichtlich und gut strukturiert implementiert war. Meistens herrscht hier das blanke Chaos. Selbst dort, wo ein "Framework" für das Auslagern von Code implementiert wurde, gibt es viel Wildwuchs.

Die Ursachen sind vielfältig, und sie fangen beim Exit bzw. BAdI-Konzept der SAP an....

Die BAdI-Struktur

Die Granularität der Klassen

Für die Implementierung der Customer Exit Variablen wird der BAdI RSROA_VARIABLES_EXIT_BADI genutzt. Dieser hat einen Filter nach InfoObject. D.h. wir können uns pro InfoObject entscheiden, welche Klasse das jetzt implementieren soll. Das ist aber leider die falsche Granularität für das Problem, das wir lösen wollen. Denn ein InfoObject hat ja häufig mehrere Variablen und die haben (meist) unterschiedliche Logiken.

Hier hilft man sich häufig mit einer zentralen Implementierung des BAdIs zusammen mit einem eigenen Framework, z.B. mit Funktionsbausteinen oder Methoden, die dann auf Grund von Namenskonventionen aufgerufen werden.

Die Granularität der Klassen sollten aber weder InfoObject noch Variable sein, sondern die unterschiedlichen Logiken.

Die Granularität der Methoden

Das zweite Problem mit dem BAdI ist die eine Methode PROCESS, die das Interface IF_RSROA_VARIABLES_EXIT_BADI vorgibt. Denn diese eine Methode wird für jeden der unterschiedlichen Verarbeitungszeitpunkte (Parameter I_STEP) für unterschiedliche Aufgaben mit einer Vielzahl von Parametern aufgerufen . Damit widerspricht das Konzept mehren Clean Code Prinzipien

"Functions should do one thing. They should do it well. They schoud do it only. "

"The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification -- and then shouldn't be used anyway."

Robert C. Martin, Clean Code

Aber was sind die Probleme damit?

  • Wir brauchen mehrere Fallunterscheidungen CASE in unterschiedlichen Dimensionen:
    • InfoObject - I_VNAM
    • Verarbeitungszeitpunkt - I_STEP
  • Wir haben ein zentrales Stück Code
    • wenn ich einen Aspekt anpasse, dann muss ich alle anderen Aspekte auch testen. Nur dann kann ich sicher sein, dass nichts kaputt gemacht wurde.
    • Alles wird gemeinsam transportiert und nicht nur der tatsächlich angepasste Code.
  • Methoden mit umfangreichen CASEs und vielen Parametern sind schwer sorgfältig zu testen. UnitTests mit guter Abdeckung lassen sich am leichtesten für Code mit wenig Verzweigungen erstellen.

Die richtige Granularität der Methoden sind die unterschiedlichen Verarbeitungszeitpunkte I_STEP. Also eine Methode für jeden Zeitpunkt.

Viele Köche

Diesen Abschnitt möchte ich mit einem Zitat aus einem anderen Artikel zu diesem Thema beginnen, dem ich mich vollumfänglich anschließen kann:

"Der Customer-Exit für BEx Variablen war schon immer ein guter Kandidat für unstrukturiertes, umfangreiches und historisch (häufig auch hysterisch) gewachsenes Coding. Das liegt zum einen daran, dass der Exit (Include ZXRSRU01) in der Regel von viele unterschiedliche Entwickler (häufig auch kurzfristig eingekaufte externe Berater) mit unterschiedlichen Programmieransichten (Funktional oder Objektorientier) bearbeitet wird. Nicht selten bringen diese ihre Verfahren und Ansätze zur Strukturierung des Customer-Exits (Aufruf dynamischer Funktionsbausteine / Methoden, Schachtelung von Includes, …) aus anderen Projekten mit ein."

Aus dem Artikel "Koexistenz von BAdI RSROA_VARIABLES_EXIT_BADI und Customer-Exit EXIT_SAPLRRS0_001" von Thorsten Kessler

Im Laufe der Zeit können in einem größeren BW System sehr viele unterschiedliche Autoren von BEx Exit Variablen Implementierungen beteiligt sein. Ich habe in einem Projekt schon mal über 25 gezählt. Und natürlich haben diese Autoren auch unterschiedliche Erfahrungen, Interessen, Fähigkeiten und Vorlieben. Der Eine sucht sich vielleicht eine ähnliche Logik und arbeitet mit Copy & Paste und passt dann an. Ein Anderer nutzt die neusten Features aus dem ABAP, z.B. den VALUE Konstruktor-Ausdruck und Inline Deklarationen. Vielleicht nutzt ein älterer Kollege noch Includes, um Logik wieder verwendbar zu machen, weil er den Klassen misstraut. Und ein jüngerer Kollege kennt keine Includes mehr, sondern nur Klassen und Methoden.

Häufig werden neue Variablen angelegt, obwohl exakt die gleiche Funktion in einer bestehenden Variable schon zur Verfügung steht. Das liegt zum Einen daran, dass es schwer ist, sich einen vollständigen Überblick über die bestehenden Variablen zu verschaffen. Und zum anderen traut man den Variablen der Kollegen vielleicht auch nicht. Letztendlich ist es ja nicht viel Arbeit, eine Variable neu anzulegen.

Es braucht also ein einheitliches Entwurfsmuster, damit die Qualität der Implementierungen gleichmäßig hoch ist. Die Suche nach bestehenden Variablenlogiken sollte vereinfacht werden und das Vertrauen in die Variablenexits der Kollegen sollte gestärkt werden.

Same same, but different

Häufig gibt es mehrere Variablenimplementierungen, die exakt das gleiche tun. Sie unterscheiden sich nur in einer Kleinigkeit. Hier ein paar Beispiele:

  • Variablen, die sich auf eine andere Variable beziehen. Z.B.
    • FISCPER minus 1 für die Variable X
    • FISCPER minus 1 für die Variable Y
    • ...
  • Variablen, die nur in einem festen Wert unterscheiden, z.B.
    • CALDAY minus 7 Tage
    • CALDAY minus 14 Tage
    • ...

Das sich eine Variable auf eine andere Variable bezieht, sieht man leider erst im Code. Oder im (nicht immer gepflegten) Beschreibungstext.

Ziel muss es sein, dass Logik nur genau einmal implementiert wird. Und das die Abhängigkeiten von Variablen transparent sichtbar gemacht werden.

DRY, DRY, DRY

In der Logik gibt es immer wieder die gleichen, ähnlichen Muster in drei Schritten:

  1. Referenzdaten ermitteln - Z.B. Daten aus anderen Variablen oder das Systemdatum
  2. Neue Werte Berechnen - Die eigentliche Logik, z.B. Berechnungen, Selektion von Daten mit OpenSQL etc.
  3. Ausgabe zusammenbauen - Die CHANGING-Tabelle C_T_RANGE zusammenbauen mit einem Wert, Listen von Einzelwerten, Intervallen oder Ranges

Der Code in diesen Schritten wiederholt sich immer wieder. Die meisten Dinge darin sind zwar scheinbar trivial, aber trotzdem widerspricht es dem Don't Repeat Yourself (DRY) Prinzip von Clean Code. Ein schönes Beispiel ist hier der Zugriff auf die Werte einer anderen Variablen aus dem Parameter I_T_VAR_RANGE . Obwohl das einfach ist, muss es nicht 100 Mal implementiert werden. Und manche weniger wichtigen Aspekte werden in den vielen Implementierungen oft ignoriert. Zum Beispiel wie mit Fehlersituationen umgegangen werden soll. Beispielsweise wenn ein Einzelwert benötigt wird, die andere Variable aber eine Liste mit 4 Werten liefert...

Abhilfe schaffen hier einheitliche Methoden, die den Entwickler bei der Implementierung unterstützen, und den Code pro Logik auf das Wesentliche reduzieren.

Überzeugungsarbeit

Für die aufgezeigten Probleme, gibt es natürlich auch elegante Lösungen, die ich oben schon angedeutet habe. In den folgenden Artikeln zu diesem Thema möchte ich sie skizzieren. Dabei ist es mir wichtig, die BW-Entwickler mit Vorteilen zu überzeugen. Denn ein neues Konzept, Framework, Entwurfsmuster, oder wie auch immer man das nennen möchte, muss nicht nur definiert sondern auch gelebt werden. Nur dann kann das BW-Projekt davon profitieren.

Ich freue mich wie immer über Kommentare auf meiner Seite zu diesem Thema.