Fiori Elements vs. Freestyle Fiori: Welcher UI-Ansatz passt zu deinem Projekt?

kategorie
UI
Veröffentlicht
autor
Johannes

Die Frage Fiori Elements oder Freestyle Fiori? beschäftigt viele UI-Entwickler im SAP-Umfeld. Beide Ansätze haben ihre Berechtigung - doch wann ist welcher der richtige? Dieser Artikel liefert eine fundierte Entscheidungshilfe mit konkreten Code-Beispielen und einer klaren Entscheidungsmatrix.

Überblick: Die zwei Welten der Fiori-Entwicklung

┌────────────────────────────────────────────────────────────────────┐
│ SAP Fiori UI-Entwicklung │
├────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────────┐ │
│ │ Fiori Elements │ │ Freestyle Fiori │ │
│ │ (Annotations-basiert) │ │ (SAPUI5 Custom) │ │
│ ├─────────────────────────┤ ├─────────────────────────────┤ │
│ │ • Automatische UI │ │ • Volle UI-Kontrolle │ │
│ │ • CDS Annotations │ │ • XML Views + Controller │ │
│ │ • Kein JavaScript │ │ • JavaScript/TypeScript │ │
│ │ • Templates (List, OP) │ │ • Custom Components │ │
│ │ • SAP Updates inklusive │ │ • Eigene Wartung │ │
│ └─────────────────────────┘ └─────────────────────────────┘ │
│ │
│ Aufwand: ●○○○○ Aufwand: ●●●●● │
│ Flexibilität: ●●○○○ Flexibilität: ●●●●● │
│ │
└────────────────────────────────────────────────────────────────────┘

Kurzvergleich

AspektFiori ElementsFreestyle Fiori
EntwicklungsaufwandGering (1-2 Tage)Hoch (1-2 Wochen)
UI-FlexibilitätBegrenzt (Templates)Unbegrenzt
WartungsaufwandMinimalKontinuierlich
LernkurveCDS AnnotationsSAPUI5/JavaScript
UX-KonsistenzAutomatisch SAP-konformManuell sicherzustellen
UpdatesVon SAP mitgeliefertEigenständig
DebuggingSchwieriger (Framework)Einfacher (eigener Code)

Was sind Fiori Elements?

Fiori Elements ist SAPs Ansatz für deklarative UI-Entwicklung. Statt UI-Code zu schreiben, definierst du die Oberfläche durch Annotations im CDS-Datenmodell.

So funktioniert Fiori Elements

┌─────────────────────────────────────────────────────────────────┐
│ Fiori Elements │
├─────────────────────────────────────────────────────────────────┤
│ │
│ CDS View + Annotations │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ Fiori Elements │ ──▶ Automatische UI-Generierung │
│ │ Runtime │ │
│ └───────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────┐ │
│ │ Fertige Fiori App │ │
│ │ • List Report │ │
│ │ • Object Page │ │
│ │ • Filter, Actions, Navigation │ │
│ └───────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

Verfügbare Fiori Elements Templates

TemplateBeschreibungTypische Anwendung
List ReportTabelle mit Filtern und ActionsStammdaten-Übersichten
Object PageDetail-Ansicht mit FacetsEinzelsatz-Bearbeitung
Analytical List PageKombination aus Chart und TabelleReporting, Dashboards
WorklistTask-orientierte ListeGenehmigungen, Aufgaben
Overview PageKachel-basiertes DashboardManagement-Übersichten
Form Object PageFormular-fokussierte AnsichtDatenerfassung

Fiori Elements Code-Beispiel

CDS Projection View mit UI Annotations:

@EndUserText.label: 'Bestellung - Projection'
@Metadata.allowExtensions: true
define root view entity ZC_ORDER
provider contract transactional_query
as projection on ZI_ORDER
{
key OrderId,
CustomerId,
OrderDate,
TotalAmount,
Currency,
Status,
/* Associations */
_Customer,
_Items
}

Metadata Extension (separate Datei .ddlx):

@Metadata.layer: #CORE
annotate view ZC_ORDER with
{
/* Header-Informationen */
@UI.headerInfo: {
typeName: 'Bestellung',
typeNamePlural: 'Bestellungen',
title: { value: 'OrderId' },
description: { value: '_Customer.CustomerName' }
}
/* Facets für Object Page */
@UI.facet: [
{
id: 'OrderHeader',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Bestelldaten',
position: 10
},
{
id: 'OrderItems',
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Positionen',
position: 20,
targetElement: '_Items'
}
]
/* Feld-Annotationen */
@UI.lineItem: [{ position: 10, importance: #HIGH }]
@UI.identification: [{ position: 10 }]
@UI.selectionField: [{ position: 10 }]
OrderId;
@UI.lineItem: [{ position: 20, importance: #HIGH }]
@UI.identification: [{ position: 20 }]
@UI.selectionField: [{ position: 20 }]
CustomerId;
@UI.lineItem: [{ position: 30 }]
@UI.identification: [{ position: 30 }]
OrderDate;
@UI.lineItem: [{ position: 40, criticality: 'StatusCriticality' }]
@UI.identification: [{ position: 40 }]
Status;
@UI.lineItem: [{ position: 50 }]
@UI.identification: [{ position: 50 }]
@Semantics.amount.currencyCode: 'Currency'
TotalAmount;
}

Ergebnis: Eine vollständige List Report + Object Page Anwendung ohne eine einzige Zeile JavaScript!


Was ist Freestyle Fiori?

Freestyle Fiori bedeutet die manuelle Entwicklung mit dem SAPUI5-Framework. Du hast volle Kontrolle über jeden Aspekt der Benutzeroberfläche.

So funktioniert Freestyle Fiori

┌─────────────────────────────────────────────────────────────────┐
│ Freestyle Fiori │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ XML Views │ │ Controllers │ │ Models │ │
│ │ (Layout) │ │ (JavaScript) │ │ (Daten) │ │
│ └───────┬────────┘ └───────┬────────┘ └───────┬────────┘ │
│ │ │ │ │
│ └────────────────────┼────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Custom Fiori Application │ │
│ │ • Eigene Komponenten │ │
│ │ • Custom Layouts │ │
│ │ • Volle JavaScript-Kontrolle │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

Freestyle Fiori Code-Beispiel

manifest.json (Auszug):

{
"sap.app": {
"id": "com.mycompany.orderapp",
"type": "application",
"title": "Bestellungen verwalten",
"dataSources": {
"mainService": {
"uri": "/sap/opu/odata4/sap/zapi_order/srvd_a2x/sap/zui_order/0001/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}
}
}
}

View (XML):

<mvc:View
controllerName="com.mycompany.orderapp.controller.OrderList"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc"
xmlns:f="sap.f"
xmlns:core="sap.ui.core">
<f:DynamicPage id="orderPage">
<f:title>
<f:DynamicPageTitle>
<f:heading>
<Title text="Bestellungen"/>
</f:heading>
<f:actions>
<Button
text="Neue Bestellung"
type="Emphasized"
press=".onCreateOrder"/>
<Button
text="Exportieren"
icon="sap-icon://excel-attachment"
press=".onExport"/>
</f:actions>
</f:DynamicPageTitle>
</f:title>
<f:header>
<f:DynamicPageHeader>
<f:content>
<FlexBox wrap="Wrap">
<Input
id="searchField"
placeholder="Bestellung suchen..."
liveChange=".onSearch"
width="300px"/>
<Select
id="statusFilter"
change=".onStatusFilterChange"
items="{/StatusOptions}">
<core:Item key="{Key}" text="{Text}"/>
</Select>
<DateRangeSelection
id="dateFilter"
change=".onDateFilterChange"/>
</FlexBox>
</f:content>
</f:DynamicPageHeader>
</f:header>
<f:content>
<Table
id="orderTable"
items="{/Orders}"
growing="true"
growingThreshold="50"
mode="MultiSelect"
selectionChange=".onSelectionChange">
<headerToolbar>
<OverflowToolbar>
<ToolbarSpacer/>
<Button
text="Löschen"
icon="sap-icon://delete"
enabled="{= ${/SelectedCount} > 0}"
press=".onDeleteSelected"/>
</OverflowToolbar>
</headerToolbar>
<columns>
<Column width="10em">
<Text text="Bestellnummer"/>
</Column>
<Column width="15em">
<Text text="Kunde"/>
</Column>
<Column width="10em">
<Text text="Datum"/>
</Column>
<Column width="8em" hAlign="End">
<Text text="Betrag"/>
</Column>
<Column width="10em">
<Text text="Status"/>
</Column>
</columns>
<items>
<ColumnListItem
type="Navigation"
press=".onOrderPress">
<cells>
<ObjectIdentifier
title="{OrderId}"
text="{OrderType}"/>
<Text text="{CustomerName}"/>
<Text text="{
path: 'OrderDate',
type: 'sap.ui.model.type.Date',
formatOptions: { style: 'medium' }
}"/>
<ObjectNumber
number="{
path: 'TotalAmount',
type: 'sap.ui.model.type.Currency',
formatOptions: { showMeasure: false }
}"
unit="{Currency}"/>
<ObjectStatus
text="{StatusText}"
state="{= ${Status} === 'COMPLETE' ? 'Success' : ${Status} === 'CANCELED' ? 'Error' : 'Warning' }"/>
</cells>
</ColumnListItem>
</items>
</Table>
</f:content>
</f:DynamicPage>
</mvc:View>

Controller (JavaScript):

sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel",
"sap/ui/model/Filter",
"sap/ui/model/FilterOperator",
"sap/m/MessageBox",
"sap/ui/export/Spreadsheet"
], function(Controller, JSONModel, Filter, FilterOperator, MessageBox, Spreadsheet) {
"use strict";
return Controller.extend("com.mycompany.orderapp.controller.OrderList", {
onInit: function() {
// Lokales View-Model für UI-State
var oViewModel = new JSONModel({
SelectedCount: 0,
StatusOptions: [
{ Key: "", Text: "Alle Status" },
{ Key: "NEW", Text: "Neu" },
{ Key: "INPROCESS", Text: "In Bearbeitung" },
{ Key: "COMPLETE", Text: "Abgeschlossen" },
{ Key: "CANCELED", Text: "Storniert" }
]
});
this.getView().setModel(oViewModel);
},
onSearch: function(oEvent) {
var sQuery = oEvent.getParameter("newValue");
var oTable = this.byId("orderTable");
var oBinding = oTable.getBinding("items");
var aFilters = [];
if (sQuery) {
aFilters.push(new Filter({
filters: [
new Filter("OrderId", FilterOperator.Contains, sQuery),
new Filter("CustomerName", FilterOperator.Contains, sQuery)
],
and: false
}));
}
// Kombiniere mit Status-Filter
this._applyFilters(aFilters);
},
onStatusFilterChange: function(oEvent) {
var sStatus = oEvent.getParameter("selectedItem").getKey();
this._sCurrentStatus = sStatus;
this._applyFilters();
},
onDateFilterChange: function(oEvent) {
var oDateRange = oEvent.getSource();
this._oDateFrom = oDateRange.getDateValue();
this._oDateTo = oDateRange.getSecondDateValue();
this._applyFilters();
},
_applyFilters: function(aAdditionalFilters) {
var aFilters = aAdditionalFilters || [];
// Status-Filter
if (this._sCurrentStatus) {
aFilters.push(new Filter("Status", FilterOperator.EQ, this._sCurrentStatus));
}
// Datum-Filter
if (this._oDateFrom && this._oDateTo) {
aFilters.push(new Filter("OrderDate", FilterOperator.BT,
this._oDateFrom, this._oDateTo));
}
var oTable = this.byId("orderTable");
var oBinding = oTable.getBinding("items");
oBinding.filter(aFilters.length > 0 ? new Filter({
filters: aFilters,
and: true
}) : []);
},
onSelectionChange: function(oEvent) {
var iSelectedCount = oEvent.getSource().getSelectedItems().length;
this.getView().getModel().setProperty("/SelectedCount", iSelectedCount);
},
onCreateOrder: function() {
// Navigation zu Create-View
var oRouter = this.getOwnerComponent().getRouter();
oRouter.navTo("orderCreate");
},
onOrderPress: function(oEvent) {
// Navigation zu Detail-View
var oItem = oEvent.getSource();
var sOrderId = oItem.getBindingContext().getProperty("OrderId");
var oRouter = this.getOwnerComponent().getRouter();
oRouter.navTo("orderDetail", {
orderId: sOrderId
});
},
onDeleteSelected: function() {
var oTable = this.byId("orderTable");
var aSelectedItems = oTable.getSelectedItems();
MessageBox.confirm(
"Möchten Sie " + aSelectedItems.length + " Bestellung(en) wirklich löschen?",
{
title: "Löschen bestätigen",
onClose: function(sAction) {
if (sAction === MessageBox.Action.OK) {
this._deleteOrders(aSelectedItems);
}
}.bind(this)
}
);
},
_deleteOrders: function(aItems) {
// OData Delete-Aufrufe
var oModel = this.getView().getModel("mainService");
aItems.forEach(function(oItem) {
var sPath = oItem.getBindingContext().getPath();
oModel.remove(sPath, {
success: function() {
// Erfolgreich gelöscht
},
error: function(oError) {
MessageBox.error("Fehler beim Löschen: " + oError.message);
}
});
});
},
onExport: function() {
var oTable = this.byId("orderTable");
var oBinding = oTable.getBinding("items");
var oSpreadsheet = new Spreadsheet({
workbook: {
columns: [
{ label: "Bestellnummer", property: "OrderId" },
{ label: "Kunde", property: "CustomerName" },
{ label: "Datum", property: "OrderDate", type: "Date" },
{ label: "Betrag", property: "TotalAmount", type: "Number" },
{ label: "Währung", property: "Currency" },
{ label: "Status", property: "StatusText" }
]
},
dataSource: oBinding,
fileName: "Bestellungen.xlsx"
});
oSpreadsheet.build();
}
});
});

Vergleich: Deutlich mehr Code, aber auch deutlich mehr Kontrolle über jeden Aspekt der UI!


Entscheidungsmatrix: Wann welcher Ansatz?

Fiori Elements empfohlen

KriteriumBegründung
✅ Standard CRUD-OperationenList Report + Object Page perfekt geeignet
✅ Schnelle Entwicklung gefordertFaktor 5-10x schneller als Freestyle
✅ SAP-Standard UX gewünschtAutomatisch Fiori Design Guidelines
✅ Wenig UI-Expertise im TeamKeine JavaScript-Kenntnisse nötig
✅ Langfristige Wartbarkeit wichtigSAP-Updates automatisch integriert
✅ Transaktionale Apps (RAP)Perfekte Integration mit RAP
✅ Analytical Apps mit Standard-ChartsAnalytical List Page nutzen

Freestyle Fiori empfohlen

KriteriumBegründung
✅ Hochgradig individuelle UIKeine Template-Grenzen
✅ Custom VisualisierungenEigene Charts, Diagramme, Karten
✅ Komplexe InteraktionsmusterDrag & Drop, Wizards, Multi-Step
✅ Third-Party Libraries nötigIntegration externer Komponenten
✅ Pixel-perfektes Design gefordertVolle Layout-Kontrolle
✅ Gaming/Gamification-ElementeAnimationen, Spielmechaniken
✅ Offline-First AnforderungVollständige Offline-Logik implementierbar

Entscheidungsbaum

Start: Neue Fiori App erforderlich
┌──────────────────────┐
│ Ist die UI-Anforderung │
│ mit Templates abbildbar?│
└─────────┬────────────┘
┌────────┴────────┐
│ │
▼ ▼
JA NEIN
│ │
▼ ▼
┌───────────────┐ ┌────────────────┐
│ Braucht die │ │ → FREESTYLE │
│ App komplexe │ │ FIORI │
│ Custom-Logik? │ └────────────────┘
└──────┬────────┘
┌────┴────┐
│ │
▼ ▼
JA NEIN
│ │
▼ ▼
┌─────────────────┐ ┌────────────────────┐
│ Kann die Logik │ │ → FIORI ELEMENTS │
│ in ABAP (RAP) │ │ (Rein deklarativ)│
│ implementiert │ └────────────────────┘
│ werden? │
└────────┬────────┘
┌────┴────┐
│ │
▼ ▼
JA NEIN
│ │
▼ ▼
┌──────────────────┐ ┌────────────────────┐
│ → FIORI ELEMENTS │ │ → FREESTYLE FIORI │
│ mit RAP Backend│ │ oder Hybrid │
└──────────────────┘ └────────────────────┘

Hybrid-Ansatz: Fiori Elements + Custom Extensions

In vielen Fällen ist ein Hybrid-Ansatz der beste Kompromiss: Fiori Elements als Basis mit gezielten Custom Extensions.

Extension-Möglichkeiten in Fiori Elements

Extension-TypBeschreibungAufwand
Header ExtensionCustom Header ContentNiedrig
Footer ExtensionCustom Actions im FooterNiedrig
Custom ColumnEigene Spalte mit Custom RendererMittel
Custom SectionEigene Section in Object PageMittel
Controller ExtensionOverride von Controller-MethodenHoch
Building BlockWiederverwendbare UI-KomponentenHoch

Beispiel: Custom Section Extension

manifest.json (Auszug):

{
"sap.ui5": {
"extends": {
"extensions": {
"sap.ui.controllerExtensions": {
"sap.fe.templates.ObjectPage.ObjectPageController": {
"controllerName": "com.mycompany.orderapp.ext.ObjectPageExt"
}
}
}
},
"routing": {
"targets": {
"OrderObjectPage": {
"options": {
"settings": {
"content": {
"body": {
"sections": {
"CustomSection": {
"template": "com.mycompany.orderapp.ext.CustomSection",
"title": "Lieferverfolgung",
"position": {
"anchor": "OrderItems",
"placement": "After"
}
}
}
}
}
}
}
}
}
}
}
}

Custom Section Fragment:

<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:macros="sap.fe.macros">
<VBox class="sapUiSmallMargin">
<Title text="Sendungsverfolgung"/>
<!-- Custom Map Component -->
<FlexBox height="300px" width="100%">
<core:HTML content="{= ${DeliveryMapHtml} }"/>
</FlexBox>
<ProgressIndicator
percentValue="{= ${DeliveryProgress} }"
displayValue="{= ${DeliveryStatus} }"
state="{= ${DeliveryProgress} === 100 ? 'Success' : 'Information' }"
showValue="true"/>
<HBox class="sapUiTinyMarginTop">
<Button
text="Tracking-Details"
type="Transparent"
icon="sap-icon://map"
press=".extension.com.mycompany.orderapp.ext.ObjectPageExt.onShowTrackingDetails"/>
</HBox>
</VBox>
</core:FragmentDefinition>

Controller Extension:

sap.ui.define([
"sap/ui/core/mvc/ControllerExtension",
"sap/m/MessageBox"
], function(ControllerExtension, MessageBox) {
"use strict";
return ControllerExtension.extend("com.mycompany.orderapp.ext.ObjectPageExt", {
// Override: Wird beim Laden der Object Page aufgerufen
override: {
onPageReady: function(mParameters) {
// Custom Initialisierung
this._loadDeliveryTracking();
}
},
// Custom Method für Tracking-Details
onShowTrackingDetails: function(oEvent) {
var oContext = this.base.getView().getBindingContext();
var sTrackingNumber = oContext.getProperty("TrackingNumber");
// Custom Dialog öffnen
if (!this._oTrackingDialog) {
this._oTrackingDialog = sap.ui.xmlfragment(
"com.mycompany.orderapp.ext.TrackingDialog",
this
);
this.base.getView().addDependent(this._oTrackingDialog);
}
this._oTrackingDialog.open();
},
_loadDeliveryTracking: function() {
// Custom API-Aufruf für Tracking-Daten
var oContext = this.base.getView().getBindingContext();
if (!oContext) return;
var sOrderId = oContext.getProperty("OrderId");
// Beispiel: Externe Tracking-API
fetch("/api/tracking/" + sOrderId)
.then(function(response) { return response.json(); })
.then(function(data) {
// Update Model mit Tracking-Daten
var oModel = this.base.getView().getModel();
oModel.setProperty(oContext.getPath() + "/DeliveryProgress", data.progress);
oModel.setProperty(oContext.getPath() + "/DeliveryStatus", data.status);
}.bind(this));
}
});
});

Vor- und Nachteile im Detail

Fiori Elements: Vorteile

  1. Entwicklungsgeschwindigkeit

    • Bis zu 80% weniger Code
    • Keine JavaScript-Entwicklung
    • Standard-Patterns sofort verfügbar
  2. Wartbarkeit

    • SAP liefert Updates automatisch
    • Accessibility-Konformität garantiert
    • Performance-Optimierungen inklusive
  3. Konsistenz

    • Automatisch Fiori Design Guidelines
    • Einheitliche UX über alle Apps
    • Mobile-ready ohne Zusatzaufwand
  4. Integration mit RAP

    • Nahtlose Anbindung an Backend
    • Draft Handling out-of-the-box
    • Validations und Actions automatisch verfügbar

Fiori Elements: Nachteile

  1. Eingeschränkte Flexibilität

    • Nur vordefinierte Templates
    • Custom Layouts schwer umsetzbar
    • Komplexe Interaktionen nicht möglich
  2. Debugging-Herausforderungen

    • Framework-generierter Code schwer zu debuggen
    • Annotation-Fehler schwer zu finden
    • Weniger direkte Kontrolle
  3. Lernkurve bei Annotations

    • Viele Annotations zu lernen
    • Dokumentation manchmal lückenhaft
    • Trial-and-Error bei komplexen Szenarien

Freestyle Fiori: Vorteile

  1. Volle Kontrolle

    • Jedes UI-Element individuell gestaltbar
    • Komplexe Interaktionen möglich
    • Pixel-perfekte Designs umsetzbar
  2. Third-Party Integration

    • Externe Libraries integrierbar (Charts, Maps, etc.)
    • Custom Components wiederverwendbar
    • Keine Framework-Grenzen
  3. Debugging

    • Eigener Code einfacher zu debuggen
    • Volle Browser-DevTools-Unterstützung
    • Klare Fehlerquellen

Freestyle Fiori: Nachteile

  1. Hoher Entwicklungsaufwand

    • 5-10x mehr Code als Fiori Elements
    • JavaScript/TypeScript-Expertise nötig
    • Längere Entwicklungszeiten
  2. Wartungsaufwand

    • Updates manuell einpflegen
    • Accessibility selbst sicherstellen
    • Performance-Optimierung selbst verantworten
  3. Konsistenz-Risiko

    • UX-Inkonsistenzen möglich
    • Fiori Guidelines manuell einhalten
    • Team-Koordination wichtiger

Code-Vergleich: Dieselbe Funktionalität

Anforderung: Bestellliste mit Filter und Navigation

Fiori Elements (ca. 50 Zeilen Annotations):

@Metadata.layer: #CORE
annotate view ZC_ORDER with
{
@UI.headerInfo: {
typeName: 'Bestellung',
typeNamePlural: 'Bestellungen',
title: { value: 'OrderId' }
}
@UI.lineItem: [{ position: 10 }]
@UI.selectionField: [{ position: 10 }]
OrderId;
@UI.lineItem: [{ position: 20 }]
@UI.selectionField: [{ position: 20 }]
CustomerId;
@UI.lineItem: [{ position: 30 }]
OrderDate;
@UI.lineItem: [{ position: 40, criticality: 'StatusCriticality' }]
@UI.selectionField: [{ position: 30 }]
Status;
}

Freestyle Fiori (ca. 250 Zeilen Code):

  • manifest.json: 50 Zeilen
  • View.xml: 80 Zeilen
  • Controller.js: 120 Zeilen

Fazit: Für Standard-CRUD-Szenarien ist Fiori Elements deutlich effizienter.


Praxisbeispiele: Wann welchen Ansatz?

Beispiel 1: Stammdatenpflege (→ Fiori Elements)

Anforderung: CRUD für Kundenstammdaten mit Adresse und Ansprechpartnern

Warum Fiori Elements:

  • Standard-CRUD mit List Report + Object Page
  • Child-Entities (Adressen, Ansprechpartner) als Facets
  • Validations über RAP
  • Draft Handling für lange Formulare

Beispiel 2: Dashboard mit Custom Charts (→ Freestyle)

Anforderung: Management-Dashboard mit interaktiven Visualisierungen

Warum Freestyle:

  • Custom D3.js oder Highcharts-Visualisierungen
  • Drag & Drop für Widget-Anordnung
  • Real-time Updates via WebSocket
  • Komplexe Filter-Interaktionen

Beispiel 3: Genehmigungsworkflow (→ Fiori Elements)

Anforderung: Genehmigungsprozess für Bestellanforderungen

Warum Fiori Elements:

  • Worklist Template ideal für Aufgabenlisten
  • Actions (Genehmigen, Ablehnen) einfach zu definieren
  • Standard-Navigation zwischen Aufgaben
  • Mobile-ready ohne Zusatzaufwand

Beispiel 4: Produktkonfigurator (→ Freestyle)

Anforderung: Interaktiver 3D-Produktkonfigurator

Warum Freestyle:

  • 3D-Rendering mit Three.js
  • Komplexe Interaktionen (Rotation, Zoom)
  • Dynamische Preisberechnung im Frontend
  • Custom Validierungslogik

Beispiel 5: Hybrid - Bestellverwaltung mit Tracking

Anforderung: Standard-Bestellverwaltung + Lieferverfolgung mit Karte

Lösung: Fiori Elements + Custom Extension

  • List Report und Object Page als Basis
  • Custom Section mit Map-Integration
  • Controller Extension für Tracking-API

Migration zwischen den Ansätzen

Von Freestyle zu Fiori Elements

In manchen Fällen kann eine Migration von Freestyle zu Fiori Elements sinnvoll sein:

SchrittBeschreibung
1. AnalyseWelche Features sind Standard vs. Custom?
2. RAP-BackendBackend auf RAP umstellen
3. AnnotationsUI-Annotations für Standard-Features erstellen
4. Custom ExtensionsNicht-Standard-Features als Extensions implementieren
5. MigrationSchrittweise umstellen, Template für Template

Von Fiori Elements zu Freestyle

Selten nötig, aber möglich wenn Anforderungen stark wachsen:

SchrittBeschreibung
1. XML-ExportGenerierte XML-Views als Basis exportieren
2. ControllerController-Logik aus Extensions übernehmen
3. RefactoringFramework-Abhängigkeiten entfernen
4. Custom ModelOData-Anbindung anpassen

Best Practices

Für Fiori Elements

DODON’T
✅ Annotations in separater .ddlx Datei❌ Zu viele Annotations inline in CDS
✅ RAP für komplexe Logik nutzen❌ Logik in UI-Extensions packen
✅ Standard-Templates verwenden❌ Exzessive Anpassungen vornehmen
✅ Metadata Extensions für Lesbarkeit❌ Alles in eine Datei schreiben
✅ Building Blocks für Wiederverwendung❌ Jede Extension einzeln implementieren

Für Freestyle Fiori

DODON’T
✅ Fiori Design Guidelines folgen❌ Komplett eigenes Design erfinden
✅ sap.ui.core Controls verwenden❌ Native HTML ohne guten Grund
✅ Routing für Navigation nutzen❌ Eigene Navigation bauen
✅ Models für Datenbindung❌ DOM-Manipulation direkt
✅ Controller-Logik klein halten❌ Alles in einen Controller packen

Fazit: Der richtige Ansatz für dein Projekt

Wähle Fiori Elements wenn:

  • Du Standard-CRUD-Anwendungen baust
  • Du schnell produktiv sein musst
  • Du wenig Frontend-Expertise hast
  • Du langfristige Wartbarkeit priorisierst

Wähle Freestyle Fiori wenn:

  • Du hochgradig individuelle UIs brauchst
  • Du Custom Visualisierungen oder Third-Party Libraries integrierst
  • Du komplexe Interaktionsmuster implementierst
  • Du volle Kontrolle über jeden Aspekt benötigst

Wähle Hybrid wenn:

  • 80% Standard, 20% Custom
  • Spezielle Sections oder Columns nötig sind
  • Du die Vorteile beider Welten kombinieren willst

In der Praxis zeigt sich: 80-90% der transaktionalen SAP-Anwendungen können effektiv mit Fiori Elements umgesetzt werden. Der Hybrid-Ansatz deckt weitere 5-8% ab. Nur für wirklich spezielle Anforderungen ist reines Freestyle Fiori der richtige Weg.


Siehe auch: