Lade Inhalt...

Entwicklung eines Webshops für die Lebensmittelbranche auf Basis von Ruby on Rails

von Daniel Mattes (Autor) Volker Kindermann (Autor)

Diplomarbeit 2008 122 Seiten

Informatik - Internet, neue Technologien

Leseprobe

Inhaltsverzeichnis

1 Einleitung

2 Technologie- und Branchenanalyse
2.1 Wahl der Programmiersprache und eines Frameworks
2.1.1 Anforderungen
2.1.1.1 Plattformunabhängigkeit
2.1.1.2 Netzwerkunterstützung
2.1.1.3 Skalierbarkeit
2.1.1.4 Unterstützung verteilter Systeme
2.1.1.5 Design Pattern
2.1.1.6 Verschlüsselung
2.1.1.7 Test-getriebene Entwicklung
2.1.1.8 Open-Source
2.1.2 Die Kandidaten
2.1.2.1 Gemeinsamkeiten und Unterschiede
2.1.2.2 Vergleich der Frameworks
2.1.3 Fazit
2.2 Einblick in den Lebensmitteleinzelhandel (LEH)
2.2.1 Struktur des Lebensmitteleinzelhandels
2.2.2 Unternehmen auf dem deutschen Markt
2.2.3 Die Entwicklung des Online-Shoppings im Lebensmitteleinzelhandel
2.2.3.1 Eignung von Lebensmitteln für das Online-Shopping
2.2.3.2 Erfolgsanalyse von bestehenden Online-Shops
2.2.4 Analyse der Voraussetzungen von Online-Shops für Lebensmittel in Deutschland
2.2.4.1 Umweltanalyse
2.2.4.2 Branchenanalyse
2.2.4.3 Lieferantenanalyse
2.2.5 Zusammenfassung der Erkenntnisse über den Online-LEH in Deutschland
2.2.5.1 Nachfragerperspektive
2.2.5.2 Anbieterperspektive
2.2.5.3 Erfolgsaussichten

3 Anforderungen an die Anwendung
3.1 Anforderungen an das Frontend
3.1.1 Umgebung
3.1.1.1 Browseroptimierung
3.1.1.2 Bildschirmauflösung
3.1.2 Anforderungen
3.1.2.1 Registrierung und Anmeldung von Benutzern
3.1.2.2 Produktsuche
3.1.2.3 Warenkorb
3.1.2.4 Bestellvorgang
3.2 Anforderungen an das Backend
3.2.1 Datenbeschaffung
3.2.2 Client-Ausführungsumgebung
3.2.3 Auftrags-Status-Modell
3.2.4 Sitzungs- / Session-Verwaltung
3.2.5 Filial-Einstellungen
3.2.6 Auftragseingang
3.2.6.1 Auftragseingangsfilter
3.2.6.2 Auftragsauswahl
3.2.6.3 Ausklappbare Detailansicht
3.2.7 Auftragsbearbeitung
3.2.7.1 Artikelsortierung
3.2.7.2 Drucken
3.2.7.3 Liefermenge
3.2.7.4 Kundenbenachrichtigung
3.2.7.5 Verlinkte Kundenansicht
3.2.8 Kundensuche
3.2.9 Bestell- / Kundenrestriktionen

4 Lösungskonzept
4.1 Allgemein
4.1.1 Server-Umgebung
4.1.1.1 Hardware
4.1.1.2 Software
4.1.2 Domain Modell und Datenbankmodell
4.1.2.1 Der Kunde
4.1.2.2 Das Produkt
4.1.2.3 Der Auftrag
4.1.2.4 Produkt-Auftrag-Relation
4.1.2.5 Die Filiale
4.1.2.6 Öffnungszeiten
4.1.2.7 Organisation der Filialen nach ihrem Sortiment
4.1.3 Anpassung der Konfiguration
4.1.3.1 Gemeinsame Modellklassen
4.1.3.2 Gemeinsames Plugin-Verzeichnis
4.1.3.3 Einrichtung des Action Mailers
4.1.3.4 Speicherort der Sessions
4.2 Entwicklung des Frontends
4.2.1 Allgemeiner Aufbau des Shops
4.2.1.1 Aufteilung der Seite mittels Block-Elementen
4.2.1.2 Navigation und Aktualisierung mittels Ajax (Asynchronous JavaScript and XML)
4.2.1.3 Deaktiviertes JavaScript
4.2.2 Registrierung und Anmeldung von Benutzern
4.2.2.1 Installation
4.2.2.2 Abschluss der Installation
4.2.2.3 Customer-Modell
4.2.2.4 CustomerObserver und Mailer
4.2.2.5 Ablauf der Registrierung
4.2.2.6 Ablauf der Anmeldung
4.2.3 Suche von Filialen
4.2.3.1 Installation
4.2.3.2 Verwendung
4.2.4 Verwaltung und Suche von Produkten
4.2.4.1 Installation von „acts_as_taggable_on_steroids“
4.2.4.2 Installation und Anpassung von „will_pagination“
4.2.4.3 Erstellung und Zuweisung von Tags
4.2.4.4 Suche von Produkten
4.2.5 Warenkorb
4.2.6 Bestellvorgang
4.2.6.1 Details
4.2.6.2 Alles richtig?
4.2.6.3 Fertig
4.2.7 Plugin SimpleCaptcha
4.2.7.1 Installation
4.2.7.2 Verwendung
4.3 Entwicklung des Backends
4.3.1 Datenbeschaffung
4.3.2 Produktkategorien
4.3.2.1 Hersteller
4.3.2.2 Produkte
4.3.2.3 Filialdaten
4.3.2.4 Update-Intervall
4.3.3 Client-Ausführungsumgebung
4.3.4 JavaScript / Ajax / Prototype
4.3.5 Auftrags-Status-Modell
4.3.6 Sitzungs- / Session-Verwaltung
4.3.7 Filial-Einstellungen
4.3.8 Ersatz des Auftragsfilters durch Links in der Navigationsleiste
4.3.9 Auftragseingang
4.3.10 Auftragsbearbeitung
4.3.11 Auftragsabholung
4.3.12 Auftragskulanz
4.3.13 Auftragsstorno
4.3.14 Kundensuche
4.3.15 Bestell-/Kundenrestriktionen
4.3.16 Kundenbenachrichtigung

5 Fazit
5.1 Fazit von Bearbeiter
5.2 Fazit von Bearbeiter

Aufteilung der Diplomarbeit

Die Diplomarbeit ist eine Gemeinschaftsarbeit. Die Ausarbeitung wurde wie folgt aufgeteilt:

Bearbeiter 1

2.1 Wahl der Programmiersprache und eines Frameworks

3.2 Anforderungen an das Backend

4.1.1 Server-Umgebung

4.1.2 Domain Modell und Datenbankmodell

4.3 Entwicklung des Backends

5.1 Fazit

Bearbeiter 2

2.2 Einblick in den Lebensmitteleinzelhandel (LEH)

3.1 Anforderungen an das Frontend

4.1.3 Anpassung der Konfiguration

4.2 Entwicklung des Frontends

5.2 Fazit

Abkürzungen

Abbildung in dieser Leseprobe nicht enthalten

1 Einleitung

Im Internet ist bereits eine Vielzahl an Webshops vorhanden, die ein breites Spektrum an Artikeln abdecken, wie z. B. Bücher, Elektroartikel, Kosmetikprodukte und vieles mehr. Im Lebensmittelbe- reich ist die Anzahl an Webshops in Deutschland jedoch sehr gering und wird meist nur in größe- ren Städten angeboten. Anders verhält es sich in Ländern wie der Schweiz oder in Großbritannien. Dort sind Webshops für Lebensmittel erfolgreich am Markt platziert. In der durchgeführten Bran- chenanalyse wurde ersichtlich, dass auch der deutsche Markt Potenzial für Onlineshops mit Lebensmittel bietet. Die zentrale Idee ist die Abholung des Einkaufs anstelle des Versands. Damit unterscheidet sich das Vorhaben von den bestehenden reinen Onlineversendern. Der Grund, die Abholung dem Versand vorzuziehen, liegt in den ungelösten Problemen beim Verschicken von Lebensmitteln.

In Zusammenarbeit mit der X in Y ist ein Webshop für Lebensmittel entstanden. Dabei hat der Kunde die Möglichkeit, seinen Einkauf online zusammenzustellen und an eine Filiale von X zu senden. Der erstellte Auftrag wird dann in der vom Kunden ausgewählten Filiale kommissioniert und steht dem Kunden fertig zusammengestellt zur Abholung bereit.

Anhand der ausgearbeiteten Technologieanalyse wurde die Programmiersprache „Ruby on Rails“ zur Realisierung der Anwendung ausgewählt.

Die Diplomarbeit umfasst die Entwicklung einer Komplettlösung aus Webshop und Auftrags-Mana- gement, wie sie in einem Lebensmittelkonzern mit Filialen als Vertriebsstruktur eingesetzt werden kann.

Der Webshop bietet die auf Lebensmittel abgestimmte Einkaufs-Funktionalität. Der Schwerpunkt der Einkaufszusammenstellung ist dabei auf das Auffinden von Produkten ausgelegt. Dies beinhal- tet sowohl die Produktsuche als auch die Kategorisierung sowie die semantische Verknüpfung von Lebensmitteln durch Tagging. Das Auftrags-Management dient der Verteilung der Aufträge auf die zur Kommissionierung ausgewählten Filialen und stellt dort über eine Web-Applikation die erforder- lichen Ansichten und Funktionen bereit, um die Kundenaufträge bearbeiten zu können.

2 Technologie- und Branchenanalyse

