Als ich angefangen habe, war unser System ein dicker, fetter Monolith. So ein richtig schwerfälliger Koloss, der sich nur mit viel Mühe bewegen liess. Klar, einige mutige Seelen hatten schon versucht, ein paar Teile herauszulösen, aber so richtig durchgezogen wurde das nicht.

Schöne neue Welt – oder doch nicht? Link zu Überschrift

Ein halbes Jahr nach meinem Start kam die Idee auf, viele Teile der Entwicklung nach Indien auszulagern. Und mit “viele Teile” meinte man auch gleich die gesamte Architektur. Spoiler: Das ging ordentlich schief.

Als Plan B wurde mir dann die Aufgabe übertragen, zwei grosse Bestandteile aus der Applikation herauszulösen. Damit das “einfach” bleibt, entschieden wir uns, die bestehende Layered Architecture horizontal zu spalten. Ja, im Nachhinein eine eher dämliche Idee, aber man lernt ja dazu.

So sah das Ganze dann aus: Link zu Überschrift

overview

Cockpit:

  • Service
  • Web (Frontend)
  • Web & App (Gateway)
    • Security
    • Error Handling
  • Config (Security & Web)

Algo-v3:

  • Service
  • “Microservice” für alle Planungsaufgaben
  • Simulation

Core-lib:

  • Java Library
  • Services-Layer
  • Default Config
  • Externe Schnittstellen (Twilio, Stream, GCP, etc.)
  • Utils/Helper

Domain:

  • Java Library
  • Mapping eines Modells auf eine Datenquelle
  • Datenabfragen
  • Durchsetzen von Regeln (String-Länge, Pflichtfelder, Standardwerte, etc.)

Skalierung & Resilienz Link zu Überschrift

Ein Hauptgrund für die Auftrennung war natürlich die Skalierung. Denn einfach nur mehr RAM und CPU draufzuhauen war nicht die eleganteste Lösung. Also: Horizontal skalieren!

Problem: Die Applikation hatte ein internes Scheduling. Heisst: Mehr Instanzen führten nicht zu Lastverteilung, sondern nur zu mehr Chaos.

Lösung: Scheduling raus und in den GCP Cloud Scheduler packen. Jetzt konnte die Applikation mehrfach laufen, aber trotzdem landete die gesamte Last nur auf einer Instanz – dumm gelaufen. Nächster Schritt: Geschedulte Jobs pro Tenant in eine Queue (GCP PubSub) packen und von den Instanzen abonnieren lassen. Zack, Last verteilt.

overview

Nice Job! Einfach, pragmatisch, funktioniert.

Microservices? Link zu Überschrift

Yeah, wir haben jetzt eine Microservice-Architektur! Oder? Naja, schnell kamen noch ein paar weitere Services dazu – einer für Notifications, einer für IoT-Daten… Und was passierte? Microservices-Explosion!

  • Kaum managebar für unser kleines Team
  • Wildwuchs an Services, die keiner mehr überblicken könnte

Microservices 2.0 – Regeln müssen her Link zu Überschrift

Also: Wann sollten wir einen Service splitten?

Allgemeine Gründe: Link zu Überschrift

  • Technologie-Heterogenität
  • Resilienz
  • Skalierung
  • Deployment-Erleichterung
  • Organisatorische Ausrichtung
  • Ersetzbarkeit

Unsere Gründe: Link zu Überschrift

  • Skalierbarkeit: Grosse Services sind schwer zu managen und zu skalieren. Kleinere Services können gezielt optimiert werden.
  • Resilienz: Kritische Services müssen robust und ausfallsicher sein.
  • Wartung: Kleinere Services sind einfacher zu warten und weiterzuentwickeln.

Domain-Driven Design (DDD) – Struktur, aber mit Hirn Link zu Überschrift

Wir wollen sauberen Code mit klaren Grenzen. Aber nicht alles muss ein eigener Service sein. Also: DDD!

Ein Service sollte klar definierte Inhalte haben, und diese sollten gut gekapselt sein.

Also: Brainstorming! Welche Domains gibt es? Wie passen sie zusammen? Wo ziehen wir die Grenzen?

overview
overview

Wunscharchitecture Link zu Überschrift

Aus diesem Prozess entstand eine neue Architektur mit zwei Hauptservices:

  • EconomicEntityService: Enthält Themen wie IoT, Billing, Dokumente und weitere allgemeine Domains.
  • CleaningService: Beinhaltet Touren, Kalender, Flächentypen und alles, was reinigungsspezifisch ist.

Zusätzlich gibt es einige Applikationen, die über den Gateway auf diese Services zugreifen, einen Auth-Service, einige kleinere Helfer-Services sowie die Planungs- und Simulationsservices, die als Worker laufen sollen.

overview

Umsetzung – Theorie vs. Praxis Link zu Überschrift

So einfach das Zeichnen von schönen Diagrammen war, so schwierig wurde die Umsetzung.

Kaum begannen wir, die ersten Domains zu verschieben, wurde klar: Der Code war Chaos.

  • Domain Layer & Service Layer? Wild verknüpft.
  • Entities? Reihenweise ineinander verschachtelt.

Also: Viel Arbeit, viel Frust – und ein ernüchterndes Ergebnis.

Dann kam noch die Teamverschlankung (Indien wieder raus). Und das Thema? Erstmal auf Eis gelegt…