JavaScript mit Rückgrat

Quellcode strukturieren mit Backbone
Kommentare

JavaScript ist aus modernen Webapplikationen nicht mehr wegzudenken. Doch wie strukturiert man seinen Quellcode so, dass er wart- und erweiterbar bleibt? Hier kommt das leichtgewichtige Strukturframework Backbone ins Spiel. In diesem Artikel wollen wir einen ersten Blick riskieren.

JavaScript ist aus modernen Webapplikationen nicht mehr wegzudenken. Mittlerweile haben sich auch Bibliotheken wie beispielsweise jQuery auf breiter Front durchgesetzt. Sie stellen eine Sammlung von allgemeinen Problemlösungen dar, die den Entwicklern die Arbeit erleichtern und über die Stolpersteine der Browserinkompatibilitäten hinweghelfen wollen. Vermischt man jetzt allerdings seinen JavaScript-Quellcode mit HTML, verschlechtert sich die Wartbarkeit der Applikation mit zunehmendem Wachstum rapide und Entwickler können innerhalb kürzester Zeit kaum mehr auf geänderte Anforderungen reagieren. Die Lösung für dieses Problem lautet Strukturierung. Wie strukturiert man aber seinen JavaScript-Quellcode so, damit er über lange Zeit wart- und erweiterbar bleibt und so eine lange Lebenszeit der Software unterstützt? Genau hier kommt Backbone ins Spiel. Es ist ein sehr leichtgewichtiges Strukturframework für JavaScript. Dabei ist es so konzipiert, dass es sich nur auf seine Aufgabe, die Strukturierung der Applikation, konzentriert und alle anderen Aufgaben anderen Komponenten überlässt. Das führt einerseits dazu, dass Backbone recht überschaubar ist und andererseits nur den Themenbereich fokussiert, für den es entwickelt wurde. Diese Tatsache bringt aber auch einen Nachteil mit sich: Backbone kann nicht für sich alleine verwendet werden, stattdessen müssen gewisse Voraussetzungen für die Lauffähigkeit erfüllt sein.

Voraussetzungen – was wird benötigt?

Durch die beschriebene Spezialisierung von Backbone benötigt es weitere Bibliotheken, die sich um die Unterstützung des Strukturframeworks kümmern. JavaScript wird im Clientbereich vor allem in HTML-Dokumenten verwendet. Damit die Applikation Zugriff auf den DOM erhält, ihn auslesen und auch modifizieren kann, benötigt Backbone ein Framework wie jQuery oder Zepto. Das kommt überall dort zum Einsatz, wo es Schnittstellen zum DOM gibt. Moderne Webapplikationen leben aber nicht nur von DOM-Operationen, sondern auch von der asynchronen Kommunikation mit einem Server. Auch hier benötigt Backbone Unterstützung. jQuery oder Zepto übernehmen die Kommunikation zum Server mittels der Ajax-Komponente, die durch Backbone.sync gekapselt wird. Im Weiteren wird hauptsächlich auf jQuery eingegangen, da es eine weitere Verbreitung hat und Zepto eine jQuery-kompatible Syntax aufweist.

Die zweite Voraussetzung, die für eine lauffähige Applikation mit Backbone erfüllt sein muss, ist Underscore.js. Underscore stellt so etwas wie den Werkzeugkasten für eine Applikation dar. Es ist eine lose Sammlung von Hilfsfunktionen. Dabei wird auf die browsereigenen Implementierungen der Funktionen zurückgegriffen, falls sie vorhanden sind. Andernfalls stellt Underscore eine standardkonforme Implementierung zur Verfügung. Unter anderem stehen Collection-Funktionen zur Verfügung, die verschiedene Operationen map, reduce, Suchen oder Iterationen auf Arrays und Objekte erlauben. Neben diesen Funktionen sind klassische Array-Operationen wie Differenzen oder Schnittmengen und klassische Objektoperationen wie Typprüfungen Bestandteil des Funktionsumfangs. Zu bemerken ist außerdem, dass es Unterstützungsfunktionen für Funktionen wie die bind-Funktion, die auf einfache Weise eine Funktion in einen Objektkontext bindet, gibt. Sind diese Voraussetzungen, wie in Listing 1 zu sehen, erfüllt, kann man mit dem Aufbau seiner Applikation mit Backbone beginnen.


   
      
      Backbone Example
      
      
      
   

   
      ...
   

Die Struktur von Backbone

Backbone stellt eine leichtgewichtige Implementierung eines MVC Frameworks in JavaScript dar. Die klassische Aufteilung sieht hier vor, dass die Logik der Applikation in den Models gekapselt ist; die Anzeigelogik wird von den Views übernommen und der Controller ist dafür zuständig, die eingehenden Anfragen zu bearbeiten und die Models mit den Views zusammenzubringen. In Listing 2 ist eine Möglichkeit der Strukturierung einer Applikation zu sehen. Interessant ist hierbei vor allem die Hierarchie innerhalb des /scripts/-Verzeichnisses, das die JavaScript-Dateien der Applikation enthält. Sämtliche Voraussetzungen wie jQuery und Underscore aber auch die Quellen von Backbone selbst liegen im /library/-Verzeichnis, um sie ordentlich vom Rest der Applikation getrennt zu halten. Die eigentliche Applikation befindet sich im Verzeichnis /application/. In der ersten Ebene existiert die Datei router.js; sie wird später den Front Controller unserer Applikation enthalten. In früheren Backbone-Versionen war der Name der Basisklasse des Front Controllers noch Backbone.Controller. Der Name wurde im Verlauf der Entwicklung allerdings in Backbone.Router umbenannt, da diese Klasse nur noch das Routing innerhalb der Applikation übernimmt. Das Verzeichnis /models/ beinhaltet die Models der Applikation, genau wie /views/ die entsprechenden Views enthält.

├── index.html
├── style
│   └── style.css
└── scripts
    ├── application
    │ ├── router.js
    │ ├── models
    │ └── views
    └── library
        ├── backbone.js
        ├── jQuery.js
        └── underscore.js

Events

Ein wichtiger Bestandteil von Backbone sind Events. Ein Großteil der Kommunikation innerhalb einer Applikation, die mit Backbone umgesetzt wird, erfolgt über Events. Ein Event stellt dabei im Allgemeinen ein Ereignis dar, auf dessen Auftreten andere Teile der Software reagieren können. Events werden mithilfe der trigger-Methode eines Objekts geworfen. Der Methode können neben dem Namen des Events weitere Parameter übergeben werden, die direkt an die Callbacks weitergereicht werden. Über die bind-Methode des Objekts, das ein Event triggert, können Callbacks registriert werden, die bei Auftreten des Events aufgerufen werden. Zu diesem Zweck erhält die Methode als Parameter den Namen des Events, eine Referenz auf den Callback und den Kontext, in dem der Callback ausgeführt werden soll. Mittels unbind können Callbacks wieder entfernt werden. Es gibt hierbei die Option, nur einen Callback, alle Callbacks für ein bestimmtes Event oder alle Callbacks eines Objekts zu entfernen. Die Methoden trigger, bind und unbind werden durch Backbone.Event zur Verfügung gestellt. Alle relevanten Klassen wie Backbone.View, Backbone.Model oder auch Backbone.Collection leiten von dieser Klasse ab und stellen diese Methoden somit ebenfalls zur Verfügung.

[ header = Models und Views ]

Models und Views

Der Kern einer Applikation wird durch Models und Views gebildet. Models beinhalten Daten und Operationen auf diese Daten. Views werden benutzt, um die Models darzustellen. In einer Applikation, die mit Backbone umgesetzt wird, sind einem Model ein oder mehrere Views zugeordnet. Das geschieht durch die model-Eigenschaft von Backbone.View, die meist bei der Instanziierung der Klasse übergeben wird und ein konkretes Model beinhaltet. Wie aber hängen nun Model und View in der Praxis zusammen? Ziel ist es, dass eine View den aktuellen Status eines Models visualisiert. Ändert sich ein Model, soll das dem Betrachter auch sofort angezeigt werden. Dieses Konstrukt wird mittels einer eventgetriebenen Architektur umgesetzt. Wird eine Eigenschaft eines Models über die set-Methode verändert, wird ein change-Event gefeuert. Die View bindet sich auf dieses change-Event des Models und kann so entsprechend auf Änderungen reagieren. Im Folgenden wird anhand einer beispielhaften Applikation die Verwendung der einzelnen Komponenten von Backbone gezeigt. Hierzu soll eine sehr einfache Adressenverwaltung aufgebaut werden, die im ersten Schritt lediglich die Vor- und Nachnamen von Personen anzeigt. In Listing 3 wird der Zusammenhang zwischen Model und View an einem konkreten Beispiel verdeutlicht. Im ersten Schritt werden die beiden Klassen eingebunden und damit verfügbar gemacht. Danach wird ein Knoten zum Einhängen der View geschaffen und ein Formular, über das das Model verändert werden kann. Im abschließenden script-Tag wird das Model instanziiert und die View im Konstruktor übergeben, die im Anschluss mittels Aufruf von render angezeigt wird. Um zu demonstrieren, wie sich eine Änderung des Models auswirkt, werden die Werte der Input-Felder bei einem Klick auf den Button mittels set in das Model geschrieben. In der initialize-Methode der View bindet die View ihre eigene render-Methode auf das change-Event ihres Models. Testet man die Anwendung, indem man einen beliebigen Namen eingibt und auf submit klickt, ändert sich der oben angezeigte Name.

index.html
  
     ...
     
     
  

  
     
var addressModel = new Address({firstname: 'John', surname: 'Doe'}); var view = new AddressView({model: addressModel}); view.render(); $('#submit').on('click', function() { addressModel.set({firstname: $('#firstname').val(), surname: $('#surname').val()}); }); scripts/application/models/address.js Address = Backbone.Model.extend({ defaults: { firstname: null, surname: null } }); scripts/application/views/address.js AddressView = Backbone.View.extend({ tagName: 'li', className: 'addressView', _template: ' ', initialize: function() { this.model.bind('change', _.bind(this.render, this)); }, render: function() { $(this.el).html(this._template); this.$('.firstname').html(this.model.get('firstname')); this.$('.surname').html(this.model.get('surname')); $('#addressList').append(this.el); } });

Die Collection

Im Augenblick stehen uns lediglich ein Model und eine View zur Verfügung. Will man eine Reihe von Datensätzen verwalten, reicht das natürlich bei Weitem nicht aus. Hier kommt eine weitere Komponente von Backbone zum Einsatz: die Collection. Eine Collection stellt eine Sammlung von Objekten des gleichen Typs dar; um genauer zu sein, sind es Instanzen des gleichen Models. Man kann sich eine Collection also als eine Liste von Models vorstellen. An sich ist das nichts, was man nicht auch mit einem einfachen Array oder einer Objektstruktur erreichen könnte, und hier ist man nicht nur auf einen Objekttyp festgelegt. Die Collection ist allerdings weit mehr als nur ein Container. Vielmehr steht eine Vielzahl von Hilfsfunktionen zur Verfügung, auf die im Folgenden näher eingegangen wird.

Grundsätzlich empfiehlt es sich, einer Collection eine View zuzuweisen, ähnlich wie bei einzelnen Models. Diese View ist dafür zuständig, die Collection korrekt anzuzeigen. Wird ein Element zu einer Collection hinzugefügt oder wird ein Element entfernt, werden jeweils Events gefeuert, auf die sich die View binden und die Liste neu darstellen kann. Das bietet den Vorteil, dass die Collection ihre View nicht zwingend kennen muss, um sich neu darzustellen. Sobald die einzelnen Komponenten verbunden sind, also eine Collection ihre eigene View besitzt und weiß, welche Art von Models sie aufnehmen kann, kann damit begonnen werden, die Collection mit Elementen zu füllen. Das kann auf mehrere Arten erfolgen: Zum einen kann der Collection bei der Instanziierung ein Array von Models übergeben oder die Models können per add einzeln hinzugefügt werden. In Listing 4 erweitern wir nun unser Model/View-Konstrukt um eine Collection, sodass wir mit mehreren Adressen umgehen können.

scripts/application/models/addresses.js
AddressCollection = Backbone.Collection.extend({
    model: Address,
});
scripts/application/views/addresses.js
CollectionView = Backbone.View.extend({

    collection: null,

    initialize: function() {
        this.collection.bind('add', _.bind(this.render, this));
        this.collection.bind('remove', _.bind(this.render, this));
    },

    render: function() {
        this.collection.each(function(model) {
            model.view.render();
        });
    }

});

Mit ein paar Zeilen sind wir jetzt in der Lage, eine Sammlung von Models auf einmal anzuzeigen. Dazu sind noch ein paar kleine Anpassungen an unserem bestehenden Code notwendig: Das Model hat eine zusätzliche Eigenschaft view erhalten, durch die das Model seine View kennt. Die Collection bietet die Möglichkeit, über die each-Methode über die einzelnen Models der Collection zu iterieren. Über die view-Eigenschaft können nun die einzelnen Models dargestellt werden. In der initialize-Funktion wird jeweils die render-Methode auf das Collection-Event add und remove gebunden, um die Liste neu darzustellen, falls ein Element hinzugefügt oder entfernt wird.In Listing 5 sieht man, wie einfach es mittlerweile ist, auch mit mehr als einem Model zu arbeiten.

var charly = new Address({firstname: 'Charly', surname: 'Brown'});
charly.setView(new AddressView({model: charly}));

var sally = new Address({firstname: 'Sally', surname: 'Brown'});
sally.setView(new AddressView({model: sally}));

var lucy = new Address({firstname: 'Lucy', surname: 'van Pelt'});
lucy.setView(new AddressView({model: lucy}));

var peanuts = [charly, sally, lucy];

var addresses = new AddressCollection(peanuts);
var colView = new CollectionView({collection: addresses});
colView.render();

[ header = Hinzufügen, editieren und löschen ]

Hinzufügen, editieren und löschen

Das Komfortable an den Methoden zum Füllen von Collections ist, dass sowohl initial als auch zur Laufzeit des Scripts über die add-Methode nicht nur ein Model, sondern ein ganzes Array von Models hinzugefügt werden kann. Eine weitere Art, um Models in eine Collection aufzunehmen, ist die create-Methode: Sie erhält als Parameter ein Objekt mit den Eigenschaften des neuen Objekts und verhält sich damit genau wie der Konstruktor eines Models. Der Rückgabewert ist das eben erstellte Model.

Um einzelne Models innerhalb einer Collection zu bearbeiten, muss zuerst eine Referenz auf das Model aus der Collection geholt werden. Zu diesem Zweck existiert die Methode get. Der Parameter dieser Methode ist der Wert des ID-Attributs des gesuchten Models. Der Rückgabewert ist eine Referenz auf das Model. Hier gibt es allerdings eine Kleinigkeit zu beachten: Wurde das Model noch nicht auf dem Server gespeichert, besitzt es noch keine eindeutige ID. In diesem Fall funktioniert die get-Methode nicht. Zur Lösung dieses Problems existiert die getByCid-Methode, um Models, denen noch keine feste ID zugewiesen wurde, in einer Collection zu finden. Die cid-Eigenschaft wird von Backbone automatisch für Models generiert, die auf dem Client erstellt wurden, und dient als temporäre ID. Ansonsten ist die Funktionsweise die gleiche wie bei get. Da die Models per Referenz aus der Collection gelesen werden, wirken sich Änderungen direkt auf die Instanzen innerhalb der Collection aus und müssen nicht separat zurückgeschrieben werden.

Das Entfernen eines Models aus einer Collection kann auf zwei verschiedene Arten erreicht werden: entweder über die remove-Methode der Collection oder die destroy-Methode des Models. Die remove-Methode der Collection erlaubt es, mehrere Models gleichzeitig aus der Collection zu entfernen. Dafür muss lediglich statt dem zu entfernenden Model ein Array von Models als Parameter übergeben werden. Der Aufruf führt dazu, dass die Collection ein remove-Event wirft, auf das sich die View binden kann, um sich neu darzustellen. Die zweite Variante, die destroy-Methode des Models, löscht das Model selbst und benachrichtigt die Collection des Models von der Löschung, was zum Aufruf von remove in der Collection führt (Listing 6).

scripts/application/views/address.js
_template: '...edit delete',
...
events: {
    'click a.edit': 'edit',
    'click a.delete': 'remove'
},
...
edit: function(e) {
    e.preventDefault();
    $('#id').val(this.model.cid);
    $('#inputFirstname').val(this.model.get('firstname'));
    $('#inputSurname').val(this.model.get('surname'));
},

remove: function(e) {
    e.preventDefault();
    this.model.destroy();
}
scripts/application/views/edit.js
Edit = Backbone.View.extend({

    tagName: 'li',
    className: 'addressView',
    _template: '',

    collection: null,

    setCollection: function(collection) {
        this.collection = collection;
    },

    events: {
        'click input#submit': 'save'
    },

    render: function() {
        $('body').append($(this.el).html(this._template));
    },

    save: function() {
        if ($('#id').val() == '') {
            var model = this.collection.create({firstname: $('#inputFirstname').val(),
                                                surname: $('#inputSurname').val()});
            model.setView(new AddressView({model: model}));
        } else {
            var model = this.collection.getByCid($('#id').val());
            model.set({firstname: $('#inputFirstname').val(),
                         surname: $('#inputSurname').val()});
        }
        $('#inputFirstname').val('');
        $('#inputSurname').val('');
        $('#id').val('');
    }
});

Um die beschriebenen Aktionen auch in unserem kleinen Beispiel testen zu können, führen wir eine Edit View ein, die ein Formular zur Anzeige und zum Bearbeiten von Werten zur Verfügung stellt. Außerdem wird die View des Models dahingehend erweitert, dass neben jedem Eintrag ein Link zum Bearbeiten und einer zum Löschen existiert. Backbone bietet innerhalb der View die Möglichkeit, über die Eigenschaft events bestimmte Events innerhalb des Templates auf einzelne Methoden der View zu mappen. In unserem Fall werden dadurch die beiden Links mit Funktionalität hinterlegt. Ein Klick auf Delete führt dazu, dass die View die destroy-Methode des eigenen Models aufruft und es so aus der Collection entfernt. edit lädt die Werte des Models in die neue Edit View, die sich dann um das Speichern der veränderten Werte nach dem Abschicken des Formulars kümmert.

Der Controller

Der Router bildet den zentralen Einstiegspunkt einer Applikation. Hier besteht die Möglichkeit, über Hash-Navigation verschiedene Methoden aufzurufen und so das Verhalten der Anwendung zu steuern. Zu diesem Zweck sind lediglich wenige Schritte notwendig. Zuerst benötigt man eine konkrete Implementierung der Backbone.Router-Klasse. Innerhalb dieser wird mit der route-Eigenschaft das Mapping zwischen Hash und aufzurufender Methode hergestellt. Im nächsten Schritt müssen die einzelnen Methoden implementiert werden, danach wird der Router eingebunden, instanziiert und die start-Methode von Backbone.history aufgerufen – damit ist der Router funktionsfähig. Durch diesen Aufruf beobachtet die Applikation das hashchange-Event und reagiert bei dessen Auftreten mit der für die gewählte Route hinterlegten Aktion. Mit den in Listing 7 beschriebenen Anpassungen ist es nun möglich, die Liste unseres Beispiels über http://example.dev#list anzuzeigen. Durch diese Änderung kann fast der komplette JavaScript-Code aus der index.html entfernt und in separate JavaScript-Dateien ausgelagert werden, was uns dem eigentlichen Ziel, einer sauberen Trennung von JavaScript und HTML, ein großes Stück näher bringt.

 index.html
    ...
    



    ...
    
        var router = new Router();
        Backbone.history.start();
application/router.js
Router = Backbone.Router.extend({
    routes: {
        'list': 'list'
    },

    list: function() {
        var charly = new Address({firstname: 'Charly', surname: 'Brown'});
        charly.setView(new AddressView({model: charly}));

        var sally = new Address({firstname: 'Sally', surname: 'Brown'});
        sally.setView(new AddressView({model: sally}));

        var lucy = new Address({firstname: 'Lucy', surname: 'van Pelt'});
        lucy.setView(new AddressView({model: lucy}));

        var peanuts = [charly, sally, lucy];

        var addresses = new AddressCollection(peanuts);
        var colView = new CollectionView({collection: addresses});
        colView.render();
    }
});

[ header = Mit dem Server sprechen ]

Mit dem Server sprechen

Bisher beschränkte sich unser Beispiel nur auf den Browser des Benutzers. Backbone kann allerdings sehr viel mehr als nur JavaScript-Dateien lokal zu strukturieren, Views sich dynamisch aktualisieren zu lassen und ein paar Models zusammenzufassen. Mit ein paar einfachen Handgriffen lässt sich die Beispielapplikation so erweitern, dass die Daten von einem Server gelesen werden und Änderungen zum Server geschickt werden. Um die Models mit einem Server zu verbinden, setzt man die url-Eigenschaft der Collection auf den URL, über die die Models ihre Werte beziehen beziehungsweise über die die Models sich selbst persistieren können. Änderungen an den Models können weiterhin über die set-Methode durchgeführt werden. Danach muss allerdings noch save aufgerufen werden, um die Daten an den Server zu übermitteln. Eine andere Möglichkeit besteht darin, die zu ändernden Daten direkt als Hash an save zu übergeben. Somit verhält sich save wie ein get mit anschließendem save. Bei neu erstellten Models wird ein HTTP POST an den Server geschickt, bei einem Update ein HTTP PUT. Der Server erhält in diesen Requests die Daten der Models und kann entsprechend damit umgehen. Als zweiter Parameter kann save ein Hash mit weiteren Optionen übergeben werden. Hier können zum Beispiel Callbacks für Error und Success definiert werden.

Nachdem das Erstellen und Bearbeiten von Models abgedeckt ist, kommen wir nun zum Löschen. In unserem Beispiel verwenden wir bereits die destroy-Methode des Models, um es zu löschen. Diese Methode schickt automatisch einen HTTP DELETE Request an den Server, der dadurch über die Löschung in Kenntnis gesetzt wird und so entsprechend reagieren kann. destroy übernimmt wie auch save zusätzliche Optionen wie Success oder Error Callbacks.

Schließlich soll hier noch kurz auf die Collection und deren initiales Füllen eingegangen werden. Es stellt sich die Frage: Wie kommen die Models in die Collection? Zwar existiert die Methode fetch innerhalb der Collection; sie sollte allerdings nicht verwendet werden, um die Collection initial zu füllen. Stattdessen wird empfohlen, die Daten mithilfe eines Hashes über reset in die Collection einzufügen. Das bietet den Vorteil, dass der Entwickler volle Kontrolle über den Zeitpunkt und Zustand des Füllens hat und nicht auf das asynchrone Füllen der Collection mittels fetch warten muss. Aus Platzgründen kann hier leider nur auf einen Bruchteil der Möglichkeiten, die Backbone im Zusammenhang mit asynchroner Serverkommunikation bietet, eingegangen werden. Je nach Notwendigkeit kann die Applikation noch weiter ausgebaut werden.

Ausblick

Das hier gezeigte Beispiel bietet die Grundlage für zahlreiche Erweiterungen wie die Implementierung einer Edit View für die Models, damit sie sich per Hash-Navigation in ein Formular laden lassen. Eine weitere Möglichkeit zur Verbesserung ist der Einsatz von RequireJs. Require kümmert sich um das dynamische Laden von Quellcode. Damit können die störenden script-Tags im Seiten-Header entfernt werden. Außerdem gibt es für Require ein Plug-in, mit dem sich Textdateien einlesen lassen. Der Inhalt dieser Dateien kann dann den Backbone Views als Templates zur Verfügung gestellt werden. Auf diese Weise wird die Trennung von JavaScript-Code und HTML noch weiter verbessert. Auch das große Feld der Qualitätssicherung im Bereich JavaScript soll nicht unberührt bleiben. So kann eine Backbone-Applikation problemlos mit QUnit Unit Tests abgedeckt und das Frontend mit Selenium-Tests abgesichert werden.

Mittlerweile sollte den meisten Entwicklern klar sein, dass eine Notwendigkeit zur Strukturierung von JavaScript in Applikationen besteht. Das spiegelt auch die wachsende Anzahl von Frameworks mit Strukturkomponenten wie JavaScript MVC, Knockout oder Backbone wider. Auch beim Einsatz großer Frameworks wie Dojo oder extJs empfiehlt es sich, die jeweils vorgeschlagene Struktur für die Applikation zu verwenden. Backbone ist ein Framework, das sich nur mit der Strukturierung beschäftigt und alle weiteren Aufgaben an andere Komponenten auslagert. Es ist relativ einfach aufzusetzen und man kann innerhalb kurzer Zeit eine funktionsfähige und leicht erweiterbare Applikation implementieren. Aber wie es im Leben immer ist: „With great power comes great responsibility”. Bevor man sich mit der Implementierung einer Lösung beschäftigt, sollte man sich die Zeit nehmen, ein klares Konzept zu entwickeln und einige Konventionen aufzustellen. Gerade der eventgetriebene Ansatz macht eine Applikation sehr schnell undurchsichtig, da man im Codefluss nicht direkt ablesen kann, welche Folgen bestimmte Events nach sich ziehen.

Aufmacherbild: Male doctor holding tablet von Shutterstock / Urheberrecht: Syda Productions

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -