© saicle/Shutterstock.com
Mit Node.js, Express und Seneca ins Microservices-Universum

Microservices mit Node.js


Dieser Artikel beschäftigt sich sowohl mit der Umsetzung von Microservices in Node.js als auch mit der Einbindung bestehender Microservices in eine Node. js-Applikation. Außerdem erfahren Sie, wie Sie Microservices skalieren können und wie sich sowohl synchrone als auch asynchrone Microservices mit Node.js umsetzen lassen.

Microservices sind der Inbegriff einer leichtgewichtigen und flexiblen Architektur. Warum sollten Sie also nicht solche Microservices mit einer ebenfalls leichtgewichtigen Plattform wie Node.js umsetzen? Einer der großen Vorteile einer Microservices-Architektur ist, dass Sie für jeden Microservice, den Sie implementieren, die Programmiersprache und die Technologien unabhängig von den übrigen Services in Ihrer Infrastruktur für das jeweils zu lösende Problem wählen können. Das bedeutet, dass Sie nicht Ihre komplette Applikation mit Node.js schreiben müssen, sondern nur ganz bestimmte Teile damit umsetzen können. Der modulare Aufbau von Node.js zieht sich sowohl durch die Plattform selbst als auch durch den Paketmanager npm und die meisten Applikationen, die mit Node.js umgesetzt werden. Auf dieser Basis können Sie mit der Umsetzung einer Applikation mit Microservices starten. Dabei sollten Sie jedoch einiges beachten, damit Sie auch von den Vorteilen dieser Architekturform profitieren.

Ein Microservice in Node.js

Sie haben sich also entschlossen, einen Microservice in Node.js umzusetzen. Bevor Sie mit der eigentlichen Entwicklung beginnen, sollten Sie sich zunächst darüber Gedanken machen, wie Ihr Service aufgebaut ist. Ein Microservice kann grundsätzlich nur eine Sache, die dafür aber gut. Erledigt ein solcher Service zu viele Dinge, besteht die Gefahr, dass ein weiterer Monolith daraus entsteht.

Für die folgenden Abschnitte gehen wir von einem Time-Tracking-Service aus, der beispielsweise zur Zeit­erfassung in einem Projekt dienen kann. Über diesen Time-Tracker können Sie die Beginn- und Endzeiten sowie das Datum bestimmter Aufgaben erfassen. Jeder Aufgabe ist neben dem Beginn und Ende ein Titel, eine Beschreibung, ein Projektkürzel und ein Mitarbeitername zugeordnet. Das Ziel der Implementierung ist, dass Sie diesen Service in jeder beliebigen Applikation einbinden und nutzen können; zu diesem Zweck müssen Sie allerdings eine definierte Schnittstelle schaffen. In den meisten Fällen wird das ein Web Service sein. Diese Art der Anbindung lässt sich mit Node.js sehr gut bewerkstelligen, da die Schnittstellen entweder direkt durch interne Module oder externe Pakete erzeugt werden können. Ob es sich dabei um einen leichtgewichtigen REST-Service oder einen etwas unhandlicheren SOAP-Service handelt, hängt vor allem von Ihren Anforderungen und Vorlieben ab.

Für die Implementierung des Beispiels kommt ein REST-Service zum Einsatz. Im Gegensatz zu einer monolithischen Applikation, bei der sämtliche Features fest in die Applikation integriert sind und auch die Datenhaltung zentral an einer Stelle erfolgt, ist jeder Microservice für die Datenhaltung selbst verantwortlich. Das hat den entscheidenden Vorteil, dass jeweils die passende Datenbanktechnologie für ein bestimmtes Problem verwendet werden kann und Sie bei der Entwicklung kaum Rücksicht auf das Gesamtsystem nehmen müssen. Auch in diesem Fall spielt der modulare Aufbau von Node. js seine Stärke aus: Für nahezu jedes Datenbanksystem sind Treiber vorhanden, die über npm installiert werden können. Für unser Beispiel kommt SQLite als Datenbank zum Einsatz.

Bis auf den Datenbanktreiber beinhaltet Node.js bereits alles, was Sie für die Implementierung Ihres Service benötigen. Allerdings ist die Behandlung eingehender Requests und die Behandlung von Routen mit den nativen Schnittstellen immer etwas umständlich. Aus diesem Grund sollten Sie auf ein Framework zurückgreifen, das Ihnen die meiste Arbeit bereits abnimmt. Typischerweise kommt hier Express zum Einsatz. Dieses Framework basiert auf dem Node.js-eigenen HTTP-Modul und fügt einige zusätzliche Features wie Routing und ein Plug-in-Interface hinzu, über das Sie so genannte Middleware-Komponenten einbinden können. Diese dienen dazu, für alle oder nur bestimmte Requests zusätzliche Funktionalitäten wie beispielsweise Routing oder Authentifizierung hinzuzufügen.

Bevor Sie nun mit der eigentlichen Umsetzung beginnen, sollten Sie Ihr Projekt zunächst initialisieren. Dazu gehört vor allem die Einrichtung einer Versionskontrolle beispielsweise über Git, die Erstellung einer Readme-Datei, die die Dokumentation der Schnittstelle enthält, sowie die Erzeugung der package.json-Datei für Ihr Projekt. Diese Datei enthält alle wichtigen Rahmendaten für Ihre Applikation, wie die Lizenz, den Einstiegspunkt und sämtliche Abhängigkeiten. Auf der Kommandozeile können Sie mit npm über den Befehl npm init eine package.json-Datei für Ihr Projekt erzeugen lassen, indem Sie ein paar grundlegende Fragen beantworten.

Bei der Erstellung eines Microservice empfiehlt es sich, testgetrieben vorzugehen. In diesem Fall ist die Logik Ihrer Applikation komplett durch Tests abgesichert. Neben dem qualitätssichernden Aspekt dokumentieren die Tests außerdem die Verwendung Ihres Quellcodes und beschreiben zusätzlich Edge Cases. Das Thema Unit-Tests wird im Zuge dieses Artikels nur am Rande behandelt, um den Umfang nicht zu sprengen.

Mit npm install --save express body-parser sqlite3 installieren Sie alle Abhängigkeiten, die Sie für die Umsetzung Ihres Service benötigen. Die Pakete werden heruntergeladen und im /node_modules/-Verzeichnis Ihrer Applikation gespeichert. Die Namen und Versionsnummern der Pakete werden zusätzlich in der package.json-Datei im Abschnitt dependencies hinterlegt. Dies dient zum einen der Dokumentation, zum anderen, dass Sie die Pakete jederzeit in der korrekten Version mit npm install nachinstallieren können. Im Normalfall werden Paketabhängigkeiten nicht mit dem Quellcode der Applikation versioniert.

Der Aufbau eines Microservice in Node.js folgt in den meisten Fällen immer dem gleichen Schema. Sie erzeugen eine index.js-Datei (Listing 1) im Wurzelverzeichnis Ihrer Applikation; sie dient als Einstiegspunkt. Um diesen Sachverhalt zu dokumentieren, halten Sie den Namen der Datei in der package.json-Datei unter dem Schlüssel main fest. Die Indexdatei sollte lediglich dem Einstieg dienen und möglichst wenig Quellcode aufweisen; hier sorgen Sie nur dafür, dass Ihr Service ordnungsgemäß gestartet wird. Ihre eigentliche Applikation befindet sich dann in einer Verzeichnisstruktur in einem Verzeichnis mit dem Namen /lib/. Ihre Unit-Tests speichern Sie parallel zum /lib/-Verzeichnis in einem Verzeichnis mit dem Namen /test/.

Listing 1: Der Einstieg in die Applikation: index.js

const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.urlencoded({extended: false})); require('./lib/router')(app); app...

Neugierig geworden?

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang