Konsistenz und Synchronisation in verteilten Systemen
Herausforderung
Schon allein aufgrund der regulatorischen Anforderungen der BaFin ist eine Bank verpflichtet, Daten ihrer Kunden zu verwalten. Ein Finanzinstitut kann nur dann für einen Kunden verantwortungsvoll Finanz-Transaktionen durchführen, wenn es den Kunden kennt: Know-Your-Customer. Die Qualität der Kundendaten wird durch eine zentrale Datenhaltung sichergestellt. Nicht selten werden Kundendaten allerdings auch dezentral benötigt. Beispielsweise ist für die Verwaltung von Krediten eine Standardanwendung im Einsatz, die Teile der Kundendaten in ihrer eigenen Anwendung erwartet. Für solche Anwendungen ist eine Datensynchronisation im Einsatz, um eine Kopie der Kundendaten in der Kreditanwendung aktuell zu halten.
Die Anforderungen an die Datensynchronisation sind überschaubar. Die abzulösende Altanwendung, die mehr als 20 Abnehmer (Konsumenten) mit den Daten aus 170 Tabellen versorgt, hat uns aber gezeigt, dass aus einer einfachen Anforderung eine hochkomplexe Anwendung werden kann. Die Herausforderung besteht also darin, zu einer einfachen Anforderung auch eine einfache Lösung zu finden.
Vorgehen
Die Komplexität einer Lösung ist das Ergebnis der unterschiedlichen Lösungsansätze des Software-Entwicklungs-Prozesses. Die folgenden Entscheidungen sind wichtig gewesen, um für den Prozess der Datensynchronisation eine einfache und wirtschaftlich günstige Lösung zu finden.
Schnittstellen-Design:
- Biete die Synchronisation als Service an: Die Anforderungen von 20 Konsumenten lassen sich nur schwer konsolidieren. Stattdessen wird die Datensynchronisation als Service angeboten. Der Konsument abonniert die Synchronisation von Objekten.
- Verwende das Objekt-Modell des Daten-Owners: Datenobjekte werden dem Konsumenten zugestellt. Für die Form und die Semantik der Daten wird auf das Objekt-Modell der zentralen Kundenverwaltung zurückgegriffen. Damit wird der Aufwand der Service-Beschreibung minimiert. Das Vorgehen stellt sicher, dass kein Attribut der Kundendaten vergessen wird. Die Schnittstelle ist so stabil wie die Datenstruktur der zentralen Kundenverwaltung.
- IST: Erstelle Aggregate aus zusammengehörigen Objekten: Die Datensynchronisation verfolgt das Need-To-Know-Prinzip. Der Konsument bekommt die Objekte angeliefert, die er benötigt. So kann ein Konsument Adressdaten zu Kunden bestellen. Die Adresse gehört zu dem Aggregat Person. Die Zuordnung erfolgt über die Primär-Schlüssel der Person.
Lösungs-Design:
- Führe die Datensynchronisation asynchron aus: So entkoppeln wir die Kundenverwaltung von der Aufgabe der Datensynchronisation. Die Kunden-Verwaltung wartet nicht, bis alle Konsumenten die Daten synchronisiert haben. Die Kundenverwaltung erzeugt nur eine Notifikation, die angibt, welches Objekt in welchem Aggregat verändert wird.
- Separation of Concern: Erstellung von Data-Events erfolgt in eigener Anwendung: Der Converter hat die Aufgabe, die Notifikation der Kundenverwaltung zu verarbeiten. Der Converter erstellt die Data-Events, die den Konsumenten asynchron angeboten werden.
- Biete neuen Konsumenten ein bekanntes Konzept an: Data-Events stellen eine Implementierung des Konzepts Event-Driven SOA dar. Der Service wird durch die Data-Events definiert. Erzeuger und Konsumenten sind voneinander entkoppelt.
Implementierung:
- Entwickle evolutionär. Verwende das Open-Closed-Principle. Die Datensynchronisation wurde evolutionär entwickelt. Sukzessive wurden Data-Events in das Service-Angebot aufgenommen. So ließen sich ,Kinderkrankheiten' mit geringem Aufwand heilen. Das Open-Closed-Principle ließ sich durch die Einführung von Data-Event-Buildern sicherstellen. Ein Data-Event-Builder ist für die Erstellung genau eines Data-Events verantwortlich. Data-Events lassen sich hinzufügen, indem weitere Data-Event-Builder hinzugefügt werden. Der übrige Code bleibt unverändert.
- Setze Domain-Specific-Language für komplexe Entscheidungs-Logik ein. Die Entscheidung, ob ein Data-Event erzeugt werden soll, hat eine gewisse Komplexität. Die zugehörigen Regeln müssen deutlich im Code erkennbar sein. Dazu sind die Regeln als Predicate-Objekte definiert. Die logische Semantik mit and und or ist durch die API java.util.function.Predicate bereits implementiert. Mithilfe von Predicates ist die Entscheidungs-Logik in vom Menschen lesbarer Form implementiert.
- Verwende Contract-Tests als Unit-Tests. Die Qualität der Implementierung wird durch Modul-Tests sichergestellt. Die Besonderheit dieser Implementierung ist jedoch das Zusammenwirken der Data-Event-Builder. Werden genau die erforderlichen Data-Events produziert? Nicht weniger? Aber auch nicht mehr? Diese Überprüfung findet in Form von Szenario-basierten Unit-Tests statt. Vorgegeben werden dabei Notifications - die erwarteten Data-Events werden überprüft. Die Szenarien sind in Text-Dateien - sogenannten Contracts - formuliert. Neben der Absicherung der Qualität sind diese Contract-Tests bereits bei der Entwicklung hilfreich. Der Contract kann zu Beginn der Entwicklung eines neuen Data-Events stehen: Ein Test-First-Ansatz wird dadurch leicht gemacht.
Kundennutzen/ Mehrwert
Für den Kunden war es wichtig, zeitnah erste Data-Events zur produzieren. Bereits nach wenigen Wochen ging die Datensynchronisierung mit ersten Data-Events in Produktion. Die Abstimmung mit den Konsumenten der Data-Events erfolgte reibungslos: Eine klare Definition des Angebots der Data-Events machte deutlich, dass der Service eine umfassende Datensynchronisation ermöglicht. Die Konsumenten haben ihren Anteil der Datensynchronisation als Aufgabe akzeptiert.
Die Implementierung der Datensynchronisation war effizient: Innerhalb eines Jahres konnte die lückenlose Datensynchronisation von einem Team mit drei Entwicklern umgesetzt werden. Auch hohe Performance-Anforderungen ließen sich durch eine horizontale Skalierung erfüllen. Die Anwendung läuft seit mehr als einem Jahr stabil und fehlerfrei.
Diese durchweg positive Lösung war eine Team-Leistung. Wir als ORDIX-Mitarbeiter haben unsere Erfahrung und unser Know-how in das Team eingebracht. Als Team haben wir Entscheidungen getroffen und diese entschieden umgesetzt.
Kunde
Unser Kunde ist eine führende Großbank mit Hauptsitz in Deutschland. Die geschäftlichen Schwerpunkte liegen dabei im Bereich von Privat- und Firmenkunden, für die ein umfassendes Portfolio an Finanzdienstleistungen angeboten wird.
Branche: Banken
Methoden & Technologien
Business-Analyse:Service-Design, Business-Object-Model
Architektur:
Event-Driven-SOA, Separation of Concern, Domain Driven Design, asynchrone Kommunikation
Implementierung:
Evolutionäre Entwicklung, Open-Closed-Principle, Open-Closed-Principle, Domain-Specific-Language, Contract-Testing
Vorgehen:
Agile Entwicklung mit SCRUM
Basis-Technologien:
Java, Spring-Boot
Nachrichtenaustausch:
Camel, JMS, Kafka
Systemintegration:
Open-Shift
Build:
Maven, Bitbucket, Team City