Für die Umsetzung des Web-Shops ist eine geeignete Basis zu wählen auf der die Programmierung und Entwicklung erfolgen kann. In diesem Abschnitt soll die technische Realisierbarkeit der Idee mit den betrachteten Mitteln geprüft werden. Dabei werden zum einen die Möglichkeiten der Programmiersprachen genauer betrachtet und zum anderen die Funktionalität der auf der Programmiersprache aufsetzenden Frameworks.

Als nicht-technischer Teil ist in diesem Kapitel ein Einblick in den Lebensmitteleinzelhandel (LEH) gegeben. Zur Übersicht wird die Struktur des LEH sowie die Unternehmen am deutschen Markt dar- gestellt. Im Speziellen wird auf die Entwicklung des Online-Shoppings im LEH und die Eignung von Lebensmittel für diesen Bereich eingegangen. Weiterhin werden die Voraussetzungen für den deut- schen Markt anhand einer Umwelt-, Branchen- und Lieferantenanalyse dargestellt und zuletzt ein Fazit abgegeben.

2.1 Wahl der Programmiersprache und eines Frameworks

2.1.1 Anforderungen

Aufgrund der kurzen Zeit von fünf Monaten, die für die Entwicklung eines ablauffähigen Prototyps zur Verfügung steht, ist es wichtig, dass die Programmiersprache und das darauf aufbauende Fra- mework umfangreich zur Unterstützung beitragen. Deshalb werden vor Beginn der Implementie- rung zwei für ein solches Projekt geeignete Frameworks in ihrer Funktionalität verglichen.

Das Zusammenspiel von Frontend und Backend soll, gerade weil es sich um eine verteilte Anwen- dung handelt, möglichst einfach sein. Die Benutzung unterschiedlicher Programmiersprachen für Frontend und Backend würde den Einsatz von Web Services oder geeigneter Middleware wie z. B. CORBA oder SOAP mit sich bringen. Das würde zusätzlichen Aufwand für die Definition von Schnitt- stellen und deren Beschreibung in einem unabhängigen Format, wie etwa WSDL, bedeuten.

Die im Folgenden angeführten Leistungsmerkmale sind wichtig, damit während der Entwicklung zentrale Punkte wie etwa die Implementierung der Geschäftslogik im Mittelpunkt stehen, und nicht die Auseinandersetzung mit Problemen wie Transaktionen oder Authentifizierung, die abstrahiert in vielen Anwendungen vorkommen und bereits gelöst wurden.

2.1.1.1 Plattformunabhängigkeit

Ausschlaggebend bei der Wahl der Programmiersprache ist die selbstgestellte Forderung, die Management-Applikation betriebssystem-unabhängig zu implementieren, um auf bestehende Systeme potentieller Kunden mit der Management-Applikation problemlos aufsetzen zu können.

2.1.1.2 Netzwerkunterstützung

Des Weiteren sollte die Sprache und das Framework auf die Benutzung von Netzwerkkomponenten, wie etwa Verbindungsendpunkte (Sockets), Protokolle (TCP, IP, HTTP), ausgelegt sein und das Arbei- ten damit durch Module und Bibliotheken unterstützen. Bei der Benutzung von Netzwerkfunktionen sollte eine Abstraktionsschicht die netzwerknahe (low-level) Schicht kapseln, um Fehlerquellen auf dieser Ebene von vornherein auszuschließen und die Konzentration bei der Programmierung auf die Geschäftslogik lenken zu können.

2.1.1.3 Skalierbarkeit

Eine weitere wichtige Anforderung an das einzusetzende Framework ist eine gute Skalierbarkeit, um bei wachsender Auslastung das System um benötigte Ressourcen erweitern zu können. So kann es z. B. notwendig werden, mehrere Webserver einzusetzen und die Anfragen per Lastverteilung (load balancing) zu verteilen. Werden die steigenden Seitenabfragen bewältigt, kann oft als Folge die Datenbank zum Flaschenhals werden. Deshalb sollten auch Mechanismen zur Verteilung von Anfragen auf mehrere Datenbanken vom Framework bereitgestellt werden.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 2.1: Skalierbarkeit

2.1.1.4 Unterstützung verteilter Systeme

Aufgrund der komplexen Anforderungen, wie sie bei verteilten Systemen auftreten, sollte das einzusetzende Framework wichtige Grundfunktionen bereitstellen.

Zum einen muss sichergestellt sein, dass beim gleichzeitigen Zugriff vieler Benutzer auf eine Ressource die betreffenden Daten konsistent sind. Zum Beispiel kann die Situation auftreten, dass mehrere Angestellte gleichzeitig versuchen, denselben Auftrag auszuwählen, weil sie ihn bearbeiten wollen. Dazu sollte das Framework einen Transaktionsmechanismus bereitstellen. Mit Transaktionen können kritische Operationen zusammengefasst und als Ganzes ausgeführt werden. Schlägt ein Aufruf fehl, so können alle während der Transaktion getätigten Änderungen, die das System in einen inkonsistenten Zustand versetzen würden, rückgängig gemacht werden.

Zum anderen müssen rollen-basierte Rechte vergeben werden können. Jeder Mitarbeiter darf je nach seinem Status unterschiedliche Funktionen ausführen. So kann etwa nur der Abteilungsleiter die Filiale vom System an- und abmelden oder einen Auftrag stornieren. Somit muss das Framework einen Mechanismus bieten, um vor jedem Aufruf einer Methode eine Überprüfung anzustoßen, die ermittelt, ob der aufrufende Benutzer die Funktion auch ausführen darf.

2.1.1.5 Design Pattern

Weiterhin sollte das Framework das Design-Pattern „Model View Controll“ (MVC) unterstützen, um die Trennung von Anwendungslogik und Präsentation zu ermöglichen. Der MVC-Ansatz steigert die Übersichtlichkeit und damit auch die Wartbarkeit der Anwendung für den Entwickler. Eine sinnvolle Trennung dieser Bereiche ermöglicht zu einem späteren Zeitpunkt eine einfache Erweiterung der Funktionalität.

2.1.1.6 Verschlüsselung

Da die Sicherheit der zu übermittelnden Daten hohe Priorität hat, ist eine Verschlüsselung der Kommunikation zwischen Webserver und Backend-Anwendung angedacht. Das Framework sollte deshalb Unterstützung zum Aufbau gesicherter Verbindungen mitbringen.

2.1.1.7 Test-getriebene Entwicklung

Um nach der Prototypphase ein möglichst stabil laufendes Produkt anbieten zu können, ist es wichtig, sehr früh in der Entwicklung mit dem Testen zu beginnen. Auch hier sollte das Framework Design-Pattern wie z. B. Unit Tests unterstützen.

2.1.1.8 Open-Source

Schließlich soll das einzusetzende Framework kostenlos einsetzbar sein. Zwar gibt es kommerzielle Lösungen, doch integrieren sie Neuerungen meist recht spät, da es keine treibende Community dafür gibt. Außerdem bieten sie keine K.o.-Kriterien, die ihren Einsatz und den damit verbundenen Kostenaufwand rechtfertigen würden.

2.1.2 Die Kandidaten

Auf die hier beschriebenen Anforderungen ist sowohl die Programmiersprache Java als auch die Programmiersprache Ruby ausgelegt. Für beide Sprachen existieren ausgereifte Frameworks. Zum Vergleich stehen somit folgende Frameworks gegenüber: JBoss Application-Server als Referenzimplementierung der Java Enterprise Edition (J2EE) und Ruby on Rails (RoR).

2.1.2.1 Gemeinsamkeiten und Unterschiede

Sprachen

Beide Sprachen sind durchgehend objektorientiert. Dabei wurde bei Ruby noch etwas mehr als bei Java auf die konsequente Einhaltung dieses Paradigmas geachtet. So gibt es in Ruby keine primiti- ven Datentypen wie byte, int oder double. Stattdessen handelt es sich tatsächlich bei allen Datentypen um Objekte. Aus diesem Grund ist der Aufruf von 5.succ möglich, der die folgende Ganzzahl liefert.

Die engere Bindung an das objektorientierte Design spiegelt sich auch durch feine Unterschiede in der Syntax wieder. Um z. B. in Java eine Instanz von einer Klasse zu erzeugen, lautet der Aufruf: Date d = new Date(), in Ruby hingegen: d = Date.new. Die Syntax in Ruby ist etwas intuiti- ver, da sich der für die Instanzierung notwendige Code (in Java also der Konstruktor, in Ruby die Methode „initialize“) in der jeweiligen Klasse befindet und damit auf die Klasse zugegriffen wird.

Paradigmen

Die Entwicklung in Ruby folgt mehreren Paradigmen. Eines davon ist das Prinzips der geringsten Überraschung. Im Englischen bekannt als „Principle of least surprise“ (POLS) Das bedeutet, Ruby verhält sich genau so, wie man es intuitiv erwarten würde. Auf das Eingangsbeispiel bezogen: um ein Objekt einer Klasse zu erzeugen, muss ein bestimmter Code-Block dieser Klasse aufgerufen werden. Ein solcher Aufruf erfolgt erwartungsgemäß über Angabe der Klasse, gefolgt von einem Punkt und dem Methodennamen. Somit ist die Ruby-Syntax enger an das objektorientierte Design angelegt als die Syntax in Java.

Typisierung

Anhand des obigen Beispiels zeigt sich ein weiterer gravierender Unterschied. Beide Sprachen sind streng typisiert, doch erfolgt die Bestimmung des Datentyps in Ruby erst zur Laufzeit. Zunächst mag dieser Umstand als Nachteil wirken, da Zuweisungsfehler nicht zum Übersetzungszeitpunkt erkannt werden können. Bei genauerer Betrachtung bringt es aber auch Vorteile, weil z. B. ohne weiteres unterschiedliche Datentypen in einem Array gespeichert werden können.

Interpreter vs. Virtuellen Maschine

Diese Fähigkeit hat damit zu tun, dass Ruby eine Interpretersprache ist. Der Quellcode wird zur Laufzeit analysiert und ausgeführt. Das heißt: Ruby-Code kann ohne vorausgehende Kompilierung ausgeführt werden. Java hingegen geht einen eigenen Weg zwischen Compiler- und InterpreterSprache. Der Source-Code wird hier zuerst in Bytecode übersetzt, der auf einer Virtuellen Maschine ausgeführt wird. Erst die Virtuelle Maschine erzeugt den Maschinencode, der wiederum direkt vom Prozessor ausgeführt werden kann. Damit ergibt sich zwangsläufig, dass die Ausführungsgeschwindigkeit von Java-Anwendungen die von Ruby-Anwendungen übersteigt.

Vererbung

Betrachtet man die von den Programmiersprachen angebotenen Möglichkeiten der Vererbung, scheinen zunächst beide Sprachen nur den Weg der Einfachvererbung (single inheritance) anzubie- ten. Das heißt im Klartext, dass eine neu angelegte Klasse nur von einer anderen Klasse erben kann. Sie erhält so deren Funktionalität, aber kann dann von keiner zweiten Klasse mehr erben. Benötigt man weitere Funktionalität, bleibt einem in Java nur die Implementierung weiterer Interfa- ces. Das bedeutet hohen Programmieraufwand, da alle Methoden des Interfaces per Hand pro- grammiert werden müssen.

Doch nicht so in Ruby. Hier haben die Entwickler mehrere Möglichkeiten vorgesehen, Funktionalität in Klassen einzubauen. Gemäß der Einfachvererbung kann eine Klasse durch Angabe des KleinerOperators genau von einer anderen Klasse erben. Zur Veranschaulichung hier ein Beispiel anhand einer Basisklasse für Zahlen:

Abbildung in dieser Leseprobe nicht enthalten

Eine andere Klasse für sehr große Zahlen, im Folgenden BigInteger genannt, erbt die Funktionalität der Basisklasse:

Abbildung in dieser Leseprobe nicht enthalten

Zusätzlich kann eine Klasse um Funktionalität erweitert werden, indem sie sogenannte Module benutzt. Module sind Methodensammlungen, die Funktionalitäten implementieren. Dabei kann es sein, dass Module gewisse Annahmen über die Klasse machen, die das Modul einfügt. Bei solchen Annahmen kann es sich um zur Verfügung gestellte Variablen oder Methoden handeln, die existieren müssen, um eine erfolgreiche Ausführung zu gewährleisten. Das Modul kann in seinen Methoden wiederum beliebige Klassen benutzen.

Der folgende Code-Block zeigt, wie die Definition eines Moduls aussehen kann. In Anlehnung an die Klasse Math aus der Java-Standard-Library wird hier das Modul Math definiert, welches exem- plarisch die Methode add bereitstellt. Innerhalb der Methodendefinition verwendet das Modul die Klasse BigInteger.

Abbildung in dieser Leseprobe nicht enthalten

Ein weiteres Modul mit dem Namen Stringify bietet die Möglichkeit, Zahlen in ausgeschriebener Form auszugeben. Das Modul greift dabei auf die Instanzvariable @value der einbindenden Klasse zu und macht somit einen Annahme, die die einbindende Klasse erfüllen muss.

Abbildung in dieser Leseprobe nicht enthalten

Die Verantwortung, diese Erwartungen zu erfüllen, liegt bei der einfügenden Klasse. Die Einbettung der Methoden erfolgt durch Übernahme der entsprechenden Codestücke aus den Modulen in die Klasse. Die Platzierung des importierten Codes erfolgt an den, mit den Schlüsselwörtern include bzw. extend markierten Stellen in der Klasse. Das Schlüsselwort include wird benutzt, um die Funktionen des eingebundenen Moduls in der Klasse als Instanzmethoden zur Verfügung zu stel- len. Instanzmethoden können nur mit einem Objekt als Empfänger aufgerufen werden. Das Schlüs- selwort extend importiert hingegen Klassenmethoden. Diese können ohne Vorliegen eines Objekts direkt auf eine Klasse aufgerufen werden. Im folgenden Beispiel wird die zuvor eingeführte Klasse BigInteger um die Funktionalität der Module Math und Stringify erweitert:

Abbildung in dieser Leseprobe nicht enthalten

Eine Besonderheit beim Einbinden von Modulen mittels extend besteht darin, dass der Aufruf von extend auch zur Laufzeit auf ein Objekt erfolgen kann. Die Erweiterung beschränkt sich somit nur auf das eine Objekt.

Der folgende Programmcode zeigt die Benutzung der durch Vererbung und Benutzung von Modulen neu erworbenen Funktionalität. Zuerst wird ein Objekt vom Typ BigInteger erzeugt, auf der die Methode intValue aus der Basisklasse aufgerufen wird, um den aktuellen Wert auszugeben. Als zweites wird die Funktion add aus dem Modul Math als Klassenmethode aufgerufen. Im Anschluss daran wird der Wert der Variablen in ausgeschriebener Form ausgegeben. Dazu wird die Instanzmethode stringify aus dem gleichnamigen Modul benutzt. Als nächstes wird zur Laufzeit ein weiteres Modul zur Währungsformatierung definiert und in eine neue Instanz eines BigInteger Objekts eingebunden. Die somit herbeigeführte Situation soll den Gebrauch des Schlüsselworts extend verdeutlichen. Der Aufruf der neuen Funktion format auf beide BigInteger Instanzen zeigt, dass die Funktion nur der zweiten Instanz zur Verfügung steht:

Abbildung in dieser Leseprobe nicht enthalten

Implementiert eine neu angelegte Klasse zum Beispiel die, in Ruby fast allgegenwärtige Methode each, so erhält die neue Klasse durch den Aufruf von include Enumerable ungefähr 30 neue Methoden zum Durchlaufen, Suchen und Sortieren von Elementen. Das Erweitern der Funktionalität durch Verwendung von Modulen wird in Ruby „Mixin“ genannt. Der Vorteil von Mixins besteht in der einfacheren Struktur und Nachvollziehbarkeit des Programmcodes im Gegensatz zur Mehrfach- vererbung (vgl. [1]).

Fehlerbehandlung

Bei der professionellen Entwicklung von Software ist man sich im Klaren, dass während der Ausführung eines Programms Laufzeitfehler auftreten können. Es wäre naiv, davon auszugehen, dass alle Aufrufe erfolgreich verlaufen. Die wohl geläufigsten Fehlersituationen sind z. B. das Öffnen einer Datei oder der Abbruch einer Netzwerkverbindung.

Zur Veranschaulichung ein Beispiel in Pseudo-Code (vgl.[2]):

Abbildung in dieser Leseprobe nicht enthalten

Der vorgesehene Programmablauf gerät durch das Auftreten eines Fehlers aus der Bahn, so dass nachfolgende Befehle nicht ordnungsgemäß ausgeführt werden können. Deshalb ist es wichtig, sich bewusst zu sein, an welchen Stellen Fehler auftreten können und wie man sie behandeln kann. Die Komplexität der Fehlerbehandlung nimmt mit dem Umfang des Programms zu. In kurzen überschaubaren Programmen kann die Kontrolle des Rückgabewertes nach dem Aufruf einer Funktion ausreichen, um Fehler zu erkennen und den Aufrufer darüber zu informieren.

Abbildung in dieser Leseprobe nicht enthalten

Doch wird dieser Weg schnell unübersichtlich, wenn sich der Aufruf über mehrere Klassen und Objekte zieht. Aus diesem Grund ist es hilfreich, bei großen Projekten den Code zur Fehlerbehandlung vom „normalen“ Programmcode zu trennen.

Abbildung in dieser Leseprobe nicht enthalten

Beim Design der Programmiersprache Java waren sich die Entwickler dieser Problematik stets bewusst und bauten Fehlerbehandlungen mittels sogenannter Exceptions ein.

Exceptions sind Objekte, die generiert werden, sobald ein Ereignis eintritt, das den vorgesehenen Programmablauf stört. Das Objekt enthält eine Meldung, warum es zu diesem Ereignis gekommen ist, eine Auflistung der aufgerufenen Methoden bis zu diesem Ereignis und einen Typ dem das Ereignis zuzuordnen ist. Entlang der Aufrufhierarchie wird aufsteigend nach einer passenden Behandlung des aufgetretenen Fehlers gesucht. Kümmert sich niemand um den Fehler, kommt es zum Programmabbruch.

Bereits die Standardbibliotheken von Java bieten eine umfangreiche Exception-Sammlung an. Diese lassen sich beliebig erweitern und auf die speziellen Bedürfnisse der jeweiligen Anwendung anpas- sen. Fehler können damit an der Stelle, an der sie entstehen, mittels try-catch-finally-Anweisungen abgefangen und behandelt werden oder über den throw-Mechanismus an den Aufrufer weitergeben werden, so dass durch ihn geeignete Maßnahmen eingeleitet werden können.

Auch in Ruby wird auf das bewährte System der Exceptions gesetzt. Hier lautet die Anweisung begin, rescue, else, ensure und end. Mit begin wird ein Bereich eingeleitet, in dem Fehler auftreten können. Mit dem reservierten Wort rescue kann auf die verschiedenen Typen von Exceptions wie z. B. Syntaxerror, Standarderror, usw. eingegangen und entsprechende Fehlerbe- handlungen können ausgelöst werden. Das Schlüsselwort else kann verwendet werden, um einen Bereich zu definieren, der nur ausgeführt wird, falls bisher kein Fehler aufgetreten ist. Soll ein Stück Code auf jeden Fall ausgeführt werden, benutzt man das Schlüsselwort ensure als Pendant zu finally in Java.

