shopping24 tech blog

s is for shopping

April 12, 2016 / by Robert Braatz / Web developer / @webcodecs

Laravel und wir

In den letzten Wochen und Monaten hat sich bei uns in der Webentwicklung einiges geändert. Grunt ist Geschichte, PHPUnit-Tests und javascript-Tests werden konsequent geschrieben und ein vernünftiges Monitoring wurde eingerichtet. Über die entscheidenste Änderung möchte ich ein paar Zeilen loswerden. Darf ich vorstellen? Unsere zwei neuen, besten Freunde Laravel und Artisan.

#Warum Laravel?

Irgendwann vor einem halben Jahr waren wir soweit, dass wir etwas Neues ausprobieren wollten und vielleicht auch mussten. Eines unser Projekte war in dem damaligen Zustand kaum noch wartbar. Tests dafür zu schreiben war die größte Qual und verstanden hat man das komplette Ökosystem der Applikation sowieso schon lange nicht mehr. Das Projekt beruhte auf dem Micro-Framework Slim. Slim an sich, ist nicht schlecht, aber auch nicht komfortabel. Es ist schnell, sehr schnell, aber kann eben auch nicht wirklich viel. Vielleicht hätte man die Neuentwicklung des Projektes auch auf Slim durchführen können. Das wäre aber nichts Neues gewesen. Also haben wir unsere Köpfe zusammengesteckt und nach einem geeignetem Nachfolger gesucht.

Die Google Trends, Facebook Feeds und Stackoverflow Beiträge sorgten dafür dass wir von Laravel hörten. Beim ersten Besuch der offiziellen Seite konnte man sofort folgende Worte lesen:

clean code

Schöner Code? Kunsthandwerker im Web? Hört sich gut an! Nur so viel zum allersten Eindruck. Der wiegt bekannter Maßen ja schwer. Die Community ist groß. Das Framework war und ist in aller Munde und so dachten wir, wird es sich lohnen auf den Zug aufzuspringen. Nach dem oberflächlichen Studieren der Beispiele und der Dokumentation, konnten wir für uns feststellen - ja sehr schöner Code. Noch ein, zwei Meinungen von anderen Entwicklern eingeholt und die Entscheidung war gefallen.

#Patient 0

Das erste Projekt sollte die API für unser Widget werden. Das Widget soll Produkte anzeigen. Diese Produkte bekommen wir von unserer Produkt-Api. Dem aufmerksamen Beobachter fällt auf: die Key-Features die Laravel mit Eloquent und der Daten-Abstraction bietet fallen somit quasi weg. Trotzdem wollten wir es mit Laravel versuchen.

Das Aufsetzen von Laravel ist denkbar einfach. Den Laravel-Installer via composer installieren und ein $ laravel new project-name ausführen. Die Applikation wird gecraftet und ist einsatzbereit.

###Redis als Datenablage

Unser Produkt, die API für unser Widget, soll Produkte und ein paar Zusatzinformationen für unser Javascript liefern. Welche Produkte, die Menge der Produkte und andere Einstellungen liegen bei uns in einer Postgres Datenbank. Uns stellte sich die Frage: Sollen wir diese Informationen wie bei der alten Version des Widgets direkt aus der Datenbank holen, oder speichern wir diese Daten zwischen um die Latenz und die direkte Abhängigkeit zu einer Datenbank zu vermeiden?
Wir haben uns für den zweiten Weg entschieden und wollten sowohl die Daten aus der Postgres-Datenbank als auch die Cache-Daten in Redis ablegen. Die erste Aufgabe war es also einen Job zu schreiben, der sich an die Datenbank wendet, die erforderlichen Daten extrahiert und in einer geeigneten Struktur in den Redis schreibt. Da Redis ein sehr effizienter Key-Value-Store ist, wollten wir die Daten pro Widget als JSON in den Redis schreiben. Diese Idee stellte sich als gut und effizient heraus. Die Konfiguration von Redis fand einfach in der .env Datei statt. Um Redis in Laravel nutzen zu können, muss man allerdings noch 1 Paket via composer installieren. Die Laravel Dokumentation hat dies gut beschrieben.

Der oben beschriebene Job ist ein Schedule Task in Laravel. Er wird alle 10 Minuten durchgeführt und aktualisiert so die Einstellungen aus der Datenbank. Unser Cache ist für 20 Minuten gültig. Summa summarum sind die angezeigten Daten eines Widgets maximal 30 Minuten alt.

###Caching

Um nicht bei jeder Anfrage mit unserer Produkt-API sprechen zu müssen haben wir Caching implementiert. Wie oben beschrieben benutzen wir dafür Redis. Für den Cache-Key ziehen wir die für die Produkt-API relevanten Informationen aus der Anfrage raus, konkatenieren die Werte und hashen sie. Das gesamte Caching wird mit einer Middleware gelöst. Bevor der Benutzer die Applikation erreicht wird geprüft ob Daten, die zu der aktuellen Anfrage passen, schon im Cache liegen. Wenn dass der Fall ist wird aus dem Cache gelesen und das Ergebnis sofort an den Benutzer geschickt. Das hat den Vorteil dass wir keine Applikationslogik treffen wenn wir gecachte Daten ausspielen, was eine enorm gute Antwortzeit ermöglicht.

Das Schreiben in den Cache passiert nachdem die Antwort an den Browser des Benutzers verschickt wurde. Das ist durch die terminate() Methode einer Laravel Middleware möglich. Das heißt, ohne dass die Antwortzeit beim Anfragen einer ungecachten Ressource größer wird, können wir diese Ressourcen in den Cache schreiben. Einzig das debuggen der terminate() Methode ist schwierig. Da die Antwort schon verschickt ist bevor die terminate()Methode ausgeführt wird, hilft kein dd() und auch kein xdebug. Unsere Lösung war es Werte in den Redis zu schreiben um sie zu überprüfen. Sicherlich kann man auch Logs schreiben. Die Redis-Variante schien aber am einfachsten, da wir beim Cachen ja sowieso schon mit dem Redis sprechen.

###Testing

Schnell stellte sich die Frage wie wir unsere Applikation jetzt richtig Testen können. Da wir alle Daten aus der Datenbank in den Redis schreiben, können wir Eloquent nicht benutzen.
Oder doch?
Wir haben uns entschlossen, für das Testing, Eloquent-Models für die Datenbank, mit allen Einstellungen für das Widget, zu erstellen. Bei einer etablierten Datenbank, mit viel Altlast, wirklich einiges an Arbeit. Aber es hat sich gelohnt. Nachdem wir die Eloquent-Models erstellt haben, haben wir uns noch um Factories für jedes Model gekümmert. Bei jedem Test werden erforderliche Objekte via Factory erstellt und in die Datenbank gespeichert. In der setUp() Methode werden die Einträge aus der Testing-Datenbank jetzt in den Test-Redis geschrieben. Somit sind wir mit der Architektur des Testsystems sehr nah an dem Live-System. Nach einem Testfall werden die Einträge aus Test-Datenbank und Test-Redis wieder gelöscht.

###Umstieg auf Lumen

Unsere Antwortzeiten waren gut aber nicht herrausragend. Die Konfiguration die Laravel bietet ist für eine API zu groß. Die Facaden schlucken Zeit. Der Plan war diese Faktoren mit einem Umstieg auf Lumen auszuschalten. Das Einzige was dafür getan werden musste, war die Facaden mit app(Facaden-Name) auszutauschen. Siehe da, unsere Antwortzeiten haben sich um 20% verbessert. Das ist ein sehr zufriedenstellendes Ergebnis.

#Fazit

Die Entwicklung mit Laravel/Lumen hat enorm viel Spaß gemacht. Es ist alles strukturiert, verständlich und komfortabel. Das Testen unserer Anwendung ging nach dem initialen Aufsetzen und dem Erstellen der Factories gut von der Hand. Das Performance-getriebene Ergebnis ist phänomenal. Gegenüber der alten Version des Widgets, welches wirklich sehr schnell war, konnten wir uns nochmal um 15% steigern.
Wir haben mittlerweile auch ein neues, internes Tool auf Basis von Laravel entwickelt. In diesem Tool haben wir zum ersten mal Eloquent auch produktiv eingesetzt und sind noch mehr von diesem Framework angetan als zuvor.