Ein Framework für alle Anforderungen im BW Transformationsroutinen

Ein Framework für alle Anforderungen im BW Transformationsroutinen

In dieser Beitragsserie geht es ausnahmsweise nicht um SQLScript, sondern um primär um ABAP. Die Transformationsroutinen im SAP BW lassen sich ja in beiden Programmiersprachen implementieren. Und es gab und gibt viele Kunden, die ABAP gerne dafür Einsetzen. Dafür gibt es unterschiedliche Gründe. Eine große Stärke von ABAP ist die dynamische Verwendung, die ich in dieser Form mit SQLScript nicht erreichen kann.

Ich möchte in dieser Serie meine Erfahrungen aus meinen letzten BW Projekte veröffentlichen. Ich hatte immer wieder das Glück, dass ich in Greenfield Projekten bei sehr großen Kunden mit hohen Anforderungen an das SAP BW und insbesondere an die Backend Verarbeitung arbeiten durfte. Das ist für mich als Berater mit einem Fokus auf Softwareentwicklung und starkem BW und HANA Wissen natürlich ideal.

Die Probleme in BW Transformationsroutinen

In den über viele, teilweise über mehr als 10 Jahren, gewachsenen SAP BW-Systemen findet man häufig eine große Bandbreite von unterschiedlichen BW-Transformationsroutinen. Manche davon sind sehr sauber und effizient programmiert. Andere sind kaum mehr wartbar oder haben massive Performance Probleme. Ein paar typische Fälle, die wahrscheinlich die meisten schon mal gesehen haben, möchte ich hier vorstellen.

Der Spaghetti LOOP

Unstrukturierten bzw. schlecht strukturierten Code bezeichnet man als Spaghetticode [1]https://de.wikipedia.org/wiki/Spaghetticode. Mit der Länge des Codeabschnitts nimmt die Lesbarkeit erheblich ab. Das Problem wird in Transformationsroutinen dadurch verstärkt, dass diese normalerweise aus einem großen LOOP über dem ResultPackage[2]Ich schreibe ResultPackage, weil es der meiste Fall ist. Je nach Routinentyp kann natürlich auch das SourcePackage gemeint sein bestehen. Diese unheilvolle Kombination nenne ich Spaghetti LOOP. Ein typisches Problem hierin ist beispielsweise, das Werte aus einem vorherigen Schleifendurchlauf in Variablen abgespeichert und in späteren Durchläufen wieder verwendet werden. Das bedeutet, dass ich stets den Code vor und nach der aktuellen Zeile durchsuchen muss, ob eine Variable verändert wird.

Ich habe schon Spaghetti LOOPs über mehr als 2000 Zeilen gesehen und sie haben die Fehlersuche und die Bearbeitung von neuen Anforderungen fast zum Erliegen gebracht. Weil jede Korrektur unvorhersehbare Auswirkungen auf die anderen Anforderungen hervorgebracht hat.

50 shades of modularisation

Es gibt in ABAP leider sehr viele unterschiedliche Möglichkeiten, wie man vermeiden kann, dass der ganze Code komplett in der Transformationsroutine landet. Leider werden auch alle dieser Modularisierungstechniken verwendet:

  • Instanzmethoden
  • Klassenmethoden
  • Funktionsbausteinen
  • Unterprogrammen
  • Makros
  • Includes (!)

Ich habe tatsächlich schon Transformationsroutinen gesehen, die nur aus einem INCLUDE Anweisung bestanden. Das gleiche Include wurde dann in unterschiedlichen Routinen genutzt. Das war die hässlichste Anwendung des DRY[3]Don’t Repeat Yourself Prinzips, die ich je gesehen hatte.

Performanceprobleme

Generic Overengineering

Generische und dynamische Programmierung ist in ABAP sehr gut möglich. Damit kann man sehr nützliche Sachen machen, aber man kann auch einfache Anforderungen extrem verkomplizieren. Die Les- und Wartbarkeit des Codes leiden dann darunter. Und häufig sieht man, dass die gleiche generische Funktion mehrfach von unterschiedlichen Entwicklern erfunden wurde. Oder dass generische Funktionen nur genau einmal aufgerufen werden.

TYPE STANDARD TABLE und ASSIGN

Die Auslagerung von Code aus den Transformationsroutinen bringt ein Typisierungsproblem mit sich. Der Type des ResultPackage wird in der Routinenklasse generiert. Deshalb wird in der Signatur von Methoden typischerweise mit TYPE STANDARD TABLE gearbeitet. Der Zugriff auf die Zeilen der Tabelle erfolgt dann mit LOOP ASSIGNING und der Zugriff auf die Felder der Zeilen auch wieder dynamisch mit ASSIGN. Mit den Feldsymbolen können wir dann direkt in die Tabelle in der aktuellen Zeile/Spalte schreiben. Das ist praktisch, erlaubt aber auch die Veränderung von Feldern, die wir ursprünglich nur lesen wollten. Es fehlt also eine saubere Dokumentation, was gelesen und was verändert wird. Und der Code für jedes verwendete Feld ist relativ lang (und langsam):

FIELD-SYMBOL <lv_value> TYPE ..
ASSIGN COMPONENT 'FELDNAME' 
    OF STRUCTURE <resultline>
     TO <lv_value>. 
ASSERT <lv_value> IS ASSIGNED. 

Das kann man schöner machen.

Die Ursachen

Für diese (und andere) Probleme gibt es mehrere Ursachen, die man in Projekten immer wieder beobachtet.

Gelegenheitsprogrammierer

Fast immer werden die Transformationsroutinen von BW-Beratern[4]Der Begriff Gelegenheitsprogrammierer klingt sehr negativ, er soll aber nicht die Arbeit der Kollegen abwerten. Die meisten BW-Berater sind sehr kompetent und erfahren und ich habe großen Respekt vor Ihrer Arbeit. Aber als Entwickler, die sich 8 Stunden am Tag mit ABAP auseinandersetzen, habe ich eine anderes Verständnis für die Programmierung als ein typischer BW-Berater. entwickelt, die nur begrenzt Programmiererfahrungen haben. Das reicht auch, um vielleicht für 70% der Anforderungen eine ausreichende Lösung zu finden. Aber wenn es dann komplexer wird, sind fundierte ABAP und Programmierkenntnisse gefragt. Die erlernt man nicht nebenbei, sondern dann braucht es einen erfahrenen Programmierer.

Manchmal überschätzen sich auch BW-Berater und versuchen es einfach. Und manchmal führt das auch zu einer mehr oder weniger lauffähigen Lösung. Aber in Bezug auf Wartbarkeit sieht es dann ganz schlecht aus.

ERP ABAP Entwickler

Falls die BW-Berater scheitern, kommt es häufig zur nächsten Ursache von Problemen: ERP ABAP Entwickler sind es nicht gewöhnt, mit echten Massendaten zu arbeiten. Darum werden zwar lauffähige, saubere, wartbare, aber langsame Konzepte auf Zeilenebene umgesetzt.

Bewegliche Ziele

Häufig sind die Anforderungen nicht von Anfang an klar. Zum einen verändern sich die Anforderungen und zum anderen kommt immer wieder was neues hinzu. Diese Veränderungen werden immer in den bestehenden Code eingewoben. Das passiert auch dann, wenn der bestehende Code nicht mehr 100% verstanden wird.

Viele Köche

Durch lange Softwarelebenszyklen und lange Projekte ergibt sich das nächste Problem. Da in einem BW laufend weiter entwickelt wird, sind im Laufe der Zeit viele unterschiedliche Personen daran beteiligt. Damit wird das Rad immer wieder neu erfunden, und die Anzahl der Variationen nimmt zu. Für die komplexen Transformationen aus den ersten Tagen findet sich selten ein echter Experte.

Keine modernen Konzepte und Methoden

In der SAP Welt setzen sich moderne Konzepte und Methoden zum Entwickeln von Software langsamer durch, als das in anderen Gebieten der Fall ist. Dazu gehören unter anderem:

  • Test Driven Development (TDD)[5]https://de.wikipedia.org/wiki/Testgetriebene_Entwicklung
  • Clean Code [6]https://de.wikipedia.org/wiki/Clean_Code

Das gilt in besonderem Maße für BW-Transformationsentwicklung. Zum einen, wegen der oben unter genannten Aspekte. Zum anderen auch, weil die Anforderungen häufig unterschätzt werden, nach dem Motto: Für die kleine Routine lohnt sich das doch gar nicht.

Auch ist die Anwendung von TDD erschwert, wenn man gegen den Datenbankzustand programmieren muss. Das ist aber im BW leider der Fall.

Das Transformationsframework

Wir haben schon in unterschiedlichen Projekten die Transformationsroutinen in eine saubere Struktur gebracht. Dabei hilft ein Framework, das ein sauberes und einheitliches Vorgehen erzwingt.

Der wichtigste Punkt ist, dass die Komplexität der Anforderungen in kleine, einfache Schritte zerlegt wird. Und dass jeder dieser Schritte in einer separaten Methode implementiert wird. Manche dieser Schritte können generisch implementiert sein und lassen sich in unterschiedlichen Datenmodellen immer wieder verwenden. Andere Schritte sind konkret genau nur für eine Transformation notwendig.

Diese Zerlegung ermöglicht es, dass wir zum einen die Steuerung der Schritte und die Parametrisierung auslagern. Also eine Trennung zwischen:

  1. Was soll gemacht werden
    1. Welche Schritte in welcher Reihenfolge
    2. Welche Felder werden in einem Schritt gelesen, welche verändert
    3. (Welche Datensätze sollen in einem Schritt verändert werden?)
  2. Wie soll der Schritt implementiert werden.

Durch die Zerlegung in kleine Schritte und die Auslagerung der Steuerung dieser Schritte lassen sich viele orthogonale Anforderungen implementieren. Unter anderem:

  • Laufzeitmessung pro Schritt
  • Generierung einer Dokumentation
  • Gezielte Breakpoints vor einem Schritt
  • Test Driven Development
  • Wiederverwendbarkeit von Schritten
  • Generierung von AMDP Code

In den folgenden Blogposts:

  • Die Zerlegung in Schritte – Das sieht zunächst so aus, als ob da viel Overhead dazu kommt. Das ist aber Quatsch. Laufzeit und Wartbarkeit verbessern sich, auch wenn es etwas mehr Quelltext wird.
  • Die Steuerung des Ganzen – Ein Tabellenwerk, mit dem man die Transformationen und die wichtigen Parameter der Schritte beschriebt.
  • Fehlerverarbeitung
  • Neue Felder braucht das Land – damit sich die Anforderungen in kleine Schrittte zerlegen lassen, müssen wir teilweise noch zusätzliche Felder in unser ResultPackage einbaueh

Anmerkungen und Verweise   [ + ]

1. https://de.wikipedia.org/wiki/Spaghetticode
2. Ich schreibe ResultPackage, weil es der meiste Fall ist. Je nach Routinentyp kann natürlich auch das SourcePackage gemeint sein
3. Don’t Repeat Yourself
4. Der Begriff Gelegenheitsprogrammierer klingt sehr negativ, er soll aber nicht die Arbeit der Kollegen abwerten. Die meisten BW-Berater sind sehr kompetent und erfahren und ich habe großen Respekt vor Ihrer Arbeit. Aber als Entwickler, die sich 8 Stunden am Tag mit ABAP auseinandersetzen, habe ich eine anderes Verständnis für die Programmierung als ein typischer BW-Berater.
5. https://de.wikipedia.org/wiki/Testgetriebene_Entwicklung
6. https://de.wikipedia.org/wiki/Clean_Code

Fordern Sie weitere Informationen an