Soll eine Exception weitergereicht werden, so benutzt man das reservierte Wort raise. Als Parameter können zusätzlich der Typ, eine Beschreibung des aufgetretenen Fehlers und das Objekt mit der Fehlerentstehungshistorie mitgegeben werden.

Zusätzlich bietet Ruby noch einen Catch-Throw-Mechanismus. Dabei wird mit catch (:identi- fier) ein Bereich eingeleitet, der bei einem Aufruf von throw :identifier innerhalb dieses Bereiches verlassen wird und bei einem Aufruf von throw :identifier außerhalb des Bereiches aufgerufen wird (vgl. [3]).

Ebenso wie in Java enthält die Standard-Library von Ruby ein ganzes Arsenal an Exception-Klassen für die verschiedenen Bereiche, in denen Fehler auftreten können.

Bl ö cke

Die Gruppierung von Quellcode in Blöcken dient auch in Ruby zur Kennzeichnung von zusammen- hängendem Programmcode. Sie schränken den Gültigkeitsbereich von lokalen Variablen ein und werden entweder durch geschweifte Klammern oder durch die Schlüsselworte begin und end defi- niert.

Doch sind Blöcke in Ruby weitaus flexibler einsetzbar als in Java. Zum einen können Code-Blöcke in Variablen gespeichert werden. Damit lassen sich Programmteile aufrufen, deren Gültigkeitsbe- reich zum Zeitpunkt des Aufrufs längst verlassen wurde. Dennoch können die zum Zeitpunkt der Deklaration erstellten lokalen Variablen verwendet werden. Um Code-Blöcke speichern zu können, werden so genannte Proc-Objekte generiert. Aufgabe dieser Objekte ist es, die Methode call bereitzustellen, mit deren Hilfe der gespeicherte Code-Block gegebenenfalls mit Parametern aufge- rufen werden kann. Zur Veranschaulichung wird im folgenden Beispiel (vgl. [4]) eine Funktion defi- niert, die ein Proc-Objekt erzeugt. Das Proc-Objekt enthält eine lokale Variable factor als Platzhal- ter, die eigentlich nach dem Verlassen des Code-Blocks ihre Gültigkeit verlieren würde. Im Anschluss wir diese Funktion zweimal aufgerufen und die beiden erzeugten Proc-Objekte in ver- schieden Variablen gespeichert. Mit dem Aufruf der Funktion call auf dem jeweiligen Proc-Objekt wird, wie im Beispiel ersichtlich, der ursprünglich zugewiesene Wert der lokalen Variable factor zur Berechnung des Ergebnisses benutzt.

Abbildung in dieser Leseprobe nicht enthalten

Des Weiteren lassen sich Blöcke als eine Art Parameter an Funktionen übergeben. Möchte man

z. B. die erste Zahl in einem Array finden, die sowohl durch fünf als auch durch sieben teilbar ist, kann der Aufruf so aussehen:

Abbildung in dieser Leseprobe nicht enthalten

Dabei wird die Funktion find mit dem Array-Objekt als Empfänger aufgerufen. Der Code-Block mit den verknüpften Rechenoperationen wird der Funktion beim Aufruf mitgeben. Zur Ausführung wird nun jedes Element des Arrays an den Platzhalter i übergeben und die Berechnung durchgeführt. Sobald der berechnete Wert des Ausdrucks true ergibt, wird die Funktion beendet. Ansonsten wird nil zurückgegeben (vgl.[5]).

Reflection

Eine weitere Eigenschaft, die beide Programmiersprachen aufweisen, ist die Möglichkeit der Reflec- tion (auch Introspektion genannt). Sprachen, die diese Technik bereitstellen, sind in der Lage, Erkenntnisse über ihre eigene Struktur zu gewinnen. Reflexion erlaubt, während der Laufzeit eines Programms festzustellen, welche Objekte gerade existieren, wie ihre Vererbungshierarchie aufge- baut ist, welche Methoden sie bereitstellen, wer welche Methode aufrufen darf (Sichtbarkeitsbe- reich), welchen Rückgabetyp eine Methode besitzt und welche Übergabeparameter eine Methode erwartet.

Beispiel in Ruby:

Abbildung in dieser Leseprobe nicht enthalten

Beispiel in Java:

Abbildung in dieser Leseprobe nicht enthalten

Um an diese Informationen zu gelangen, müssen Metainformationen über den Quellcode gesammelt werden, wozu eine Vielzahl von String-Vergleichen erforderlich sind, die die Performance bei exzessivem Einsatz von Reflexion beeinträchtigen kann (vgl.[6]).

Reflection ermöglicht viele Features, die zur schnelleren und komfortableren Programmierung bei- tragen. Um den Quellcode besser warten zu können und dem Programmierer eine Schnittstelle zur Anwendungsprogrammierung zur Verfügung zu stellen, bieten sowohl Java als auch Ruby die Mög- lichkeit, anhand der im Quellcode gemachten Kommentare ein „Application Programming Interface“ (API) automatisch generieren zu lassen. Die Informationen dafür werden über Reflexion ermittelt.

Ein weiteres Einsatzgebiet der Reflection sind Debugger und Test-Tools. Sie sind für die Entwicklungs- und Testphase wichtige Werkzeuge, um möglichst früh Programmierfehler zu finden. Reflexion wird hier z .B. verwendet, um Werte privater Variablen während einer Programmunterbrechung anzuzeigen (vgl.[7]).

Der für dieses Projekt wichtigste Grund, warum die einzusetzende Programmiersprache Reflection unterstützen muss, ist die relationale Abbildung von Objekten im Arbeitsspeicher auf die Daten- bank, wie sie im nächsten Kapitel beschrieben wird. Hier wird Reflexion benutzt, um den Aufbau von relationalen Tabellen anhand der Informationen über Objekt-Variablen zu automatisieren. Dazu sind neben den Objekt- und Variablennamen deren Datentypen und Zusatzinformationen im Quell- code wichtig (vgl. [8]).

Zwischen-Fazit

Sowohl Ruby- als auch Java-Programme können unabhängig vom Betriebssystem ausgeführt werden, sofern es für das Betriebssystem einen Interpreter bzw. eine Virtuelle Maschine gibt. Der vom Java Compiler erzeugte Bytecode ist für alle Rechnerarchitekturen gleich.

2.1.2.2 Vergleich der Frameworks

Der folgende Abschnitt behandelt die Vor- und Nachteile der Frameworks. Dabei wird die Erzeugung dynamischer HTML-Seiten, der Konfigurationsaufwand des Gesamtsystems und der Prozess der Auslieferung der Anwendung an die für den Betrieb vorgesehenen Server (im Folgenden Deployment genannt) im Vordergrund stehen.

Als erstes erfolgt die Darstellung des auf der Programmiersprache Java basierenden Frameworks JBoss, danach des auf der Programmiersprache Ruby basierenden Frameworks Ruby on Rails (kurz Rails).

JBoss Application-Server

Zunächst wird die Platzierung des JBoss Application-Servers im Technologieumfeld aufgezeigt. Danach werden die wichtigsten Komponenten des Frameworks erklärt.

Der JBoss Application-Server ist Bestandteil der JBoss Enterprise Application-Plattform und ist damit direkter Konkurrent zu kommerziellen Lösungen wie etwa „WebSphere“ von IBM oder „WebLogic“ von BEA. Der JBoss Application-Server bildet innerhalb der JBoss Enterprise Application-Plattform eine eigenständig lauffähige Einheit, deren Funktionalität für dieses Projekt ausreichend ist. Auf Grund der großen Community von JBoss und der daraus resultierenden schnellen Entwicklungen werden ständig Komponenten der Plattform entfernt und andere hinzugefügt (vgl. [9]).

Java 2 Enterprise Edition

Die JBoss Enterprise Application-Plattform ist eine Open-Source-Implementierung der Java 2 Enter- prise Edition-Spezifikation. Dabei handelt es sich um eine Sammlung koordinierter Spezifikationen und Verfahren, die zusammen die Entwicklung mehrschichtiger Server-Anwendungen ermöglichen.

Durch eine Umsetzung der J2EE-Spezifikation soll die Skalierung und das Management großer Anwendungen vereinfacht werden. Ist eine Ressource wie z. B. ein Webserver ausgelastet, lassen sich weitere Instanzen nachträglich starten und Anfragen über einen Load-Balancer verteilen (vgl. [10]).

Für das Projekt und das einzusetzende Framework sind folgende in der Spezifikation verwendete Technologien interessant:

- Java Servlets
- JavaServer Pages
- Java Naming and Directory Interface

Java Servlets

Java Servlets sind Java-Programme, die auf einem Webserver laufen. Sie haben keine grafische Oberfläche und können über eine definierte Abbildungsvorschrift (servlet-mapping) mit URL- Adressen angesprochen werden.

Java Servlets bieten die Funktionalität von CGI-Skripten und können dynamische Web-Inhalte gene- rieren, indem sie serverseitig Anfragen unterschiedlicher Clients (z. B. von Browsern) entgegenneh- men und verarbeiten. Hierfür stellt ein Servlet die Methode service(HttpServletRequest, HttpServletResponse) zur Verfügung. Diese Methode ermittelt den HTTP-Anfragetyp (meist GET oder POST) und ruft zur Bearbeitung der Anfrage die passende Methode auf. Danach sendet das Servlet die generierte Antwort zurück an den Client (vgl.[11]). Abbildung 2.2 veranschaulicht die Arbeitsweise eines Servlets.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 2.2: Arbeitsweise eines Servlets (Quelle:[12])

