Zum Inhalt wechseln

Praktische gewonnene Erkenntnisse aus dem Aufbau einer Microservices-Plattform

Technology

6 April 2021 - 4 Minuten lesen

1573 Blog Microservices Platform 416X300
Objectivity´s  Entwicklungsabteilung 
Alle Beiträge von Objectivity´s anzeigen

1394 DE Resources Thumbs

Teilen

Inhaltsverzeichnis

  1. Design & Architektur
  2. Versionierung & Verträge
  3. Back-End
  4. Infrastruktur
  5. Migration to Service Mesh 
  6. Zusammenfassung

Wir arbeiten mit mehreren Kunden in vielen verschiedenen Bereichen. Für einen unserer Kunden aus der Immobilienbranche haben wir eine Greenfield Microservices-Plattform aufgebaut. Ursprünglich sollten wir acht Dienste implementieren, aber diese Zahl stieg später auf siebzehn. Die Technologien, die wir verwendet haben, umfassen:

  • Frontend: Angular, TypeScript, leaflet.js (for maps) 
  • Backend: .NET Core, EF Core, Kubernetes cluster, Managed PostgreSQL, Azure Service Bus/RabbitMq 

In diesem Artikel möchten wir die interessantesten Erkenntnisse erörtern, welche wir während des Projekts gewonnen haben.

Design & Architektur

Zu Beginn und während einer Event Storming Session, trotz der begrenzten Geschäftskenntnisse und unvollständigen Anforderungen, führte es zu einer ausreichenden Aufteilung der Bereiche in Microservices.

Die meisten Dienste waren geschäftlich verwurzelt und nur zwei (Benachrichtigungen und Datenhub) waren technisch fokussiert. Dies war ein Ergebnis einer unklaren Sicht, z. B. wie man die von IoT-Sensoren erfassten Daten verarbeitet. Aufgrund der Anforderungen der DSGVO und der Benutzeranonymisierung haben wir nur benutzerbezogene Daten in einem einzigen Dienst gespeichert. Andere Microservices verweisen über einen künstlichen Schlüssel (UserId) auf den Benutzer.

Während dieses Projekts haben wir unsere Überzeugung bekräftigt, dass die frühzeitige Erstellung des API-Gateways eine lohnende Investition ist. Dank dessen können Sie die Entwicklung der Anwendung des Kunden vereinfachen. In unserem Fall hatten wir eine Web-Benutzeroberfläche und eine mobile App, da die genaue Aufteilung der Funktionen zwischen Microservices und Datenabhängigkeiten nicht erkannt werden muss.

Versionierung & Verträge

Wir haben Dienste oder Ereignisse, die zwischen Diensten ausgetauscht wurden, nicht separat versioniert. Unser Team war relativ klein, es bestand aus weniger als 10 Entwicklern, die fast alle auf der Plattform arbeiteten. Daher war es für uns einfacher, alle Änderungen auf einmal vorzunehmen und sie den Benutzern zur Verfügung zu stellen. Dieser Ansatz war schnell und projekttauglich.

Während der Entwicklungsphase haben wir von vielen Änderungen an den Serviceverträgen erfahren. Wenn mehr Teams auf der Plattform gearbeitet hätten, wäre viel Zeit verloren gegangen für die Kommunikation und die Aufteilung des gesamten Backlogs auf die Teams.

Back-End

Es ist wichtig Lesevorgänge in der Microservice-Architektur immer zu berücksichtigen. Sehr oft enthält ein einzelner Microservice nur eine ID anstelle des vollständigen Satzes von Attributen für ein Objekt. Im Allgemeinen gibt es zwei Möglichkeiten, um dieses Problem zu lösen:

  • Objektattribute zwischen Diensten kopieren - Dies kann der vollständige Satz von Attributen oder nur eine Teilmenge sein, die für den jeweiligen Microservice erforderlich ist. Der Hauptvorteil dieses Ansatzes ist die Skalierbarkeit und Ausfallsicherheit von Microservices - alle arbeiten unabhängig voneinander. Der Nachteil ist, dass Sie bei jeder Änderung der Quelldaten eine Kopie aktualisieren müssen. Dies erzeugt mehr Arbeit und es kommt zu einer Synchronisationsverzögerung.
  • API-Gateway - ein dedizierter Microservice, der andere Dienste erreicht und Daten für den API-Endbenutzer kombiniert. Zu den Vorteilen zählen das Fehlen von Datenvervielfältigung und insgesamt weniger Code. Dies geht jedoch zu Lasten einer verringerten Skalierbarkeit und Verfügbarkeit.

Die dritte Alternative könnte darin bestehen, mehrere Dienste direkt in der UI-Schicht zu kombinieren. Dies würde jedoch für dieses Projekt nicht funktionieren. Wir hatten mehrere Benutzeroberflächen (Web und Mobil), sodass dieser Weg den größten Teil der Logik duplizieren würde.

Wir haben CQRS Light in einer einzigen API verwendet. Dies bedeutet, dass wir separate Lese- und Schreibpipelines hatten, aber nur eine einzige Datenbank. Dies ist ein recht flexibler Ansatz, mit dem Sie überhöhte Domänenklassen vermeiden können, solange Sie sich an die Trennungsregeln halten. Darüber hinaus können Sie mit dieser Methode leichtes ORM (wie Dapper) für Abfragen und ein Standard-ORM (Entity Framework) für Befehle verwenden.

Infrastruktur

Unsere Microservices-Plattform sollte Cloud-unabhängig sein. Infolgedessen mussten wir uns mit verschiedenen Queues der Implementierung befassen (Azure Service Bus und RabbitMq). In solchen Situationen ist es besser, die dienstübergreifende Kommunikation vom ersten Tag an über mehrere Queues zu modellieren. Auf diese Weise ist es einfacher, sich an andere Datenstrukturen anzupassen. Leider haben wir den Fehler gemacht, mit einer gemeinsamen Queue auf Azure Service Bus zu beginnen.

Die Trennung der Domain-Logik von Infrastruktur- und Integrationscode ist eine gute und bekannte Praxis, muss jedoch ordnungsgemäß durchgeführt werden. Wenn Sie nicht wachsam bleiben, akzeptieren Sie möglicherweise zu viele Verknüpfungen unter dem Druck der Zustellung. Wir empfehlen, implementierungsunabhängige Schnittstellen und die Inversion von Abhängigkeiten zu definieren, damit der Infrastrukturcode nicht in den Domänencode gelangt.

Protokollierung und Überwachung sind Grundlagen der verteilten Systeme. In Monolithen können Sie im Laufe der Zeit dazugefügt werden, aber im Fall von Microservices zeigen Ihnen die ersten Fehler, wie notwendig diese sind.

Die IoT-Integration verlief reibungslos und recht schnell. Tatsächlich haben wir mehr Zeit gebraucht, um physisch an Geräte zu gelangen, als die PoC-Integration durchzuführen.

Wir empfehlen ein zentrales Container-Repository anstelle mehrerer in verschiedenen Clouds (für uns standen Azure- und IBM-Cloud zur Verfügung). Auf diese Weise vermeiden Sie das Duplizieren von Docker-Images, welche unnötige Speicherkosten verursachen.

Migration to Service Mesh 

Irgendwann wurde eine strategische Entscheidung getroffen, dass wir in Richtung Service Mesh migrieren müssen und Istio wurde dazu als Implementierungswerkzeug ausgewählt.

Wir haben herausgefunden, dass das verteilte System, das auf asynchroner Kommunikation und den Queues zwischen Diensten basiert, recht einfach auf die für Service Mesh erforderliche Kommunikation im Anforderungs- und Antwortstil übernommen werden kann. Diese Änderung wirkte sich offensichtlich auf die Verfügbarkeit und Leistung aus, die wir zuvor erwähnt hatten. In einer solchen Situation ist es gut zu bestätigen, dass eine solch drastische Änderung der Qualitätsmerkmale akzeptabel und vernünftig ist.

Zusammenfassung

Es sollte offensichtlich sein, dass es eine gute Idee ist, verteilte Systeme nach Möglichkeit zu vermeiden. Egal wie schwierig es ist, dies mit dem Kunden zu besprechen, allerdings empfehlen wir ihn von dieser Idee abzubringen. Microservices als Beispiel für diese Systeme werden wahrscheinlich zu einer Problemquelle, die Sie nicht lösen möchten, während Sie versuchen das Produkt auf den Markt zu bringen.

Auch wenn Sie in Zukunft möglicherweise über eine Aufteilung des Systems nachdenken, sollten Sie zuerst einen modularen Monolithen in Betracht ziehen. Auf diese Weise vermeiden Sie unnötige Probleme. Dies ist zu Beginn besonders wichtig, wenn Sie die Domäne erkunden und das Fehlerrisiko hoch ist. Dies ist vielleicht die größte Lektion, die wir aus diesem Microservices-Projekt mitgenommen haben.

1394 DE Resources Thumbs
Objectivity´s  Entwicklungsabteilung 
Alle Beiträge von Objectivity´s anzeigen

Was Sie noch interesieren könnte

Kontakt

Starten Sie Ihr Projekt mit Objectivity

CTA Pattern - Contact - Middle