10 Design und Entwicklung von Microservices 3

10.1 Datenbankstrategien: Shared Database vs. Database per Service

Die Datenhaltungsstrategie ist eine der fundamentalsten und gleichzeitig kritischsten Entscheidungen beim Entwurf einer Microservices-Architektur. Die Wahl zwischen einer gemeinsam genutzten Datenbank (Shared Database) und dem Ansatz einer dedizierten Datenbank pro Service (Database per Service) hat weitreichende Auswirkungen auf die Autonomie, Skalierbarkeit, Konsistenz und Entwicklungsgeschwindigkeit des Gesamtsystems. In diesem Abschnitt untersuchen wir beide Strategien im Detail, diskutieren ihre jeweiligen Vor- und Nachteile und betrachten Hybridansätze sowie Migrationspfade für bestehende Systeme.

10.1.1 Die fundamentale Datenhoheit in Microservices

Bevor wir die spezifischen Datenbankstrategien betrachten, ist es wichtig, das Konzept der Datenhoheit zu verstehen, das im Kern der Microservices-Philosophie steht. Datenhoheit bezieht sich auf die Frage, welcher Service die autoritative Quelle für bestimmte Daten ist und die alleinige Verantwortung für deren Verwaltung trägt.

Das Prinzip der Datenhoheit besagt - Jedes Datenentitätstyp sollte genau einem Service gehören - Nur der besitzende Service darf diese Daten direkt ändern - Andere Services dürfen diese Daten nur über die definierten APIs des besitzenden Services abfragen oder modifizieren

Diese klare Zuordnung von Datenverantwortlichkeit ist ein Schlüsselelement für die Autonomie und lose Kopplung von Microservices. Die Datenbankstrategie sollte dieses Prinzip unterstützen und nicht untergraben.

10.1.2 Shared Database: Die gemeinsame Datenbasis

Die Shared-Database-Strategie beinhaltet die Nutzung einer gemeinsamen Datenbankinstanz (oder eines Clusters) durch mehrere oder alle Microservices im System.

10.1.2.1 Varianten der Shared-Database-Strategie

Es gibt verschiedene Ausprägungen dieses Ansatzes mit unterschiedlichen Isolationsgraden:

1. Gemeinsame Tabellen/Kollektionen

Die Microservices arbeiten auf demselben Datenbankschema und teilen sich Tabellen - Mehrere Services lesen und schreiben in dieselben Tabellen - Keine physische oder logische Trennung der Daten - Stärkste Form der Kopplung

2. Separate Schemas in gemeinsamer Datenbank

Jeder Service erhält ein eigenes Schema innerhalb derselben Datenbankinstanz - Logische Trennung der Daten durch Schemas - Bessere Isolierung und Zugriffskontrolle - Weiterhin gemeinsame Ressourcen und potenzielle Beeinflussungen

3. Service-spezifische Tabellen mit definierten Schnittstellen

Jeder Service “besitzt” bestimmte Tabellen, während andere Services über definierte Datenbankschnittstellen (Views, Stored Procedures) zugreifen - Klar definierte Datenhoheit - Kontrollierte Interaktionen über Datenbankschnittstellen - Reduzierte, aber weiterhin vorhandene Kopplung

10.1.2.2 Vorteile der Shared-Database-Strategie

1. Einfachere Datenintegrität

2. Einfachere Abfragen und Berichte

3. Reduzierte operationale Komplexität

4. Geringere technische Hürden

10.1.2.3 Nachteile der Shared-Database-Strategie

1. Verletzte Service-Autonomie

2. Skalierungslimitationen

3. Erhöhtes Risiko für implizite Kopplung

4. Erhöhte Sicherheitsrisiken

10.1.2.4 Typische Anwendungsfälle für Shared Database

Trotz der Nachteile im Kontext reiner Microservices gibt es Szenarien, in denen eine gemeinsame Datenbank eine pragmatische Wahl sein kann:

1. Übergangsarchitekturen - Als Zwischenschritt bei der Migration von Monolithen zu Microservices - Bei der schrittweisen Einführung des Database-per-Service-Patterns

2. Enge funktionale Kohäsion - Für eng verbundene Services mit starker Datenkohäsion - Wenn die betroffenen Services vom selben Team entwickelt und betrieben werden

3. Analytik und Reporting - Als konsolidierte Datenquelle für Berichte und Analysen - In Form eines Data Warehouse oder Data Lake für serviceübergreifende Analysen

4. Legacy-Integration - Wenn die Integration mit Legacy-Systemen eine gemeinsame Datenbank erfordert - Als pragmatischer Kompromiss bei der schrittweisen Modernisierung

10.1.3 Database per Service: Die autonome Datenhaltung

Die Database-per-Service-Strategie weist jedem Microservice seine eigene, dedizierte Datenbank (oder Datenbankinstanz) zu. Diese Datenbank gehört exklusiv zum Service und ist die einzige direkte Datenquelle für diesen Service.

10.1.3.1 Varianten der Database-per-Service-Strategie

Auch dieser Ansatz kennt verschiedene Umsetzungsvarianten:

1. Physisch separate Datenbanken

Jeder Service hat seine eigene, vollständig unabhängige Datenbankinstanz:

2. Logisch getrennte Datenbanken

Separate Datenbanken innerhalb derselben Cluster-Infrastruktur:

3. Polyglot Persistence

Verwendung unterschiedlicher Datenbanktypen je nach Service-Anforderungen:

10.1.3.2 Vorteile der Database-per-Service-Strategie

1. Maximale Service-Autonomie

2. Klare Durchsetzung der Datenhoheit

3. Verbesserte Resilienz

4. Bessere Sicherheit und Compliance

10.1.3.3 Nachteile der Database-per-Service-Strategie

1. Komplexere Datenintegration

2. Verteilte Transaktionen und Konsistenz

3. Erhöhte operationale Komplexität

4. Steilere Lernkurve

10.1.3.4 Typische Anwendungsfälle für Database per Service

Die Database-per-Service-Strategie eignet sich besonders für:

1. Große, komplexe Systeme - Microservices-Architekturen mit vielen unabhängigen Services - Systeme mit unterschiedlichen Datenzugriffsmustern und -anforderungen

2. Organisationen mit mehreren Teams - Wenn unterschiedliche Teams für verschiedene Services verantwortlich sind - Bei autonomen, produktorientierten Teams ohne zentralisierte Datenbankadministration

3. Systeme mit differenzierten Anforderungen - Wenn verschiedene Services unterschiedliche Datenbanktypen benötigen - Bei stark variierenden Skalierungs- und Performance-Anforderungen

4. Cloud-native Anwendungen - Systeme, die für moderne Cloud-Umgebungen entwickelt werden - Architekturen, die auf container-basierte Orchestrierung wie Kubernetes setzen

10.1.4 Hybride und Übergangstrategien

In der Praxis sind reine Shared-Database- oder Database-per-Service-Ansätze selten. Stattdessen entwickeln sich oft hybride Strategien, die die Vorteile beider Ansätze zu kombinieren versuchen oder als Übergangslösungen dienen.

10.1.4.1 Datenbank-Dekomposition-Strategien

1. Domain-basierte Dekomposition

2. Skalierbarkeitsgetriebene Dekomposition

3. Strangler Fig Pattern für Datenbanken

10.1.4.2 Datenintegrationspatterns für verteilte Datenbanken

Bei der Implementierung von Database per Service werden typischerweise mehrere Patterns für die Datenintegration kombiniert:

1. API Composition

2. Command-Query Responsibility Segregation (CQRS)

3. Event-based Data Consistency

4. Data Replication und Materialized Views

10.1.4.3 Lösungsstrategien für Konsistenzherausforderungen

Die Verwaltung der Datenkonsistenz über Servicegrenzen hinweg ist eine der größten Herausforderungen in verteilten Datenbankarchitekturen:

1. Saga Pattern

2. Two-Phase Commit (2PC)

3. Outbox Pattern

4. Akzeptanz von Eventual Consistency

10.1.5 Entscheidungskriterien für die Datenbankstrategie

Die Auswahl der richtigen Datenbankstrategie sollte auf einer gründlichen Analyse der spezifischen Anforderungen und Rahmenbedingungen basieren.

10.1.5.1 Geschäftliche Faktoren

1. Organisationsstruktur - Teamorganisation und Verantwortlichkeiten - Grad der gewünschten Team-Autonomie - Vorhandene Expertise und Erfahrung

2. Geschäftskritikalität - Anforderungen an Verfügbarkeit und Ausfallsicherheit - Kosten von Inkonsistenzen oder Ausfällen - Regulatorische und Compliance-Anforderungen

3. Entwicklungsgeschwindigkeit - Gewichtung von kurzfristiger Geschwindigkeit vs. langfristiger Flexibilität - Erwartete Evolustionsgeschwindigkeit des Systems - Time-to-Market-Ziele

10.1.5.2 Technische Faktoren

1. Datenkohäsion und -kopplung - Natürliche Grenzen zwischen Datendomänen - Häufigkeit und Komplexität domänenübergreifender Abfragen - Transaktionale Anforderungen zwischen Entitäten

2. Skalierungsanforderungen - Erwartetes Datenwachstum und Anfragevolumen - Unterschiedliche Skalierungsanforderungen für verschiedene Services - Performance-kritische vs. weniger kritische Funktionen

3. Technologieanforderungen - Spezifische Datenbankanforderungen für bestimmte Services - Existierende Infrastruktur und Expertise - Cloud- vs. On-Premises-Strategien

10.1.5.3 Risikobewertung und Trade-offs

Jede Datenbankstrategie beinhaltet spezifische Risiken und Trade-offs:

Shared Database Risiken - Wachsende Kopplung zwischen Services - Eingeschränkte unabhängige Skalierbarkeit - Potenzielle Performance-Engpässe - Erschwerte Service-Evolution

Database per Service Risiken - Erhöhte Komplexität durch verteilte Daten - Herausforderungen bei der Datenkonsistenz - Operationaler Overhead - Komplexere Abfragen und Analysen

Die Entscheidung sollte die spezifischen Risiken gegen die potenziellen Vorteile abwägen und den Kontext des Projekts berücksichtigen.

10.1.6 Implementierungsbeispiele und Best Practices

Um die Diskussion zu konkretisieren, betrachten wir einige Implementierungsbeispiele und Best Practices für verschiedene Datenbankstrategien.

10.1.6.1 Beispiel: Shared Database mit Schema-Separation

Architektur - Gemeinsame PostgreSQL-Instanz - Separate Schemas für jeden Service - Schemaübergreifende Zugriffe über definierte Datenbankviews

Implementierungsdetails - Klare Zugriffsrechte pro Schema - Dokumentierte Datenbankschnittstellen - Service-spezifische Datenbank-Benutzer

Vorteile dieser Umsetzung - Einfachere operationale Verwaltung - Datensicherheit durch Zugriffssteuerung - Schrittweise Migrationsfähigkeit

10.1.6.2 Beispiel: Database per Service mit Event-based Synchronization

Architektur - Jeder Service mit eigener Datenbankinstanz - Kafka als Event-Bus für Datensynchronisation - Read-Modelle in Services für häufige Abfragen

Implementierungsdetails - Outbox-Pattern für zuverlässige Event-Publikation - Konsistente Event-Schemas mit Versionierung - Idempotente Event-Handler

Vorteile dieser Umsetzung - Hohe Service-Autonomie - Robuste asynchrone Kommunikation - Optimierte Abfrage-Performance

10.1.6.3 Best Practices für Shared Database

1. Klare Eigentümerschaft - Jede Tabelle hat einen eindeutigen Service als “Eigentümer” - Nur der Eigentümer-Service darf Schreibzugriffe durchführen - Zugriff anderer Services über definierte Schnittstellen

2. Schemamanagement - Versionierte Datenbankmigrationen - Geplante Release-Zyklen für Schemaänderungen - Rückwärtskompatibilität bei Änderungen

3. Zugriffskontrolle - Feingranulare Datenbankberechtigungen - Service-spezifische Datenbankbenutzer - Audit-Logs für Datenbankzugriffe

10.1.6.4 Best Practices für Database per Service

1. Datenreplikation - Gezielte Replikation nur der benötigten Daten - Klare Kennzeichnung von primären und replizierten Daten - Mechanismen zur Erkennung und Behebung von Inkonsistenzen

2. Event-basierte Kommunikation - Sorgfältige Modellierung von Domänenereignissen - Robuste Event-Verarbeitung mit Idempotenz - Monitoring der Event-Verarbeitung und -Verzögerung

3. API-Design - Ressourcenorientierte APIs für Datenzugriff - Unterstützung für Bulk-Operationen bei häufigen Abfragen - Konsistente Fehlerbehandlung und Status-Codes

10.1.7 Migration von Shared Database zu Database per Service

Für viele Organisationen ist die Migration von einer monolithischen Datenbank zu einer verteilten Datenbankarchitektur ein schrittweiser Prozess.

10.1.7.1 Phasenweise Migrationsstrategie

Phase 1: Analyse und Vorbereitung - Identifikation natürlicher Domänengrenzen - Analyse von Datenzugriffsmustern und -abhängigkeiten - Auswahl von Pilotkandidaten für die erste Extraktion

Phase 2: Logische Separation - Einführung von Schemas oder anderen logischen Trennungen - Implementierung von Service-spezifischen Datenbankzugriffsschichten - Durchsetzung der Zugriffsdisziplin über explizite Schnittstellen

Phase 3: Schrittweise Extraktion - Beginnend mit unabhängigeren Services oder Domänen - Implementierung von Synchronisierungsmechanismen während der Übergangsphase - Umstellung der Anwendungslogik auf neue Datenquellen

Phase 4: Optimierung und Konsolidierung - Entfernung temporärer Synchronisierungsmechanismen - Optimierung der Service-übergreifenden Kommunikation - Vollständige Nutzung serviceoptimierter Datenbanktechnologien

10.1.7.2 Herausforderungen und Lösungsansätze

1. Datenabhängigkeiten - Herausforderung: Stark verwobene Datenbeziehungen - Lösung: Domänenanalyse, temporäre Datenreplikation, schrittweise Entkopplung

2. Transaktionale Integrität - Herausforderung: Aufrechterhaltung der ACID-Eigenschaften während der Migration - Lösung: Einführung von Saga-Mustern, Kompensationslogik, eventual consistency

3. Laufende Geschäftsprozesse - Herausforderung: Minimierung von Auswirkungen auf den laufenden Betrieb - Lösung: Parallelbetrieb, Feature-Flags, schrittweise Umstellung

4. Skill-Gap - Herausforderung: Fehlende Erfahrung mit verteilten Datenarchitekturen - Lösung: Schulung, externe Expertise, Pilotprojekte für Wissensaufbau

10.1.7.3 Tools und Techniken für die Migration

10.1.8 Die richtige Balance finden

Die Wahl zwischen Shared Database und Database per Service ist keine binäre Entscheidung, sondern erfordert eine nuancierte Betrachtung der spezifischen Anforderungen und Kontexte. In vielen Fällen ist ein hybrider oder evolutionärer Ansatz die pragmatischste Lösung.

Die ideale Datenbankstrategie für Microservices sollte:

1. Die Geschäftsdomäne respektieren: Die Datenhaltung sollte den natürlichen Grenzen der Geschäftsdomänen folgen.

2. Die Organisation widerspiegeln: Die Datenbankarchitektur sollte die Teamstruktur und Verantwortlichkeiten unterstützen.

3. Pragmatisch sein: Die Lösung sollte die spezifischen Anforderungen und Einschränkungen des Projekts berücksichtigen, nicht dogmatische Prinzipien.

4. Evolutionär sein: Die Strategie sollte sich mit dem System und der Organisation weiterentwickeln können.

5. Komplexität managen: Die eingeführte Komplexität sollte in einem angemessenen Verhältnis zum erwarteten Nutzen stehen.

Letztendlich ist die Wahl der Datenbankstrategie eine der wichtigsten architektonischen Entscheidungen in einer Microservices-Architektur – sie beeinflusst nicht nur die technische Implementierung, sondern auch die Teamorganisation, die Entwicklungsgeschwindigkeit und die langfristige Evolutionsfähigkeit des Systems. Eine sorgfältige Abwägung der Vor- und Nachteile sowie der spezifischen Kontextfaktoren ist entscheidend für den Erfolg der gewählten Strategie.