Hier geben wir Einblicke in das Unternehmen, Gedanken, How-Tos, Wissenswertes und News, die sich aus der Programmierung und Projekten ergeben. Auch unsere Open Source Aktivitäten begleiten wir hier.

Corporate Blog der prooph software GmbH

Mustache und Logic-Less Templates - weniger ist manchmal mehr

Mustache und Logic-Less Templates

Auf StackOverflow, Blogs und in Foren findet man unzählige Diskussionen zu dem Thema, ob Template Engines mehr können sollten, als nur Platzhalter mit Daten zu füllen. Eine der am meisten diskutierten Engines nennt sich Mustache. Logic-Less Templates ist das oberste Ziel dieses Systems, besser der Systeme, denn Mustache wurde in viele Programmiersprachen portiert. Dieser Punkt ist auch gleichzeitig der herausragende Vorteil von Logic-Less Templates.

Ein weiterer Glaubenskrieg im Internet

Warum muss im Internet eigentlich alles in einen Glaubenskrieg ausarten? Welche Programmiersprache ist die beste? Für mich PHP, gefolgt von JavaScript! Welches Framework ist das beste? Für mich ZF2! Welche Software Architektur ist die beste? Für mich MVC! Das könnte ich noch eine Weile fortführen. Fragt man jemand anderen, gibt er wahrscheinlich andere Antworten. Mittendrin würde dann auch die Frage auftauchen: Sind Logic-Less Templates sinnvoll? Hauptargument der Fürsprecher ist: Logic-Less Templates verhindern, dass Business Logik in die Templates gelangt (separation of concerns). Da gibt es nichts zu diskutieren, das ist Fakt. Die Gegenargumentation lautet aber: Die wenige Logik in den Template Systemen sorgt dafür, dass man das Rad ständig neu erfinden muss, um z.B. Zahlenformatierungen oder Übersetzungen zu realisieren. Außerdem wandert dadurch Präsentationslogik ins Model, was wiederum der Trennung der Zuständigkeiten widerspricht. Dem ersten Gegenargument kann ich etwas abgewinnen. Die Arbeit mit einer Template Engine wie Mustache lässt sich gut mit folgender Situation beschreiben: Ich bin in einem fremden Land und beherrsche nur wenige Worte der Landessprache, muss aber trotzdem im Supermarkt meine Einkäufe erledigen, im Restaurant mein Essen bestellen und mit öffentlichen Verkehrsmitteln zurechtkommen. Es ist schwierig, aber irgendwie schaff ich es, mich zu verständigen, sei es mit Händen und Füßen.

Das zweite Argument trifft nur auf Software Architekturen wie Model-View-View-Model (MVVM) zu. In einer MVC Anwendung wandert die Präsentationslogik in den Controller. Ob man das jetzt gut oder schlecht findet ist – mit Blick auf das Prinzip "separation of concerns" – relativ egal.

Verwendung von Business Logik in der View

Ich bin kein genereller Verfechter von Logic-Less Templates aber sie gehören definitiv zu meinem Repertoire. Wovon ich jedoch überhaupt kein Fan bin: Methoden aus dem Model in der View zu verwenden. Gern kann ein Template System ViewHelper zum Rendern und Formatieren mitbringen aber Business Logik hat in einer View nichts zu suchen. Ein Problem, das ich bei der Arbeit mit JavaScript Frameworks wie Backbone.js habe. Daher setze ich auf so ein Framework immer eine MVC Applikation drauf, die durch Controller wieder View und Model trennt – und nein, Backbone.Router ist kein Controller, sondern - wie der Name schon sagt – ein Router.

Die Daten immer über einen Controller in die View zu leiten ist sicherlich zeitaufwendiger, als Model und View einfach direkt kommunizieren zu lassen aber je größer das Projekt wird, desto schwieriger wird es, dabei die Übersicht zu behalten. Mal kurz ein Beispiel, was ich selbst bei einem Kunden miterlebt habe:

Bei der Anwendung handelte es sich um ein Intranet Telefonbuch, aufgepeppt mit social Funktionen wie User Profile, Interessensuche, usw.. Die Mitarbeiterdaten wurden aus zwei Quellen aggregiert: LDAP (> 50.000 Mitarbeiter) und einer Oracle Datenbank, in der der social Content wie Hobbys, Interessen usw. gespeichert wurde. Alles schön und gut. Die Anwendung ging irgendwann online und nach ca. 1 Woche legte sie nicht nur das komplette Intranet lahm sondern auch die Backendsysteme, die auf das LDAP angewiesen waren. Die Anwendung wurde mir in diesem Zustand übergeben. Meine Aufgabe war es nun herauszufinden, was da schief läuft. Die Antwort ließ sich durch ein Profiling der Anwendung recht schnell finden. Die Business Logik für ein User Profil war so ausgelegt, dass fehlende Informationen zu einem User Attribut automatisch nachgeladen wurden (Lazy Loading). In der Personensuche der Anwendung wiederum wurden LDAP Queries definiert, die als Rückgabe nur die IDs der User lieferten. Die Details überspringe ich mal. Das Ende vom Lied war, dass ein ViewHelper beim Rendern des Suchergebnisses dafür sorgte, dass zu jedem User alle LDAP Attribute einzeln nachgeladen wurden. Der Frontend Programmierer wusste davon nichts. Er rief einfach nur $user->get(‚name‘) auf und bekam einen Namen zurück. Dass im Hintergrund eine LDAP Abfrage getriggert wurde, um den Namen zur User-ID zu ermitteln, war außerhalb seines Sichtbereiches. Während der Testphasen ist das nie aufgefallen, weil die Anwendung nur von wenigen Leuten verwendet wurde aber als sie dann online ging und 6.000 Leute pro Tag in der Anwendung unterwegs waren, hatte dies mehr als 1.000.000 LDAP Queries zur Folge und die LDAP Server gingen in die Knie. Eine klassische DoS Attacke, ungewollt ausgelöst durch die Verwendung eines ActiveRecord Objekts in einer View.

In einem mehrköpfigen Team, in dem jeder nur einen Teil der Anwendung entwickelt, ist die Trennung von View und Business Logik ein Muss. Ein neues Teammitglied hat sonst kaum eine Chance, sich richtig in die Anwendung einzuarbeiten, oder – wie in meinem Beispiel – die Anwendung zu bugfixen. Bei einer sauberen MVC Struktur brauche ich wenige Stunden, um eine fremde Anwendung komplett zu überblicken. Das Routing auf die Controller ist der Ausgangspunkt. Von den jeweiligen Controller-Actions geht es dann tiefer in die Anwendung rein, einmal in Richtung Business Logik und einmal in Richtung View. Wenn ich in jedem Template aufs Neue schauen muss, wo, wann und wie Methoden aus dem Model aufgerufen werden, dann brauche ich um ein Vielfaches länger dafür. Aber auch für ein kleines und konstantes Team birgt eine solche Vorgehensweise Gefahren. Kein Entwickler ist davor geschützt, auch wenn er noch so gut ist, kann ihm eine nahende Deadline oder anderweitiger Stress einen Strich durch die Rechnung machen.

Mustache - weniger ist manchmal mehr

Mustache treibt es mit der Trennung von Zuständigkeiten auf die Spitze. Die Möglichkeiten, die ich in Mustache zum Rendern von Templates habe, lassen sich an einer Hand abzählen. Keine If-Statements, keine Switch-Case-Anweisungen, keine "out of the box"-ViewHelper, nichts was einem das Leben erleichtern könnte. ViewHelper lassen sich allerdings mit etwas Geschick nachrüsten, da Mustache immerhin die Möglichkeit bietet, Funktionen auszuführen. Warum nimmt Mustache das Statement "Logic-Less Templates" so ernst? Ganz einfach! Nur so wird ermöglicht, dass ein Mustache Template von PHP genauso gerendert werden kann wie von Java, Ruby oder JavaScript. Gerade Letzteres ist entscheidend. Man wird selten in die Verlegenheit kommen, ein Template zwischen unterschiedlichen Backend Systemen zu teilen, aber dasselbe Template vom Backend wie vom Frontend rendern zu lassen ist schon eher eine interessante Möglichkeit. Single Page JavaScript Applications rendern ihre Views in der Regel mit JavaScript, z.B. mit Underscore.js Templates. Was ist aber auf einer Webseite mit vielen Einstiegspunkten? Da möchte ich schon ganz gern die initiale Ausgabe vom Backend rendern lassen - schon allein aus SEO Sicht – und anschließend mit JavaScript Teilbereiche der Webseite neu rendern lassen, nachdem der User eine Zustandsänderung ausgelöst hat. Mustache bietet mir für dieses Szenario das richtige Werkzeug. Immer dann, wenn ich ein Template im Backend wie im Frontend benötige, rendere ich es mit Mustache und schiebe es über einen <script type=“text/template“> Tag auch ins Frontend, um dort per JavaScript dasselbe Template für einen Refresh verwenden zu können.

Diese Aufwandsersparnis ist wesentlich höher, als mehr Logik im Template zur Verfügung zu haben. Auch wenn ich dadurch schon in meinem Controller mal ein count() auf eine Liste durchführen und die Anzahl in eine extra Variable speichern muss, weil ich sonst die Anzahl der Liste mit Mustache nicht darstellen kann. Ob ich nun im Controller Listen zähle, Zahlen formatiere und Übersetzungen vornehme oder dies erst über ViewHelper mache, ist doch egal. Der Controller entscheidet, was mit den Daten passiert, also kann er sie auch vorformatieren.

Sie suchen einen Software Dienstleister für Ihr Projekt: Projektanfrage stellen

Blog Artikel mit ähnlichen Themen