Was haben Tesla, Edison und Turing gemeinsam? Um diese Frage zu beantworten muss ich ein wenig ausholen:
Unser E-Commerce-Entwicklungsbereich bei OTTO ist in verschiedene kleine agile Teams aufgeteilt, die jeweils mehrere Anwendungen entwickeln. Dabei bestimmt jedes Team seinen Technologiestack selbst und kann pro Anwendung die passende Technologie wählen. So setzt ein Team für eine Anwendung zum Beispiel auf Java, für eine andere auf Clojure oder auch auf NodeJS.
Wenn ich mich als Team also für eine Technologie für meine neue Anwendung entschieden habe, geht es los und ich baue den betrieblichen Rahmen (Logging, Healthcheck, Statuspage, DB-Anbindung, usw...) der Applikation auf. Im Microservice-Umfeld baue ich so häufig neue Applikationen, dass dieser Rahmen wiederholt geschaffen werden muss. Noch unangenehmer wird es durch die Verwendung von verschiedenen Programmiersprachen und deren Technologien. Als Techie hat man natürlich grundsätzlich keine Lust - egal was - doppelt und dreifach zu implementieren, also haben wir einen Weg gesucht, den oben genannten Grundstock unserer Anwendungen sinnvoll zu abstrahieren.
Um eine sinnvolle Abstraktion zu gewährleisten, werden die Microservices von OTTO nicht von Innen heraus definiert, sondern von Außen. Es wird also nicht vorgegeben welche Sprache oder welches Framework verwendet werden soll. Sondern es wird definiert, dass ein Service unabhängig von seiner implementierten Fachlichkeit bestimmte Eigenschaften erfüllen muss.
Zu den Eigenschaften gehören für den Betrieb notwendige Aspekte, wie eine Healthcheck und Status Seite, die jeweils unter definierten URLs erreichbar sind, ein embedded Server um die Anwendung ohne zusätzlichen Container starten zu können und Metrik-Daten (CPU, RAM, Requests/s, usw...) um die Applikation überwachen zu können. Um Sicherheit zu gewährleisten wird eine Anbindung an Vault und die Absicherung durch HMAC vorausgesetzt.
Aus Applikationssicht wird das Einlesen von hierarchischer, umgebungsspezifischer Konfiguration und das Loggen nach Gelf & Kafka benötigt.
Optionale Anteile der Mikroservice Frameworks sind Anbindungen an Datenbanken, Unterstützung von Feature Toggles, Bereitstellung von Jobs und Nutzung von Applikations-Cache.
Bevor ein Team spezifisch in seiner gewählten Technologie einen Microservice schreibt, entwickelt es den beschriebenen abstrakten Rahmen. Ihr ahnt es schon, wenn es bereits einen passenden abstrakten Rahmen gibt, kann das Team diesen wiederverwenden. Das führt dazu, dass sobald es in die Entwicklung der eigentlichen Anwendung geht, sich das Team genau auf diese konzentrieren kann und nicht mehr den betrieblichen Rahmen schaffen muss.
Jeder von den drei Herren repräsentiert eines unser bereits entwickelten Microservice Frameworks. Tesla ist in Clojure geschrieben, Edison mit SpringBoot in Java und Turing in NodeJS. An weiteren Services, wie z.B. in Scala mit Akka, wird gearbeitet.
Wer dieses Blog schon länger verfolgt und genau aufgepasst hat, könnte meinen, diese Herangehensweise würde unserem Bedürfnis nach Autonomie widersprechen. Die Microservice Frameworks sind im Prinzip shared Code und den wollen wir nicht. Um diesen Widerspruch aufzulösen und aus vielen weiteren guten Gründen sind unsere Microservice Frameworks Open Source. So teilen wir uns zwischen den Teams keinen Code, der Abhängigkeiten zwischen den Teams erzeugt, sondern lediglich eine Abhängigkeit zu einem öffentlichen Projekt, das von vielen Teams und auch Externen weiterentwickelt wird. - Was meint ihr, ist die Autonomie der Teams dadurch gesichert?
Wenn ihr Lust habt, mit an den Frameworks weiter zu entwickeln oder ihr die Servies mal ausprobieren wollt, seid ihr herzlich dazu eingeladen. Alle Informationen dazu findet ihr im jeweiligen github Repository:
Abschließend möchte ich euch noch herzlich dazu einladen mehr Details und praktische Live-Coding Beispiele zu diesem Thema, während meins talks, im September, auf der CodeTalks, in Hamburg, zu erfahren.
Ich freue mich schon auf eure Fragen!
We have received your feedback.