Der Benutzer gibt anwendungsspezifische Daten in ein Formular ein und sendet über seinen Brow- ser ein HTTP-GET oder -POST-Anfrage an den Webserver. Dieser ermittelt das für die Verarbeitung zuständige Servlet und übergibt die erhaltenen Daten. Das Servlet führt den zur Bearbeitung der Benutzeranfrage erforderlichen Programmcode aus und übermittelt das Ergebnis in Form einer HTML-Seite an den Webbrowser des Benutzers.

Da Servlets Java-Konstrukte sind, müssen sie in einem Kontext ausgeführt werden, der auf JavaTechnologie basiert. Das bedeutet: Servlets brauchen einen sogenannten Web-Container, in dem sie bei Bedarf erzeugt werden, einen Lebenszyklus (life-cycle) durchlaufen und mit ihrer Umgebung kommunizieren können. Bekannte Open-Source Web-Container sind z. B. die Projekte Jetty von Webtide und Tomcat von Apache (vgl.[13]).

JavaServer Pages

JavaServer Pages (JSP) gehören zu der Familie der serverseitigen Scriptsprachen, wie PHP, ASP und ColdFusion, mit dem Unterschied, dass sie selbst Servlets sind. Bevor eine JSP ausgeführt wird, wandelt der Servlet-Container die JSP in ein Servlet um, kompiliert es und führt es dann als Servlet aus.

Umgekehrt müssen die von Servlets generierten Daten, bevor sie an den anfragenden Client zurückgesendet werden, in HTML eingebettet werden. Diesen Vorgang übernehmen die JavaServer Pages, indem sie im statischen HTML Skelett einer Seite Bereiche für dynamisch erzeugten Inhalt definieren. Diese Stellen werden durch spezielle XML-Tags gekennzeichnet und mit dem Ergebnis der Anfrage ersetzt.

JSPs stellen einen einfachen Weg dar, grafische HTML-Elemente mit Java-Code zu mischen. Deshalb werden sie im Gegensatz zu Servlets meist dann verwendet, wenn das Layout im Vordergrund steht (vgl.[14]).

Java Naming and Directory

Das Konzept hinter dem Java Naming and Directory Interface (JNDI) ist die Auffindung von Objekten anhand eines für Menschen einfach zu merkenden und verständlichen Namens. Der Ansatz ist mit dem des Internet Domain Name System (DNS) oder eines Dateisystems vergleichbar. Während das Domain Name System dafür verantwortlich ist, einen Hostnamen auf eine IP-Adressen abzubilden, kann ein Dateisystem durch Angabe eines Dateinamens einen Dateideskriptor (File-Handle) anle- gen.

Der Vorteil beim Einsatz des Java Naming and Directory Interface besteht darin, dass Objekte über einen eindeutigen Namen auch über das Netzwerk in einem verteilten System auffindbar sind. Existiert ein solches Objekt nicht, kann es durch eine zentrale Kontrollstruktur erzeugt und angemeldet werden, so dass es zur Verfügung steht. Das bedeutet, dass nicht ständig alle Objekte geladen sein müssen, wodurch Arbeitsspeicher eingespart werden kann.

Der JBoss Application-Server macht exzessiven Gebrauch von dieser Technologie, um bestimmte Komponenten und Dienste zu laden.

Enterprise JavaBeans (EJB)

Sie sind ein weiterer Teil der J2EE Spezifikation mit der Intention, eine standardisierte und bewährte Lösung für Probleme, wie sie im unternehmerischen Bereich auftreten, zu bieten. Ihre Funktion ist die Kapselung der Geschäftslogik in Module, die unter der Verwaltung und Beobachtung des Application-Servers stehen. Durch Implementierung von Enterprise JavaBeans stellt der ApplicationServer Mechanismen bereit, zur Speicherung der Anwendungsdaten, zum Einsatz von Transaktionen, zur Abwicklung gleichzeitiger Ressourcenzugriffe, zur Behandlung von Ereignissen und zur Implementierung von Sicherheitsabfragen (vgl.[15]).

Es gibt im wesentlichen vier Arten von EnterpriseBeans, wie in Abbildung 2.3 zu erkennen ist:

- Entity Beans
- Session Beans
- Stateful Session Bean
- Stateless Session Bean

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 2.3: Enterprise JavaBeans

Jede Einheit der Geschäftslogik, die sich als Objekt modellieren lässt und einen Informationsgehalt speichert, wird als Entity Bean realisiert. Jedes Entity Bean definiert ein Interface mit Methoden, die nach außen hin sichtbar sind. Je nachdem, ob es sich bei dem aufrufenden Objekt um ein ent- ferntes oder lokales Objekt handelt, können weitere Vorkehrungen getroffen und Kriterien zur Ein- stellung der Sicherheit bedacht werden. Des Weiteren enthalten EntityBeans, sofern sie nicht flüch- tige Informationen enthalten, Zusatzinformationen (z. B. Annotations im Quellcode oder Regeln in einer XML-Datei), mit deren Hilfe das jeweilige EntityBean in einer Datenbank abgelegt werden kann. Damit übernehmen die EntityBeans die Persistierung der anfallenden Geschäftsdaten (vgl. [16]).

Session Beans

Die zweite Art von EnterpriseBeans, die so genannten Session Beans, repräsentieren einen anfra- genden Client innerhalb des Servers. Session Beans können in zwei Formen auftreten. Stateful Ses- sion Beans haben die Eigenschaft, sich Informationen, die während der Client-Anfragen entstehen, für die Dauer einer Verbindung zu merken. Daten, die in einer ersten Anfrage von einer Session Bean erzeugt wurden, stehen auch in der zweiten Anfrage desselben Clients zur Verfügung. Damit entspricht eine Session Bean dem, was man bei einer Webanwendung unter einer User-Session versteht.

Stateless Session Beans behalten Informationen nur für die Dauer eines Funktionsaufrufs bzw. einer Client-Anfrage. Ein Stateless Session Bean steht somit mehreren Clients gleichzeitig zur Verfügung. Damit sind sie z. B. für den Versand einer E-Mail oder dem einfachen Auslesen und Zurückliefern von Daten geeignet (vgl.[17]).

Dienste des JBoss Application-Server

Der JBoss Application-Server stellt noch wesentlich mehr Dienste zur Verfügung, die für dieses Projekt eventuell zu einem späteren Zeitpunkt interessant werden können. Abbildung 2.4 zeigt eine komplette Übersicht der im JBoss Application Server enthaltenen Dienste.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 2.4: JBoss Application Server (Quelle:[18])

Sicherheitsvorkehrungen

Um die Sicherheit des Servers nicht unnötig zu gefährden, sollten alle nicht benötigten Dienste deaktiviert werden. Richtig konfiguriert startet der JBoss Application-Server nur noch die für dieses Projekt wirklich benötigten Dienste. Dazu gehören Dienste wie JNDI , Remote Method Invocation (RMI), Apache Tomcat als Webserver, der Hibernate Persistance Manager und ein Pool mit Daten- bankverbindungen.

Konfiguration

Die Vielfalt der hier aufgezählten Technologien zeigt, dass hinter der Fassade des JBoss Application Server viele Technologien ineinander greifen. Dieses Zusammenspiel macht den Konfigurationsaufwand für den JBoss Application Server sehr hoch.

Der JBoss Application Server enthält mehrere Konfigurationsdateien. Zu den wichtigsten gehören:

- jndi.properties:

legt die Erzeuger-Klasse (Factory) und den Port für den Namensdienst fest.

- log4j.xml:

steuert das Format und den Empfänger bzw. den Ausgabeorte von Log-Meldungen.

- bindings.xml:

zentrale Stelle, an der die Ports für sämtliche Dienste festgelegt werden.

Da es bei Projekten dieser Größe nicht ausreicht, einzelne Dateien auf den Webserver zu spielen, wird das Projekt in Archive gepackt. Dabei entsteht ein Hauptarchiv mit der Bezeichnung „ear“.

Damit der Application-Server die neue Anwendung installieren kann, sind einige Metainformationen notwendig wie z. B. aus welchen Modulen (Unterarchiven) die J2EE Anwendung besteht. Diese Informationen werden über die Datei application.xml bereitgestellt.

Das Hauptarchiv enthält die komplette J2EE-Anwendung, die sich wiederum in zwei Unterarchive unterteilt, dem ejb-Archiv und dem war-Archiv. Ersteres enthält die Anwendungslogik und Persistenzklassen. Dazu muss das Archiv dem Application-Server mitteilen, wie auf die Datenbank zugegriffen und welcher Mechanismus dazu verwendet werden soll. Diese Informationen werden innerhalb dieses Archivs in der persistance.xml-Konfigurationsdatei geregelt. Das zweite Unterarchiv enthält die Web-Komponenten wie z. B. Bilder, Servlets und JSPs. Auch hier müssen Informationen für den Application-Server bereitgestellt werden, damit ein Aufruf der Servlets über eine URL erfolgen kann. Diese Abbildungsdaten (Servlet-Mapping) werden über die web.xml Datei im war-Archiv bereitgestellt. Der Archivaufbau hat folgendes Schema:

- ear-Archiv (Hauptarchiv)

Konfigurationsdatei: application.xml

- ejb-Archiv (Persistenzklassen):

Konfigurationsdatei: persistance.xml

- war-Archiv (Web-Komponenten): Konfigurationsdatei: web.xml

Um den Vorgang des Deployments zu automatisieren, werden so genannte Ant-Files benutzt. Ähnlich einem Makefile in der Programmiersprache C enthält diese Datei ein in XML-Syntax festgelegtes Regelwerk von Anweisungen, die erforderlich sind, um

- die Umgebung von der letzten Version zu säubern,
- eine Ausgangssituation mit allen benötigten Verzeichnissen und Dateien wiederherzustellen,
- den Quellcode zu übersetzen,
- die erforderlichen Archive zu erstellen und
- das Archiv an der richtigen Stelle im Server zu installieren.

Hot Deployment

Jedes neue oder veränderte Archiv wird durch zyklische Abfragen (polling) automatisch vom JBoss Application-Server erkannt. Die entsprechenden Dateien werden neu geladen, so dass die Änderungen ohne Neustart des Servers wirksam werden (vgl.[19]).

Ruby on Rails

Im Folgenden wird das auf der Programmiersprache Ruby basierende Framework Ruby on Rails (kurz Rails) vorgestellt. Analog zum vorherigen Kapitel werden die einzelnen Komponenten des Frameworks erklärt und es wird auf den Konfigurationsaufwand und die Erzeugung dynamischer Webseiten eingegangen. Abschließend wird das Deployment der Anwendung noch betrachtet.

Rails ist ein Framework, das nicht aus Überlegungen und Theorie entstanden ist, sondern aus einem praktischen Projekt (http://www.basecamphq.com/) extrahiert wurde. Rails ist darauf ausge- legt mit seinen Werkzeugen Datenbank-basierte Webanwendungen zu entwickeln. Dabei hält sich das Framework an folgende Paradigmen:

- Model View Controller

Rails stellt für jede der Schichten Model, View und Controll eine Komponente bereit. Das Domain Model einer Anwendung wird mit ActiveRecord abgebildet, es kümmert sich um die Persistierung der Anwendungsdaten. Die Komponente ActionView ist verantwortlich für die grafische Darstellung und der ActionController implementiert die Geschäftslogik. Die Einhaltung dieses Prinzips führt zur Entkoppelung bzw. Kapselung dieser Logischen Einheiten, so dass die einzelnen Komponenten später leichter austauschbar sind.

-Konvention über Konfiguration

Die Einhaltung von Konventionen erspart dem Rails-Programmierer eine Menge Konfigurationsaufwand. So werden z. B. Datenbanktabellen in pluralisierter Form nach den Modell-Klassen von ActiveRecord benannt. Als weiteres Beispiel werden eingehende HTTP- Anfragen nach dem Schema: „Contoller-Name, Action-Name, Id“ zerlegt und damit die aufzurufende Methode in der Geschäftslogik bestimmt.

-DRY-Prinzip

Die Abkürzung DRY steht für „don't repeat yourself“ und wurde entwickelt, um die Komplexität von Programmen zu vereinfachen, Änderungen leichter durchführen zu können und inkonsistente Zustände zu verhindern. DRY besagt, „[...] dass Wissen nur eine einzige, eindeutige Repräsentation in einem System hat. Weder Daten noch Funktionalität sollten redundant vorkommen, da anderenfalls der Wartungsaufwand beträchtlich erhöht wird.“ ([20], 8)

-CRUD durch Scaffolding

Rails bietet die Möglichkeit, ein Gerüst für die Anwendung automatisch erzeugen zu lassen (englisch: scaffolding). Dabei werden für jede Entität im Domain Model vier grundlegende Funktionen bereitgestellt, deren Anfangsbuchstaben die Abkürzung des Paradigmas bilden:

- Create:

erzeugt ein neues Objekt

- Read:

liest ein gespeichertes Objekt ein

- Update:

ändert einzelne Attribute eines Objekts

- Delete:

löscht ein Objekt Somit lässt sich in kurzer Zeit ein ablauffähiger Prototyp einer Anwendung entwickeln.

Grundlagen

Der Ablauf einer typischen Client-Anfrage ist in der Abbildung 2.5 dargestellt. Dabei sendet ein Cli- ent einen Request an den HTTP-Server. Der Server leitet den Aufruf an den sogenannten Dispatcher weiter. Dieser entscheidet, welcher ActionController dafür zuständig ist und erstellt eine neue Instanz des Controllers.

Diese Instanz repräsentiert, vergleichbar mit einer Java Session Bean, den Client auf der Server-Sei- te. Der ActionController führt die mit ihm verknüpfte Geschäftslogik aus, instanziiert verschiedene Objekte und kommuniziert gegebenenfalls mit anderen Modell-Instanzen von ActiveRecord.

Das aus dem Aufruf hervorgehende Ergebnis leitet der Controller zur Darstellung weiter an die dafür zuständige ActionView. Besteht die Antwort aus einer zu generierenden HTML-Seite, kommen verschieden HTML Templates zum Einsatz. Handelt es sich bei der Anfrage um einen Web-Service, erzeugt das Modul Action Web Service ein XML-Dokument.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 2.5: Bestandteile und Zusammenspiel (vgl.[20], 10)

Die Generierung von HTML-Seiten wird durch umfangreiche Bibliotheken und Helfer-Klassen verein- facht. Neben der Erzeugung verschiedenster Formulare unterstützt Rails auch das komplette Proto- type Java-Script Framework, so dass Web 2.0 Anwendungen auf Ajax-basis realisiert werden kön- nen.

Deployment

Die Rails-Entwicklungsumgebung sieht drei Umgebungen mit jeweils eigener Datenbank und einer Konfigurationsdatei für die Anwendungsentwicklung vor. Die drei Umgebungen sind:

- Entwicklungsumgebung
- Testumgebung
- Umgebung für den Live-Betrieb

Während der Entwicklungsphase wird die Anwendung für jede Anfrage neu geladen, damit sich Änderungen im Quellcode sofort auswirken. Zudem werden so viel Informationen wie möglich über Log-Dateien, Konsole und Web-Browser ausgegeben, um den Programmierer mit möglichst vielen Details und Informationen zu versorgen. Als Webserver wird WEBrick verwendet, ein in Ruby geschriebener Server mit Debugmodus.

Der Testbetrieb ist auf Performanz ausgelegt. Alle Testsituationen werden dabei durchgespielt und künstlich Last erzeugt, damit die Grenzen und Engstellen der Anwendung aufgezeigt werden. Als Webserver ist dergleiche wie im Live-Betrieb verwendet. Zur Auswahl stehen hier die Webserver Apache oder LightTPD.

Im Live-Betrieb wird die Anwendung nur beim Start einmalig geladen. Die Einstellungen sind so gewählt, dass die Anwendung möglichst stabil und schnell läuft. Mögliche interne Fehler werden vor dem Anwender durch allgemeine Fehlermeldungen verborgen. Außerdem werden bei der Konfiguration der Servers Sicherheitsvorkehrungen getroffen (vgl.[20], 291).

Für das Deployment muss die Live-Umgebung vom Entwicklungsrechner auf den Server, der die Anwendung über das Internet verfügbar macht, übertragen werden. Da es sich bei Ruby um eine Interpretersprache handelt, entfällt der Compiliervorgang. Anders als in Java werden auch keine Archive erstellt. Rails benutzt für den Deploymentvorgang das Programm Capistrano. Es basiert auf der automatisierten Benutzung folgender Programme:

- ein Revision Control System (RCS) z. B. Subversion (SVN)
- eine gesicherte Verbindung mit Secure Shell (SSH)

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 2.6: Capistrano - Produktionsumgebung (Quelle:[20], 304)

Das Zusammenspiel und die Automatisierung wird durch eine entsprechende Konfiguration von Capistrano erreicht. Das RCS wird benutzt, um die Entwicklungsarbeit mehrerer Entwickler auf einem zentralen Rechner abzulegen und mit verschiedenen Versionen zu versehen. Capistrano kann nun den Server mit der Live-Umgebung dazu veranlassen, sich den neuesten Stand aus die- sem Repository zu besorgen. Um nun verschiedene Dienste wie z. B. den Webserver nach Änderun- gen am Dateisystem über das Netzwerk neu zu starten, wird das SSH Kommando von Capistrano benutzt.

Datenhaltung

Die Datenhaltung ist in diesem Projekt von zentraler Bedeutung. Benutzerspezifische Daten müs- sen mit möglichst geringer Latenzzeit verfügbar sein. Beispielsweise sollen Abläufe in der Auftrags- bearbeitung später nachvollziehbar sein. Dazu müssen Mitarbeiter identifiziert und Rechte zuge- ordnet werden. Kunden haben Präferenzen, Warenkörbe und Bestellungen, die sie einsehen und gegebenenfalls ändern möchten. Eine große Menge an Zuordnungen entsteht durch Zuweisungen von Produktsortimenten an Filialen. Informationen dieser Art müssen jederzeit bereitstehen. Da es nicht möglich sein wird, diese auftretenden Datenmengen im Arbeitsspeicher zu halten, wird es zum massiven Austausch von Daten zwischen flüchtigem Speicher und Festplatte kommen.

Bereits in der Vergangenheit haben sich für genau diese Problemstellung relationale Datenbanken bewährt. Die Daten werden dauerhaft gespeichert und mit Hilfe von SQL lassen sich Informationen hinzufügen, suchen, löschen, ändern und speichern. Datenbanken sind dank ihrer ständigen Weiterentwicklung ein ausgereiftes Werkzeug zur Manipulation von Daten. Selbst Abfragen von mehreren 100 000 Datensätzen können oftmals noch im Millisekundenbereich erfolgen.

Mit der Kapselung von Datenbankzugriffen in Transaktionen sind die Datenbestände selbst beim Abbruch von Operationen in einem konsistenten Zustand, da alle bis dahin erfolgten Änderungen rückgängig gemacht werden können bzw. Änderungen erst gültig werden, sobald die letzte Operati- on erfolgreich beendet wurde. Selbst für den gleichzeitigen Zugriff vieler Benutzer auf eine Res- source stehen Mechanismen wie optimistisches Locking bereit.

Allerdings basiert der Aufbau relationaler Datenbanken nicht auf der Objektorientiertheit, sondern der relationalen Algebra. Das bedeutet: relationale Datenbanken implementieren Operationen (Mengenoperationen, kartesisches Produkt, Division, Projektion, Selektion, Joins, usw.), um Relatio- nen miteinander zu verknüpfen oder zu redzieren und komplexe Informationen daraus herzuleiten.

Die Zerlegung der Objekte in ihre Attribute und anschließende Formulierung in SQL-Statements zur Speicherung stellt einereits einen Stilbruch der Objektorientierten Programmierung dar und ist andererseits extrem aufwändig.

Objektrelationale Abbildung

Genau hier setzt die objektrelationale Abbildung an. ORM-Frameworks bieten Erweiterungen für Klassen, die auf die Datenbank abgebildet werden sollen. Es werden Funktionen zum Suchen, Erstellen, Ändern und Löschen dieser Objekte innerhalb der Datenbank bereitgestellt. Existiert bereits ein Datenbankschema, so kann das Framework einen Teil der für die objektorientierte Klasse benötigten Informationen aus den vorhandenen Deklarationen des Datenbankmodells ziehen, wie z. B. Variablen und deren Datentypen.

Umgekehrt kann mit Hilfe von Reflection (siehe Kapitel 2.1.2.1 Reflection) aus einem bestehenden Klassenmodell ein Teil des Datenbankschemas erstellt werden, indem Variablen und Datentypen der Klasse ausgelesen werden.

Informationen bezüglich der Beziehungen unter den Objekten, wie beispielsweise die Kardinalität zwischen Kunde und Auftrag, können von den Frameworks nicht generiert werden. Diese Information ist durch den Programmierer hinzuzufügen.

Die beiden objektrelationalen-Mapper im Vergleich

Beide Frameworks benutzen eine objektrelationale Abbildung (ORM), um Objekte in einer relationalen Datenbank abzulegen und wieder zu laden.

Objektorientierte Programmiersprachen bilden bei diesem Vorgang ihre Klassen auf Tabellen ab, Attribute einer Klasse auf die Spalten.

Jeder Datensatz einer Tabelle entspricht somit einem Objekt. Auch Vererbungshierarchien und Beziehungsstrukturen lassen sich mit auf relationale Datenbanken abbilden.

Der Vorteil der objektrelationalen Abbildung wird dann ersichtlich, wenn Objekte aus dem Arbeitsspeicher dauerhaft auf ein Medium gespeichert und später wieder geladen werden sollen. Verwendet man hierfür einfache SQL-Statements, sind diese für jedes Objekt individuell zu erstellen. Je nachdem, wie viele Attribute, welche Vererbungshierarchie und welches Beziehungsgeflecht die einzelnen Objekte aufweisen, variiert die Komplexität und der Aufwand, der nötig ist, um die Informationen auf ein relationales System abzubilden.

Hibernate vs. ActiveRecord

Der JBoss Application Server setzt Hibernate als ORM ein. Dabei handelt es sich um ein bewährtes Open-Source-Projekt. Als Pendant benutzt Ruby on Rails ActiveRecord, um seine Objekte auf die Datenbank abzubilden. Die Ansätze der beiden Produkte sind jedoch unterschiedlich.

Ansatz von Hibernate

Mit Java und Hibernate erfolgt zuerst die Entwicklung der Objekte unter Berücksichtigung der zusätzlich für Hibernate bereitzustellenden Informationen bezüglich der zu erzeugenden Daten- bankstruktur. Diese Informationen werden in Form von Metadaten, sogenannten Annotations im Java-Quellcode deklariert. Erst mit dem Fortschritt der Implementierung der Geschäftslogik wächst das zugehörige Datenbankmodell.

Das verwendete Pattern lautet „Data Mapper“ und stellt mehrere Abstraktionsschichten zwischen Arbeitsspeicher und Datenbank für das Mapping bereit. Die zu verwaltenden Objekte haben durch die Abstraktion keinerlei Wissen von dem zugrunde liegendem Datenbankschema und brauchen kein SQL-Interface zu implementieren. Hibernate benutzt zum Transfer der Daten die Enterprise JavaBeans Query Language (EJB QL). Sie bietet neben der Funktionalität von SQL eine erweiterte Syntax für die Navigation zwischen den in Beziehungen stehenden Objekten.

Soll Hibernate im Nachhinein auf ein bestehendes Projekt mit bereits existierendem Dantebankschema angewendet werden, gibt es Tools für die umgekehrte Entwicklung (reverse engineering). Der JBoss Application-Server bietet dazu das Seam Freamework an, welches die Datenbanktabellen analysiert und Java-Klassen mit Annotations generiert.

Ansatz von ActiveRecord

In Rails und ActiveRecord wird von Beginn an mit sogenannten Migrations-Scripten gearbeitet, die das Datenbankschema erzeugen. Diese Scripte werden den ganzen Entwicklungszyklus über gepflegt. Jede Änderung in einer der verwendeten Tabellen wird im zugehörigem Migrations-Script vorgenommen. Damit lassen sich alle Änderungen nachvollziehen, aber auch wieder rückgängig machen. Diese Eigenschaft stellt sich als sehr nützlich heraus, sobald es um das Deployment der Anwendung geht. Anhand der Migrations-Scripte kann der Abgleich der Entwicklungs-Datenbank mit der Produktiv-Datenbank automatisiert erfolgen und gegebenenfalls auch wieder rückgängig gemacht werden.

ActiveRecord erstellt automatisch aus den Datenbanktabellen die zugehörigen Ruby Klassen und verwendet dabei das zum ORM gleichnamige Pattern, ActiveRecord. Der Entwickler muss lediglich in den von ActiveRecord erstellten Klassen die Beziehungen zu anderen Objekten eintragen.

Während Hibernate die Abbildungsregeln für die zu verwaltenden Objekte entweder als Java-Anno- tations im Quellcode oder gesondert als XML-Datei erwartet, versucht ActiveRecord diese Informationen mit Hilfe von datenbankspezifischen Adaptern aus der Datenbank zu holen. ActiveRecord wertet dazu die vorhandenen Metainformationen der Tabellen über die Datenbank aus. ActiveRecord erwartet lediglich die Information zu den Beziehungstypen. Dazu ein Beispiel für die Modellierung der Entität „Lebensmittel-Einzelhandels-Gruppe“:

ActiveRecord-Persistenzklasse:

Abbildung in dieser Leseprobe nicht enthalten

Hibernate-Persistenzklasse:

Abbildung in dieser Leseprobe nicht enthalten

Aus diesem Beispiel wird ersichtlich, dass der Implementierungsaufwand für den Programmierer bei ActiveRecord wesentlich geringer ist. Die Methoden zum Lesen und Setzen der einzelnen Attri- bute werden von ActiveRecord implizit erzeugt und können als Instanzmethoden verwendet wer- den.

2.1.3 Fazit

Nachdem nun die wichtigsten Aspekte für beide Sprachen betrachtet wurden, zeichnet sich Ruby durch eine umfangreichere Grundfunktionalität der Sprache an sich aus.

Während beide Sprachen weitgehend ähnliche Lösungen für Zugriffskontrolle, Fehlerbehandlung und Reflection bieten, liegen die Vorteile in der Parameter-Flexibilität und bei der Vererbung von Funktionalität bei Ruby.

Übergabeparameter

In Ruby können nicht nur Anzahl der Übergabeparameter variieren, sondern auch ein Defaulttyp gesetzt werden, falls kein Parameter übergeben wird. Übergabeparameter müssen nicht zwingend Variablen sein, es können auch ganze Code-Blöcke mitgegeben werden.

Vererbung

Ruby bietet außerdem eine umfangreichere Unterstützung (mixins), um Vererbungshierarchien abzubilden. Dadurch stehen in Ruby die Vorteile der Mehrfachvererbung zur Verfügung. In der Programmierpraxis bedeutet das eine Reduzierung des Implementierungsaufwands.

Effizienz

Vergleicht man den Umfang von Ruby-Programmen mit den von Java-Programmen, so zeigt sich, dass mit Ruby Anwendungslogik mit weniger Zeilen Quellcode ausgedrückt werden kann als mit Java. Die Vorteile liegen in der besseren Wartbarkeit der Quellcodes und der Tatsache, dass weniger Quellcode weniger Fehler beinhaltet.

Gebrauch

Die konsequentere Einhaltung des objektorientierten Ansatzes in der Programmiersprache Ruby ermöglicht dem Programmierer noch nicht bekannter Sprachkonstrukte eine intuitivere und damit einfachere Verwendung.

Da Sprachkonstrukte immer so verwendet werden können wie man es im objektorientierten Stil erwartet, fällt es dem Programmierer leichter, neue Module und Funktionen auf Anhieb richtig zu verwenden.

Performance

Ein Nachteil von Ruby, der allerdings allen Interpretersprachen anhaftet, ist die langsamere Ausfüh- rungsgeschwindigkeit gegenüber Compiler-Sprachen. Doch zeigt ein Vergleich der für dieses Projekt prognostizierten Benutzerzahlen mit Benutzerzahlen aus aktuell erfolgreichen Ruby-Anwendungen, wie etwa Twitter (www.twitter.com), dass die Ausführungsgeschwindigkeit von Ruby-Code keinen Flaschenhals darstellt und damit kein begrenzender Faktor sein wird.

Selbst bei der Annahme, dass

Angenommen es würden ca. 1,25 Prozent der deutschen Bevölkerung täglich ihre Nahrungsmittel über das Internet bei dem hier dargestellten System bestellen, oder zumindest die dafür benötigte Internetseite aufrufen, so wären das ungefähr eine Millionen Benutzer pro Tag. Wenn nun all diese Anfragen innerhalb einer Stunde eintreffen würden, müssten im Durchschnitt ca. 300 Anfragen pro Sekunde bearbeitet werden.

Im Vergleich dazu Seitenabrufe bekannter Internetgrößen (vgl.[21]):

Abbildung in dieser Leseprobe nicht enthalten

Das soziale Netzwerk Twitter ist aktuell einer der wohl bekanntesten Mikro-Blogging-Dienstanbieter, der Ruby und das darauf aufsetzende Framework Rails benutzt. Twitter bewältigt ca. 11.000 Seitenabrufe pro Sekunde. Im Vergleich dazu ist die Verarbeitung von 300 Seitenabrufen pro Sekunde für Ruby kein Problem. Damit gibt es von der Performance her keinen Grund, der gegen den Einsatz von Ruby als Programmiersprache spricht.

Deployment

Im Vergleich zum Deploment mit JBoss sind bei Rails wesentlich weniger Konfigurationsschritte notwendig. Rails bietet für das automatisierte Deployment das Programm Capistrano, das selbst in Ruby geschrieben ist und eine umfangreiche Sammlung von Deployment-Aufgaben beinhaltet.

Die Konfiguration von Capistrano ist in wenigen Schritten abgeschlossen. Capistrano erfordert lediglich das Einrichten des SVN-Systems und eines SSH-Zugangs mit Benutzername und Passwort sowie die Angabe des Hosts, auf dem die Anwendung ausgerollt werden soll.

Auf der Seite von JBoss ist der Aufwand zum Erstellen eines Ant-Scripts bzw. des dazugehörigen Build-Files um ein Vielfaches aufwändiger. Das Deployment umfasst mehr Schritte als bei einer Rails-Anwendung. Eine J2EE-Anwendung erfordert Kompilierung des Quellcodes, Erstellung des WebArchivs und Erstellung des J2EE-Archivs. Erst nach der erfolgreichen Erstellung der Archive folgt der Kopiervorgang in das entsprechende Server-Verzeichnis.

Die Unterstützung, den Quellcode auf einen entfernten Rechner zu kopieren und den Quellcode durch eine Versionsverwaltung zu organisieren, fehlt bei Ant gänzlich.

Ein wesentlicher Unterschied der beiden Frameworks, der hier besonders hervorgehoben werden soll, ist die Komplexität der objektrelationalen Abbildung der Anwendungsdaten. Um den jeweiligen Mechanismen der objektorientierten Datenhaltung in der Persistenzschicht zu realisieren, ist der zu betreibende Aufwand in den beiden Frameworks sehr unterschiedlich.

Objektrelationale Mapper

Beide Frameworks laden zunächst alle Daten „lazy“, das bedeutet, wenn einem Kunden ein Auftrag zugeordnet ist und das Kundenobjekt aus der Datenbank geholt wird, so bleibt die Verknüpfung zum Auftrag solange uninitialisiert, bis das Kundenobjekt zum ersten Mal auf das Auftrags-Objekt zugreift. Mit „eager loading“ hingegen werden alle mit einem Objekt in Verbindung stehenden Daten zu Beginn geladen, egal ob jemals auf diese Daten zugegriffen wird. Für das „lazy-“ bzw. „eager loading“ stellt Hibernate im Vergleich zu ActiveRecord ausgefeiltere Einstellungsmöglichkei- ten zur Verfügung.

Dafür ist der Aufwand für die Einführung eines ORMs bei bei ActiveRecord wesentlich geringer, da die Konfiguration im Vergleich zu Hibernate wesentlich einfacher ist. ActiveRecord bleibt dem DRYAnsatz treu und vermeidet die doppelte Deklaration von Informationen im Datenbankmodell und im Quellcode der Anwendung. Hibernate hingegen legt Informationen wie z. B. Datentypen von Attributen doppelt ab, einmal in der Datenbank und einmal im Source-Code.

Um sich bei ActiveRecord im Quellcode komplexer Domain-Modelle zurechtzufinden, ist die Einsicht in die Datenbankendefinitionen unerlässlich, da das Domain Model nur die Angaben über die Kardinalitäten enthält. Auf dem Gebiet des Caching bietet Hibernate aufgrund des Data Mapper-Pattern mehr Einstellungsmöglichleiten, als Rails.

Im Hinblick auf das umfangreiche Projekt und auf den dazu verfügbaren zeitlichen. Rahmen ist die schnellere Entwicklungsgeschwindigkeit von Rails mit ActiveRecord Hibernate vorzuziehen.

Ergebnis

Die Auswertung der betrachteten Aspekte in Hinblick auf Programmiersprache und Framework ergibt einen bessere Eignung von Ruby on Rails zur Umsetzung des Vorhabens.

2.2 Einblick in den Lebensmitteleinzelhandel (LEH)

Dieses Kapitel gibt einen Einblick in die Lebensmittelbranche und deren Strukturen. Dabei wird auf die aktuelle Entwicklung von Online-Shopping und im speziellen auf die Entwicklung des Online- Shoppings für Lebensmittel eingegangen. Anhand einer ausführlichen Umwelt- und Branchenanaly- se wird überprüft, ob die Voraussetzungen für den Online-Handel mit Lebensmitteln in Deutschland erfüllt sind.

Im Wesentlichen ist die Aufgabe des Handels, den Austausch von Gütern zwischen Konsumenten und Hersteller zu übernehmen. Die weiteren Aufgaben des Handels lassen sich in einer Wertschöpfungskette nach Falcoianu [22] detailliert darstellen (siehe Abbildung 2.7).

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 2.7: Wertsch ö pfungskette des Handels (Quelle:[22])

2.2.1 Struktur des Lebensmitteleinzelhandels

Durch unterschiedliche Erhebungsmethoden und Abgrenzungen der Marktforschungsunternehmen ergeben sich sehr unterschiedliche Umsätze. Für das Jahr 2006 wurden in der deutschen Lebensmittelbranche folgende Umsätze festgestellt.

Abbildung in dieser Leseprobe nicht enthalten

Tabelle 2.1: Umsatzzahlen im LEH 2006

Zur näheren Betrachtung des Umsatzes werden nachfolgend die Werte von A.C. Nielsen [26] ver- wendet.

Die auf dem deutschen Markt vorhandenen Lebensmitteleinzelhändler lassen sich nach dem Marktforschungsinstitut A.C. Nielsen (vgl.[26], 73) in folgende Geschäftstypen untergliedern:

Abbildung in dieser Leseprobe nicht enthalten

Tabelle 2.2 : Vertriebsformen LEH (vgl.[26], 73)

Der erzielte Gesamtumsatz von 126,3 Milliarden Euro wurde wie folgt durch die unterschiedlichen Geschäftstypen erzielt.

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 2.8 : Marktanteil der Gesch ä ftstypen ([26], 14)

2.2.2 Unternehmen auf dem deutschen Markt

Auf dem deutschen Lebensmittelmarkt ist folgende Marktstruktur bezüglich der vorhandenen Unternehmen und Unternehmensgruppen vorhanden. ([26]- Seite 9,[27],[28])

Abbildung in dieser Leseprobe nicht enthalten

Die folgende Darstellung zeigt die Aufteilung des Gesamtumsatzes auf die einzelnen Unternehmensgruppen. Als Grundlage dafür wurden die Zahlen von A.C. Nielsen (vgl. [26], 12) verwendet. Aus den Daten von A.C. Nielsen wurden die Gruppen „Restliche Geschäfte“ und „Markant“ zusammengefasst und dargestellt. Aus der Gruppe „Restliche Geschäfte“ wurden Norma und Lidl rausgerechnet und in der Grafik separat aufgeführt.1 2

Abbildung in dieser Leseprobe nicht enthalten

Abbildung 2.9 : Ums ä tze nach Unternehmensgruppen

2.2.3 Die Entwicklung des Online-Shoppings im Lebensmitteleinzelhandel

2.2.3.1 Eignung von Lebensmitteln für das Online-Shopping

Zu Beginn muss festgestellt werden, ob das Produkt „Lebensmittel“ überhaupt für den Onlinehandel geeignet ist. Laut Falcoianu (vgl.[22], 14/15) sind Lebensmittel durch die geringe Produktkomplexität und die rationale Produktbindung für das Online-Shopping gut geeignet. Auch durch die hohe Einkaufsfrequenz von Lebensmitteln, die eine „Routineaufgabe“ darstellt, sind diese für Online-Shopping wie geschaffen.

[...]


1 Edeka hat laut Lebensmittel Zeitung[29] einen Anteil von 70 % an Plus übernommen. Zum jetzigen Zeitpunkt wird die Übernahme noch durch das Kartellamt geprüft

2 Die 245 Extra-Märkte der Metro Group gehen mit Wirkung zum 1. Juli 2008 an die Rewe Gruppe (vgl. [30]).

Details

Seiten
122
Jahr
2008
ISBN (eBook)
9783668329355
Dateigröße
2.4 MB
Sprache
Deutsch
Katalognummer
v118980
Institution / Hochschule
Fachhochschule Regensburg
Note
1
Schlagworte
Entwicklung Webshops Lebensmittelbranche Basis Ruby Rails

Autoren

Teilen

Zurück

Titel: Entwicklung eines Webshops für die Lebensmittelbranche auf Basis von Ruby on Rails