- UI/Client:
- Web-UI (lokal gehostet), schlanker Desktop-Client (z. B. C++/Qt) für Edge-Geräte, je nach Interaktionsanforderung.
- Application Layer:
- Orchestriert Use-Cases, spricht Modul-APIs, kapselt Transaktionen.
- Domain-Module (Bounded Contexts):
- Jedes Modul besitzt eigene Domain-Entities, Repositories, Services, Validierungslogik. Keine Cross-Entity-Aggregate über Modulgrenzen.
- Infrastrukturadapter:
- Datenbank, Dateispeicher, Hardware-Treiber, ML-Inferenzbeschleuniger – alle als Adapter injiziert.
- Interner Event-Bus:
- Asynchrone Reaktionen in Prozessgrenzen. Für externe Integrationen: Outbox-Pattern und dedizierter Dispatcher.
- Schnittstellen nach außen:
- Stabile Integrations-API (gRPC/HTTP) mit klarer Versionierung, vorzugsweise binary-serialisiert (z. B. Protobuf) bei hohen Performanceansprüchen.
- Observability:
- Korrelierte Logs mit Request-IDs, Metriken pro Modul, in-proc Tracing-Spans. Export optional an eine On-Prem Observability-Lösung.
Zu Datenhoheit und Backups:
- Ein zentrales Datenbank-Backup deckt alle Module konsistent ab.
- Restore-Strategien sind einfacher – keine verteilten Zeitpunkte verschiedener Datenhaltungen.
Interaktion mit ML-Komponenten in einem Monolithen
In Industrieumgebungen ist ML oft Bestandteil, aber nicht der Kern. Zwei robuste Integrationswege:
- In-Prozess-Inferenz:
- Für deterministische Latenz (z. B. visuelle Qualitätsprüfung am Edge). Modelle als artefacts versionieren, Laden via Inferenz-Runtime, Ressourcen strikt steuern (z. B. GPU-Pinning).
- Sidecar-Prozess am gleichen Host:
- Wenn die Laufzeit heterogen ist (Python-Ökosystem, native Treiber) oder Isolation gewünscht ist. Kommunikation lokal via gRPC/Shared Memory/ZeroMQ. Überwachung via Watchdog, Neustart-Policy, Heartbeats.
Beide Varianten halten Netzwerkausfälle aus der Gleichung, bleiben auditierbar und erleichtern Reproduzierbarkeit.
Betriebsaspekte: Was konkret einfacher ist
Ein modularer Monolith zahlt sich an folgenden operativen Stellen aus:
- Deployment:
- Ein Artefakt, ein Installationspfad. Blue/Green auf Host-Ebene mit zwei Installationsslots und Umschaltung via Symlink/Service-Datei.
- Konfiguration:
- Zentrale Config mit Modulsektionen; Validierung beim Start; Feature-Flags zentral.
- Sicherheit:
- Harter Prozesskontext, minimierte Netzwerkoberfläche (oft nur UI/API-Port). Secrets im Host-Keystore, Rotation kontrolliert.
- Migrationskette:
- Versionierte, vorwärts- und rückwärtskompatible Migrationsskripte; Preflight-Checks (freie Disk, Locks, Backup erfolgte?).
- Monitoring und Alarmierung:
- Ein Health-Endpoint, modulare Sub-Checks; Alarmierung mit eindeutigen Ursachen.
Skalierung und Performance im Monolithen
Skalierung ist nicht nur “mehr Instanzen”. In On-Prem-Workloads geht es oft um Scale-up und planbare Auslastung:
- Concurrency-Modelle:
- Work-Stealing-Threadpools, Actor-Model in-proc, Backpressure an Queue-Grenzen. Keine Netzwerklatenz, dadurch stabileres Scheduling.
- CPU/GPU-Zuteilung:
- Feste Core-Pinning-Strategien, deterministische Latenzbänder, Prioritäten für Echtzeitanteile (soweit OS-seitig unterstützt).
- I/O-Pfade:
- Direkter Dateizugriff, gepufferte Schreibpfade, Batch-Kompression; keine Netzwerkrundreisen zwischen Services.
- Hotspots isolieren:
- Wenn ein Teil massiv rechenintensiv ist, als lokales Sidecar trennen, nicht zwingend als verteilten Service. So bleiben Deployments atomar.
Wann Microservices dennoch die bessere Wahl sind
Es gibt klare Fälle, in denen Microservices trotz On-Prem sinnvoll sind:
- Heterogene Deployability:
- Teile müssen auf verschiedenen Knoten laufen (z. B. Edge-Gateway, Zentralsystem, isolierte Netzzonen) und brauchen unabhängige Releasezyklen.
- Abweichende Skalierungsprofile:
- Ein Modul benötigt massiv mehr Durchsatz/Speicher (z. B. Videotranskodierung), das restliche System nicht.
- Rechtlich getrennte Datenräume:
- Harte Trennungen durch regulatorische Vorgaben, die nicht nur logisch, sondern physisch/organisatorisch entkoppelt sein müssen.
- Multi-Tenant mit stark variierender Mandantenzahl:
- Separate Ressourcenprofile, isolierte Upgrades pro Mandant, klare “blast radius”-Begrenzung.
- Teamgröße und Autonomie:
- Mehrere dedizierte Teams mit klar getrennten Verantwortungsbereichen, die unabhängig liefern müssen – inklusive Operations.
Auch dann gilt: Microservices sind kein All-inclusive-Paket. Ohne disziplinierte Schnittstellen, Versionierungsstrategie, Observability und ein tragfähiges Betriebsmodell wird die Verteilung zum Selbstzweck.
Migrationspfad: Vom modularen Monolithen zum Service-Schnitt
Ein Vorteil des modularen Monolithen ist der klare Exit: Wenn ein Modul die Kriterien für Eigenständigkeit erfüllt, kann man es extrahieren. Vorgehensweise, die sich bewährt hat:
- Grenzen verhärten:
- Modul-API sauber halten, keine Querverweise. Alle fachlichen Interaktionen durch definierte Schnittstellen.
- Events kanonisieren:
- Domain Events standardisieren (Namen, Schemas, Semantik). Zunächst in-proc nutzen, später über einen Broker transportieren.
- Outbox vorbereiten:
- Schreibvorgänge, die externe Reaktionen auslösen, über Outbox persistieren. So ist der Schritt zum verteilten Eventing kleiner.
- Anti-Corruption Layer:
- Beim Herauslösen neue Adapter für Persistenz/Transport bauen, alte Modul-Clients zunächst gegen ACL mappen.
- Side-by-Side betreiben:
- Neues Service parallel mitlaufen lassen, Telemetrie vergleichen, schrittweise Traffic umlegen.
Entscheidungs-Checkliste: Monolith oder Microservices?
Folgende Heuristiken setzen wir in Projekten an. Sie ersetzen keine Architekturarbeit, reduzieren aber Bauchentscheidungen:
Ein modularer Monolith ist in der Regel im Vorteil, wenn:
- Teamgröße bis ca. 8–12 Entwickler, kein separates SRE-Team.
- Releasezyklen im Wochen- bis Monatsrhythmus, klare Wartungsfenster.
- Harte On-Prem-Auflagen, keine bequeme horizontale Skalierung über Cluster.
- Fachprozesse stark transaktional gekoppelt; Audits/Backups/Restores kritisch.
- Latenz deterministisch, Netzwerk unzuverlässig oder restriktiv.
- Langfristiger Betrieb mit geringen personellen Overheads gewünscht.
Microservices werden attraktiv, wenn:
- Heterogene Laufzeiten/Hardware zwingend sind (GPU-Farmen, Edge-Cluster, getrennte Netze).
- Starke Unterschiede in Lastprofilen oder Verfügbarkeitszielen pro Funktionsbereich.
- Rechtliche Isolation auf Daten- und Prozessebene erforderlich ist.
- Mehrere autonome Teams mit eigenem Deployment- und Betriebsbudget existieren.
API-Design für Langlebigkeit – unabhängig von der Wahl
Egal ob Monolith oder Microservices: Schnittstellen sind Verträge, und Verträge leben länger als Implementierungen. Designprinzipien, die sich bezahlt machen: