refactor: Migrate from monolithic to modular architecture

1. **Dokumentation der Architektur:**
    - Vervollständigen Sie die C4-Diagramme im docs-Verzeichnis
    - Dokumentieren Sie die wichtigsten Architekturentscheidungen in ADRs

2. **Redis-Integration finalisieren:**
    - Implementieren Sie die verteilte Cache-Lösung für die Offline-Fähigkeit
    - Nutzen Sie Redis Streams für das Event-Sourcing
This commit is contained in:
stefan
2025-07-23 14:29:40 +02:00
parent a256622f37
commit 9282dd0eb4
52 changed files with 5648 additions and 3 deletions
@@ -0,0 +1,27 @@
# ADR-0000: Vorlage für Architekturentscheidungsaufzeichnung
## Status
[Vorgeschlagen | Akzeptiert | Veraltet | Ersetzt]
Falls ersetzt, fügen Sie einen Verweis auf die neue ADR ein: [ADR-XXXX](XXXX-filename.md)
## Kontext
Beschreiben Sie den Kontext und die Problemstellung, z.B. in freier Form mit zwei bis drei Sätzen. Sie können das Problem auch in Form einer Frage formulieren.
## Entscheidung
Beschreiben Sie die getroffene Entscheidung.
## Konsequenzen
Beschreiben Sie den resultierenden Kontext nach Anwendung der Entscheidung. Alle Konsequenzen sollten hier aufgeführt werden, nicht nur die "positiven". Eine bestimmte Entscheidung kann positive, negative und neutrale Konsequenzen haben, die alle das Team und das Projekt in der Zukunft beeinflussen.
## Betrachtete Alternativen
Welche anderen Optionen wurden in Betracht gezogen und warum wurden sie nicht gewählt?
## Referenzen
- [Link zu relevanter Dokumentation, Diskussionen usw.]
@@ -0,0 +1,27 @@
# ADR-0000: Architecture Decision Record Template
## Status
[Proposed | Accepted | Deprecated | Superseded]
If superseded, include a reference to the new ADR: [ADR-XXXX](XXXX-filename.md)
## Context
Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question.
## Decision
Describe the decision that was made.
## Consequences
Describe the resulting context after applying the decision. All consequences should be listed here, not just the "positive" ones. A particular decision may have positive, negative, and neutral consequences, but all of them affect the team and project in the future.
## Alternatives Considered
What other options were considered, and why were they not chosen?
## References
- [Link to relevant documentation, discussions, etc.]
@@ -0,0 +1,68 @@
# ADR-0001: Modulare Architektur
## Status
Akzeptiert
## Kontext
Das Meldestelle-System wurde ursprünglich als monolithische Anwendung entwickelt. Mit zunehmender Komplexität und Größe des Systems traten mehrere Herausforderungen auf:
1. Der Quellcode wurde schwer zu warten und zu verstehen
2. Entwicklungsteams mussten eng koordinieren, was die Entwicklung verlangsamte
3. Die gesamte Anwendung musste skaliert werden, auch wenn nur bestimmte Teile mehr Ressourcen benötigten
4. Technologieentscheidungen wurden durch die monolithische Architektur eingeschränkt
Das Team musste entscheiden, ob es mit dem monolithischen Ansatz fortfahren oder zu einer modulareren Architektur migrieren sollte.
## Entscheidung
Wir haben uns entschieden, von einer monolithischen Struktur zu einer modularen Architektur zu migrieren und das System in die folgenden Module zu organisieren:
- **core**: Gemeinsame Kernkomponenten
- **masterdata**: Stammdatenverwaltung
- **members**: Mitgliederverwaltung
- **horses**: Pferderegistrierung
- **events**: Veranstaltungsverwaltung
- **infrastructure**: Gemeinsame Infrastrukturkomponenten
- **client**: Client-Anwendungen
Jedes Domänenmodul (masterdata, members, horses, events) folgt einem Clean-Architecture-Ansatz mit separaten API-, Anwendungs-, Domänen-, Infrastruktur- und Service-Schichten.
## Konsequenzen
### Positive
- **Verbesserte Wartbarkeit**: Kleinere, fokussierte Module sind leichter zu verstehen und zu warten
- **Unabhängige Entwicklung**: Teams können an verschiedenen Modulen mit minimaler Koordination arbeiten
- **Selektive Skalierung**: Einzelne Module können basierend auf ihren spezifischen Anforderungen skaliert werden
- **Technologieflexibilität**: Verschiedene Module können je nach Bedarf unterschiedliche Technologien verwenden
- **Klare Grenzen**: Domänengrenzen sind explizit definiert, was die konzeptionelle Integrität des Systems verbessert
### Negative
- **Erhöhte Komplexität**: Die Gesamtsystemarchitektur ist komplexer
- **Deployment-Overhead**: Mehr Komponenten müssen bereitgestellt und verwaltet werden
- **Leistungsüberlegungen**: Modulübergreifende Kommunikation fügt Latenz hinzu
- **Migrationsaufwand**: Erheblicher Aufwand erforderlich, um von der monolithischen Struktur zu migrieren
### Neutral
- **Teamorganisation**: Teams müssen um Module statt um Features herum organisiert werden
- **Dokumentationsbedarf**: Umfassendere Dokumentation ist erforderlich, um das System als Ganzes zu verstehen
## Betrachtete Alternativen
### Erweiterter Monolith
Wir haben in Betracht gezogen, die interne Struktur des Monolithen mit besseren Modulgrenzen zu verbessern, ihn aber als eine einzige bereitstellbare Einheit zu behalten. Dies wäre einfacher bereitzustellen gewesen, hätte aber die Probleme mit der Skalierung und Technologieflexibilität nicht gelöst.
### Microservices
Wir haben einen feingranulareren Microservices-Ansatz mit vielen kleineren Diensten in Betracht gezogen. Dies hätte maximale Flexibilität geboten, aber für unsere aktuellen Bedürfnisse übermäßige Komplexität und betrieblichen Overhead eingeführt.
## Referenzen
- [Migrationshinweise in README.md](../../../../README.md#aktuelle-migrationshinweise)
- [Modular Monoliths von Simon Brown](https://simonbrown.je/blog/modularity-and-microservices/)
- [Clean Architecture von Robert C. Martin](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
@@ -0,0 +1,68 @@
# ADR-0001: Modular Architecture
## Status
Accepted
## Context
The Meldestelle system was initially developed as a monolithic application. As the system grew in complexity and size, several challenges emerged:
1. The codebase became difficult to maintain and understand
2. Development teams had to coordinate closely, slowing down development
3. Scaling the entire application was necessary even when only specific parts needed more resources
4. Technology choices were constrained by the monolithic architecture
The team needed to decide whether to continue with the monolithic approach or migrate to a more modular architecture.
## Decision
We decided to migrate from a monolithic structure to a modular architecture, organizing the system into the following modules:
- **core**: Shared core components
- **masterdata**: Master data management
- **members**: Member management
- **horses**: Horse registration
- **events**: Event management
- **infrastructure**: Shared infrastructure components
- **client**: Client applications
Each domain module (masterdata, members, horses, events) follows a clean architecture approach with separate API, application, domain, infrastructure, and service layers.
## Consequences
### Positive
- **Improved maintainability**: Smaller, focused modules are easier to understand and maintain
- **Independent development**: Teams can work on different modules with minimal coordination
- **Selective scaling**: Individual modules can be scaled based on their specific requirements
- **Technology flexibility**: Different modules can use different technologies as appropriate
- **Clear boundaries**: Domain boundaries are explicitly defined, improving the conceptual integrity of the system
### Negative
- **Increased complexity**: The overall system architecture is more complex
- **Deployment overhead**: More components to deploy and manage
- **Performance considerations**: Inter-module communication adds latency
- **Migration effort**: Significant effort required to migrate from the monolithic structure
### Neutral
- **Team organization**: Teams need to be reorganized around modules rather than features
- **Documentation needs**: More comprehensive documentation is required to understand the system as a whole
## Alternatives Considered
### Enhanced Monolith
We considered improving the internal structure of the monolith with better module boundaries but keeping it as a single deployable unit. This would have been simpler to deploy but wouldn't have addressed the scaling and technology flexibility issues.
### Microservices
We considered a more fine-grained microservices approach with many smaller services. This would have provided maximum flexibility but introduced excessive complexity and operational overhead for our current needs.
## References
- [Migration notes in README.md](../../../../README.md#aktuelle-migrationshinweise)
- [Modular Monoliths by Simon Brown](https://simonbrown.je/blog/modularity-and-microservices/)
- [Clean Architecture by Robert C. Martin](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
@@ -0,0 +1,68 @@
# ADR-0002: Domain-Driven Design
## Status
Akzeptiert
## Kontext
Mit der Weiterentwicklung des Meldestelle-Systems zur Bewältigung komplexer Geschäftsregeln für die Verwaltung von Reitsportveranstaltungen standen wir vor folgenden Herausforderungen:
1. Aufrechterhaltung einer klaren Trennung zwischen Geschäftslogik und technischen Belangen
2. Sicherstellung, dass das System das Verständnis der Domänenexperten vom Problemraum genau widerspiegelt
3. Schaffung einer gemeinsamen Sprache zwischen technischen und nicht-technischen Stakeholdern
4. Organisation des Codes in einer Weise, die die Geschäftsdomänen widerspiegelt
Wir benötigten einen architektonischen Ansatz, der diese Herausforderungen adressiert und eine solide Grundlage für die in [ADR-0001](0001-modular-architecture-de.md) beschriebene modulare Architektur bietet.
## Entscheidung
Wir haben uns entschieden, Domain-Driven Design (DDD)-Prinzipien für die Organisation unseres Quellcodes und die Gestaltung unseres Systems zu übernehmen. Dies umfasst:
1. **Ubiquitäre Sprache**: Entwicklung einer gemeinsamen Sprache, die von Domänenexperten und Entwicklern geteilt wird
2. **Bounded Contexts**: Definition expliziter Grenzen zwischen verschiedenen Domänenbereichen (masterdata, members, horses, events)
3. **Schichtenarchitektur**: Organisation jedes Domänenmoduls in Schichten:
- Domänenschicht: Enthält Domänenmodelle, Entitäten, Wertobjekte und Domänendienste
- Anwendungsschicht: Enthält Anwendungsdienste, Anwendungsfälle und Befehls-/Abfragehandler
- Infrastrukturschicht: Enthält technische Implementierungen von Repositories, Messaging usw.
- API-Schicht: Definiert die Schnittstellen für die Interaktion mit der Domäne
4. **Aggregate**: Identifizierung von Aggregat-Roots, die Konsistenzgrenzen aufrechterhalten
5. **Repositories**: Verwendung des Repository-Musters zur Abstraktion des Datenzugriffs
6. **Domänen-Events**: Verwendung von Events zur Kommunikation zwischen Bounded Contexts
## Konsequenzen
### Positive
- **Business-Technologie-Ausrichtung**: Die Codestruktur spiegelt direkt die Geschäftsdomänen wider
- **Verbesserte Kommunikation**: Ubiquitäre Sprache erleichtert die Kommunikation zwischen technischen und nicht-technischen Stakeholdern
- **Wartbarkeit**: Klare Trennung der Belange macht den Code leichter zu warten
- **Testbarkeit**: Domänenlogik kann unabhängig von Infrastrukturbelangen getestet werden
- **Flexibilität**: Änderungen in einem Bounded Context haben minimale Auswirkungen auf andere
### Negative
- **Lernkurve**: DDD-Konzepte erfordern Zeit, um sie richtig zu erlernen und anzuwenden
- **Initialer Entwicklungsaufwand**: Mehr Vorabdesign und Diskussion ist erforderlich
- **Potenzielle Überentwicklung**: Risiko, komplexe DDD-Muster anzuwenden, wo einfachere Lösungen ausreichen würden
### Neutral
- **Teamorganisation**: Teams benötigen Domänenwissen sowie technische Fähigkeiten
- **Dokumentationsbedarf**: Domänenmodelle und Bounded Contexts müssen gut dokumentiert sein
## Betrachtete Alternativen
### Transaction Script Pattern
Wir haben die Verwendung eines einfacheren Transaction Script Patterns in Betracht gezogen, bei dem die Geschäftslogik um Prozeduren statt um Domänenobjekte organisiert ist. Dies wäre anfänglich einfacher zu implementieren gewesen, wäre aber mit zunehmender Komplexität der Geschäftslogik schwieriger zu warten geworden.
### Anemic Domain Model
Wir haben die Verwendung eines anämischen Domänenmodells in Betracht gezogen, bei dem Domänenobjekte einfache Datencontainer sind und die Geschäftslogik in separaten Serviceklassen liegt. Dies wäre für Entwickler mit CRUD-basiertem Hintergrund vertrauter gewesen, hätte aber nicht die Vorteile der Kapselung und der reichhaltigen Domänenmodellierung geboten.
## Referenzen
- [Domain-Driven Design von Eric Evans](https://domainlanguage.com/ddd/)
- [Implementing Domain-Driven Design von Vaughn Vernon](https://vaughnvernon.co/?page_id=168)
- [Clean Architecture von Robert C. Martin](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
@@ -0,0 +1,68 @@
# ADR-0002: Domain-Driven Design
## Status
Accepted
## Context
As the Meldestelle system evolved to handle complex business rules for equestrian event management, we faced challenges in:
1. Maintaining a clear separation between business logic and technical concerns
2. Ensuring that the system accurately reflects the domain expert's understanding of the problem space
3. Creating a shared language between technical and non-technical stakeholders
4. Organizing code in a way that reflects the business domains
We needed an architectural approach that would address these challenges and provide a solid foundation for the modular architecture described in [ADR-0001](0001-modular-architecture.md).
## Decision
We decided to adopt Domain-Driven Design (DDD) principles for organizing our codebase and designing our system. This includes:
1. **Ubiquitous Language**: Developing a common language shared by domain experts and developers
2. **Bounded Contexts**: Defining explicit boundaries between different domain areas (masterdata, members, horses, events)
3. **Layered Architecture**: Organizing each domain module into layers:
- Domain Layer: Contains domain models, entities, value objects, and domain services
- Application Layer: Contains application services, use cases, and command/query handlers
- Infrastructure Layer: Contains technical implementations of repositories, messaging, etc.
- API Layer: Defines the interfaces for interacting with the domain
4. **Aggregates**: Identifying aggregate roots that maintain consistency boundaries
5. **Repositories**: Using the repository pattern to abstract data access
6. **Domain Events**: Using events to communicate between bounded contexts
## Consequences
### Positive
- **Business-technology alignment**: The code structure directly reflects the business domains
- **Improved communication**: Ubiquitous language facilitates communication between technical and non-technical stakeholders
- **Maintainability**: Clear separation of concerns makes the code easier to maintain
- **Testability**: Domain logic can be tested independently of infrastructure concerns
- **Flexibility**: Changes in one bounded context have minimal impact on others
### Negative
- **Learning curve**: DDD concepts require time to learn and apply correctly
- **Initial development overhead**: More upfront design and discussion is needed
- **Potential overengineering**: Risk of applying complex DDD patterns where simpler solutions would suffice
### Neutral
- **Team organization**: Teams need domain knowledge as well as technical skills
- **Documentation needs**: Domain models and bounded contexts need to be well-documented
## Alternatives Considered
### Transaction Script Pattern
We considered using a simpler transaction script pattern where business logic is organized around procedures rather than domain objects. This would have been simpler to implement initially but would have become harder to maintain as the business logic grew more complex.
### Anemic Domain Model
We considered using an anemic domain model where domain objects are simple data containers and business logic is in separate service classes. This would have been more familiar to developers coming from a CRUD-based background but would not have provided the benefits of encapsulation and rich domain modeling.
## References
- [Domain-Driven Design by Eric Evans](https://domainlanguage.com/ddd/)
- [Implementing Domain-Driven Design by Vaughn Vernon](https://vaughnvernon.co/?page_id=168)
- [Clean Architecture by Robert C. Martin](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
@@ -0,0 +1,77 @@
# ADR-0003: Microservices-Architektur
## Status
Akzeptiert
## Kontext
Nach der Entscheidung, eine modulare Architektur ([ADR-0001](0001-modular-architecture-de.md)) und Domain-Driven Design ([ADR-0002](0002-domain-driven-design-de.md)) zu übernehmen, mussten wir die Deployment-Strategie für unsere Module festlegen. Zu den wichtigsten Überlegungen gehörten:
1. Unabhängige Skalierbarkeit verschiedener Teile des Systems
2. Deployment-Unabhängigkeit, um Teams zu ermöglichen, Änderungen ohne Koordination mit anderen Teams zu veröffentlichen
3. Technologieunabhängigkeit, um verschiedenen Diensten die Verwendung unterschiedlicher Technologien nach Bedarf zu ermöglichen
4. Resilienz, um sicherzustellen, dass Ausfälle in einem Teil des Systems nicht das gesamte System beeinträchtigen
5. Klare Zuständigkeitsgrenzen, die mit den Teamverantwortlichkeiten übereinstimmen
## Entscheidung
Wir haben uns entschieden, eine Microservices-Architektur zu implementieren, bei der jedes Domänenmodul als separater Dienst bereitgestellt wird:
- **masterdata-service**: Verwaltet Stammdaten wie Standorte, Disziplinen usw.
- **members-service**: Verwaltet Mitgliederregistrierung und -profile
- **horses-service**: Verwaltet Pferderegistrierung und -informationen
- **events-service**: Verwaltet Veranstaltungserstellung, -planung und -anmeldungen
Jeder Dienst:
- Hat sein eigenes Datenbankschema
- Ist unabhängig bereitstellbar
- Kommuniziert mit anderen Diensten über klar definierte APIs und nachrichtenbasierte Kommunikation
- Ist für seine eigene Domänenlogik gemäß DDD-Prinzipien verantwortlich
Wir haben auch unterstützende Infrastrukturdienste implementiert:
- **gateway**: API-Gateway für Routing und Authentifizierung
- **auth**: Authentifizierungs- und Autorisierungsdienst (Keycloak)
- **cache**: Caching-Dienst (Redis)
- **messaging**: Message Broker für die Kommunikation zwischen Diensten (Kafka)
- **monitoring**: Überwachungs- und Beobachtbarkeitsdienste
## Konsequenzen
### Positive
- **Unabhängige Skalierbarkeit**: Jeder Dienst kann basierend auf seinen spezifischen Lastanforderungen skaliert werden
- **Deployment-Unabhängigkeit**: Teams können Änderungen an ihren Diensten bereitstellen, ohne sich mit anderen Teams abstimmen zu müssen
- **Technologieflexibilität**: Verschiedene Dienste können je nach Bedarf unterschiedliche Technologien verwenden
- **Resilienz**: Ausfälle in einem Dienst beeinträchtigen nicht unbedingt andere
- **Klare Zuständigkeit**: Jeder Dienst hat klare Zuständigkeitsgrenzen, die mit den Teamverantwortlichkeiten übereinstimmen
- **Kleinere Codebasen**: Jeder Dienst hat eine kleinere, fokussiertere Codebasis
### Negative
- **Komplexität verteilter Systeme**: Microservices bringen die Herausforderungen verteilter Systeme mit sich
- **Betrieblicher Mehraufwand**: Mehr Dienste müssen bereitgestellt, überwacht und gewartet werden
- **Herausforderungen bei der Datenkonsistenz**: Die Aufrechterhaltung der Datenkonsistenz über Dienste hinweg erfordert sorgfältiges Design
- **Netzwerklatenz**: Die Kommunikation zwischen Diensten fügt Latenz hinzu
- **Testkomplexität**: End-to-End-Tests werden komplexer
### Neutral
- **Teamorganisation**: Teams müssen um Dienste statt um Features herum organisiert werden
- **Dokumentationsbedarf**: Dienstschnittstellen und -interaktionen müssen gut dokumentiert sein
## Betrachtete Alternativen
### Modularer Monolith
Wir haben die Implementierung eines modularen Monolithen in Betracht gezogen, bei dem alle Module als eine einzige Anwendung bereitgestellt würden, jedoch mit klaren Modulgrenzen. Dies wäre einfacher bereitzustellen gewesen und hätte die Herausforderungen verteilter Systeme vermieden, hätte aber nicht die Vorteile der unabhängigen Skalierbarkeit und Bereitstellung geboten.
### Service-basierte Architektur
Wir haben eine dienstbasierte Architektur mit weniger, größeren Diensten in Betracht gezogen, die mehrere Domänenbereiche umfassen würden. Dies hätte den betrieblichen Overhead reduziert, aber es schwieriger gemacht, klare Domänengrenzen und unabhängige Skalierbarkeit aufrechtzuerhalten.
## Referenzen
- [Microservices von Martin Fowler](https://martinfowler.com/articles/microservices.html)
- [Building Microservices von Sam Newman](https://samnewman.io/books/building_microservices/)
- [Microservices Patterns von Chris Richardson](https://microservices.io/book)
@@ -0,0 +1,77 @@
# ADR-0003: Microservices Architecture
## Status
Accepted
## Context
Following the decision to adopt a modular architecture ([ADR-0001](0001-modular-architecture.md)) and Domain-Driven Design ([ADR-0002](0002-domain-driven-design.md)), we needed to determine the deployment strategy for our modules. Key considerations included:
1. Independent scalability of different parts of the system
2. Deployment independence to allow teams to release changes without coordinating with other teams
3. Technology independence to allow different services to use different technologies as appropriate
4. Resilience to ensure that failures in one part of the system don't bring down the entire system
5. Clear ownership boundaries aligned with team responsibilities
## Decision
We decided to implement a microservices architecture where each domain module is deployed as a separate service:
- **masterdata-service**: Manages master data such as locations, disciplines, etc.
- **members-service**: Manages member registration and profiles
- **horses-service**: Manages horse registration and information
- **events-service**: Manages event creation, scheduling, and registrations
Each service:
- Has its own database schema
- Is independently deployable
- Communicates with other services through well-defined APIs and message-based communication
- Is responsible for its own domain logic as per DDD principles
We also implemented supporting infrastructure services:
- **gateway**: API Gateway for routing and authentication
- **auth**: Authentication and authorization service (Keycloak)
- **cache**: Caching service (Redis)
- **messaging**: Message broker for inter-service communication (Kafka)
- **monitoring**: Monitoring and observability services
## Consequences
### Positive
- **Independent scalability**: Each service can be scaled based on its specific load requirements
- **Deployment independence**: Teams can deploy changes to their services without coordinating with other teams
- **Technology flexibility**: Different services can use different technologies as appropriate
- **Resilience**: Failures in one service don't necessarily affect others
- **Clear ownership**: Each service has clear ownership boundaries aligned with team responsibilities
- **Smaller codebases**: Each service has a smaller, more focused codebase
### Negative
- **Distributed system complexity**: Microservices introduce the challenges of distributed systems
- **Operational overhead**: More services to deploy, monitor, and maintain
- **Data consistency challenges**: Maintaining data consistency across services requires careful design
- **Network latency**: Inter-service communication adds latency
- **Testing complexity**: End-to-end testing becomes more complex
### Neutral
- **Team organization**: Teams need to be organized around services rather than features
- **Documentation needs**: Service interfaces and interactions need to be well-documented
## Alternatives Considered
### Modular Monolith
We considered implementing a modular monolith where all modules would be deployed as a single application but with clear module boundaries. This would have been simpler to deploy and would have avoided the distributed system challenges, but would not have provided the independent scalability and deployment benefits.
### Service-Based Architecture
We considered a service-based architecture with fewer, larger services that would encompass multiple domain areas. This would have reduced the operational overhead but would have made it harder to maintain clear domain boundaries and independent scalability.
## References
- [Microservices by Martin Fowler](https://martinfowler.com/articles/microservices.html)
- [Building Microservices by Sam Newman](https://samnewman.io/books/building_microservices/)
- [Microservices Patterns by Chris Richardson](https://microservices.io/book)
@@ -0,0 +1,75 @@
# ADR-0004: Ereignisgesteuerte Kommunikation
## Status
Akzeptiert
## Kontext
Mit der Einführung einer Microservices-Architektur ([ADR-0003](0003-microservices-architecture-de.md)) mussten wir die effektivste Art der Kommunikation zwischen den Diensten bestimmen. Zu den wichtigsten Überlegungen gehörten:
1. Lose Kopplung zwischen Diensten, um ihre Unabhängigkeit zu erhalten
2. Asynchrone Verarbeitungsfähigkeiten zur Verbesserung der Systemresilienz und Skalierbarkeit
3. Zuverlässige Kommunikation, um sicherzustellen, dass wichtige Informationen nicht verloren gehen
4. Unterstützung für komplexe Workflows, die mehrere Dienste umfassen
5. Fähigkeit, den Zustand des Systems für Audit- und Debugging-Zwecke zu rekonstruieren
## Entscheidung
Wir haben uns entschieden, ein ereignisgesteuertes Kommunikationsmuster mit Apache Kafka als Message Broker zu implementieren. Die wichtigsten Aspekte dieses Ansatzes umfassen:
1. **Domänen-Ereignisse**: Dienste veröffentlichen Domänen-Ereignisse, wenn signifikante Zustandsänderungen auftreten
2. **Event Sourcing**: Für kritische Daten speichern wir alle Ereignisse, die zum aktuellen Zustand geführt haben
3. **Nachrichtenbasierte Kommunikation**: Dienste kommunizieren hauptsächlich über asynchrone Nachrichten
4. **Choreographie**: Komplexe Workflows werden durch Ereignis-Choreographie statt Orchestrierung implementiert
5. **Ereignis-Schema-Registry**: Wir führen eine Registry von Ereignis-Schemas, um Kompatibilität zu gewährleisten
Die Implementierung umfasst:
- Kafka als zentraler Message Broker
- Schema-Registry zur Verwaltung von Ereignis-Schemas
- Ereignis-Handler in jedem Dienst zur Verarbeitung von Ereignissen aus anderen Diensten
- Ereignis-Publisher in jedem Dienst zur Veröffentlichung von Domänen-Ereignissen
## Konsequenzen
### Positive
- **Lose Kopplung**: Dienste sind entkoppelt und teilen nur die Ereignis-Verträge
- **Skalierbarkeit**: Asynchrone Verarbeitung ermöglicht bessere Skalierbarkeit unter Last
- **Resilienz**: Dienste können weiter funktionieren, auch wenn andere Dienste nicht verfügbar sind
- **Audit-Trail**: Event Sourcing bietet einen vollständigen Audit-Trail aller Zustandsänderungen
- **Flexibilität**: Neue Konsumenten können hinzugefügt werden, ohne Publisher zu modifizieren
### Negative
- **Eventuelle Konsistenz**: Das System ist letztendlich konsistent, was schwer zu verstehen sein kann
- **Komplexität**: Ereignisgesteuerte Systeme sind komplexer zu entwerfen, zu implementieren und zu debuggen
- **Reihenfolgegarantien**: Die korrekte Reihenfolge von Ereignissen sicherzustellen kann herausfordernd sein
- **Idempotenz**: Dienste müssen doppelte Ereignisse korrekt behandeln
- **Lernkurve**: Entwickler müssen ereignisgesteuerte Muster und Praktiken erlernen
### Neutral
- **Überwachungsbedarf**: Umfassende Überwachung ist erforderlich, um den Ereignisfluss zu verfolgen
- **Testansatz**: Teststrategien müssen asynchrones Verhalten berücksichtigen
## Betrachtete Alternativen
### Synchrone REST-APIs
Wir haben die Verwendung synchroner REST-APIs als primären Kommunikationsmechanismus in Betracht gezogen. Dies wäre einfacher zu implementieren und zu debuggen gewesen, hätte aber zu einer engeren Kopplung zwischen Diensten und verringerter Resilienz geführt.
### Request-Response-Messaging
Wir haben ein Request-Response-Messaging-Muster in Betracht gezogen, bei dem Dienste Anfragen senden und auf Antworten warten. Dies hätte einige der Vorteile asynchroner Kommunikation geboten und gleichzeitig ein vertrautes Request-Response-Modell beibehalten, hätte aber das Publish-Subscribe-Muster nicht so effektiv unterstützt.
### GraphQL-Federation
Wir haben die Verwendung von GraphQL-Federation zur Zusammensetzung von APIs aus mehreren Diensten in Betracht gezogen. Dies hätte eine einheitliche API für Clients geboten, hätte aber eine enge Kopplung zwischen Diensten beibehalten und asynchrone Workflows nicht so effektiv unterstützt.
## Referenzen
- [Enterprise Integration Patterns](https://www.enterpriseintegrationpatterns.com/)
- [Event-Driven Architecture von Martin Fowler](https://martinfowler.com/articles/201701-event-driven.html)
- [Apache Kafka Dokumentation](https://kafka.apache.org/documentation/)
- [Event Sourcing Pattern](https://docs.microsoft.com/de-de/azure/architecture/patterns/event-sourcing)
@@ -0,0 +1,75 @@
# ADR-0004: Event-Driven Communication
## Status
Accepted
## Context
With the adoption of a microservices architecture ([ADR-0003](0003-microservices-architecture.md)), we needed to determine the most effective way for services to communicate with each other. Key considerations included:
1. Loose coupling between services to maintain their independence
2. Asynchronous processing capabilities to improve system resilience and scalability
3. Reliable communication to ensure that important information is not lost
4. Support for complex workflows that span multiple services
5. Ability to reconstruct the state of the system for auditing and debugging purposes
## Decision
We decided to implement an event-driven communication pattern using Apache Kafka as the message broker. The key aspects of this approach include:
1. **Domain Events**: Services publish domain events when significant state changes occur
2. **Event Sourcing**: For critical data, we store all events that led to the current state
3. **Message-Based Communication**: Services communicate primarily through asynchronous messages
4. **Choreography**: Complex workflows are implemented through event choreography rather than orchestration
5. **Event Schema Registry**: We maintain a registry of event schemas to ensure compatibility
The implementation includes:
- Kafka as the central message broker
- Schema registry for managing event schemas
- Event handlers in each service to process events from other services
- Event publishers in each service to publish domain events
## Consequences
### Positive
- **Loose coupling**: Services are decoupled, only sharing the event contracts
- **Scalability**: Asynchronous processing allows for better scalability under load
- **Resilience**: Services can continue to function even when other services are unavailable
- **Audit trail**: Event sourcing provides a complete audit trail of all state changes
- **Flexibility**: New consumers can be added without modifying publishers
### Negative
- **Eventual consistency**: The system is eventually consistent, which can be challenging to reason about
- **Complexity**: Event-driven systems are more complex to design, implement, and debug
- **Ordering guarantees**: Ensuring the correct ordering of events can be challenging
- **Idempotency**: Services must handle duplicate events correctly
- **Learning curve**: Developers need to learn event-driven patterns and practices
### Neutral
- **Monitoring needs**: Comprehensive monitoring is required to track event flow
- **Testing approach**: Testing strategies need to account for asynchronous behavior
## Alternatives Considered
### Synchronous REST APIs
We considered using synchronous REST APIs as the primary communication mechanism. This would have been simpler to implement and debug but would have led to tighter coupling between services and reduced resilience.
### Request-Response Messaging
We considered a request-response messaging pattern where services would send requests and wait for responses. This would have provided some of the benefits of asynchronous communication while maintaining a familiar request-response model, but would not have supported the publish-subscribe pattern as effectively.
### GraphQL Federation
We considered using GraphQL federation to compose APIs from multiple services. This would have provided a unified API for clients but would have maintained tight coupling between services and would not have supported asynchronous workflows as effectively.
## References
- [Enterprise Integration Patterns](https://www.enterpriseintegrationpatterns.com/)
- [Event-Driven Architecture by Martin Fowler](https://martinfowler.com/articles/201701-event-driven.html)
- [Apache Kafka Documentation](https://kafka.apache.org/documentation/)
- [Event Sourcing Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing)
@@ -0,0 +1,81 @@
# ADR-0005: Polyglotte Persistenz
## Status
Akzeptiert
## Kontext
Als Teil unserer Microservices-Architektur ([ADR-0003](0003-microservices-architecture-de.md)) mussten wir die am besten geeignete Datenspeicherstrategie bestimmen. Verschiedene Teile unseres Systems haben unterschiedliche Anforderungen an die Datenspeicherung:
1. Einige Daten erfordern starke Konsistenz und komplexe Beziehungen
2. Einige Daten müssen mit sehr geringer Latenz abgerufen werden
3. Einige Daten sind ereignisbasiert und müssen in einem Zeitreihenformat gespeichert werden
4. Verschiedene Dienste haben unterschiedliche Datenzugriffsmuster
Ein Einheitsansatz für die Datenspeicherung würde Kompromisse erzwingen, die die Leistung, Skalierbarkeit oder Entwicklungsproduktivität beeinträchtigen könnten.
## Entscheidung
Wir haben uns entschieden, eine polyglotte Persistenzstrategie zu implementieren, die verschiedene Datenspeichertechnologien für verschiedene Anwendungsfälle nutzt:
1. **PostgreSQL**: Als primäre relationale Datenbank zur Speicherung strukturierter Daten mit komplexen Beziehungen
- Wird von allen Domänendiensten für ihre primäre Datenspeicherung verwendet
- Jeder Dienst hat sein eigenes Datenbankschema, um Isolation zu gewährleisten
2. **Redis**: Als verteilter Cache für schnellen Datenzugriff
- Wird für das Caching häufig abgerufener Daten verwendet
- Wird für die Sitzungsspeicherung verwendet
- Wird für Rate-Limiting verwendet
3. **Kafka**: Als Event-Store für Event Sourcing
- Wird zur Speicherung von Domänenereignissen für Event Sourcing verwendet
- Ermöglicht Event-Replay zum Wiederaufbau des Zustands
4. **Elasticsearch** (geplant): Für Volltextsuchfunktionen
- Wird für erweiterte Suchfunktionen über mehrere Domänen hinweg verwendet werden
Jeder Dienst ist für die Verwaltung seiner eigenen Datenspeicherung verantwortlich, und Dienste dürfen nicht direkt auf die Datenbanken anderer Dienste zugreifen.
## Konsequenzen
### Positive
- **Optimierte Leistung**: Jede Art von Daten wird in der am besten geeigneten Speichertechnologie gespeichert
- **Skalierbarkeit**: Verschiedene Speichertechnologien können unabhängig voneinander basierend auf ihren spezifischen Anforderungen skaliert werden
- **Flexibilität**: Teams können die beste Speichertechnologie für ihre spezifischen Anwendungsfälle wählen
- **Resilienz**: Probleme mit einer Speichertechnologie beeinträchtigen nicht unbedingt andere
### Negative
- **Betriebliche Komplexität**: Mehrere Speichertechnologien müssen bereitgestellt, überwacht und gewartet werden
- **Herausforderungen bei der Datenkonsistenz**: Die Aufrechterhaltung der Konsistenz über verschiedene Speichertechnologien hinweg erfordert sorgfältiges Design
- **Lernkurve**: Teams müssen mit mehreren Speichertechnologien vertraut sein
- **Komplexität bei Backup und Wiederherstellung**: Verschiedene Speichertechnologien haben unterschiedliche Backup- und Wiederherstellungsverfahren
### Neutral
- **Daten-Governance**: Umfassende Daten-Governance ist über alle Speichertechnologien hinweg erforderlich
- **Überwachungsbedarf**: Jede Speichertechnologie erfordert ihren eigenen Überwachungsansatz
## Betrachtete Alternativen
### Einzelne Datenbank für alle Dienste
Wir haben die Verwendung einer einzelnen PostgreSQL-Datenbank mit separaten Schemas für jeden Dienst in Betracht gezogen. Dies hätte den Betrieb vereinfacht, hätte aber einen Single Point of Failure geschaffen und hätte es uns nicht ermöglicht, für verschiedene Datenzugriffsmuster zu optimieren.
### Datenbank pro Dienst, gleiche Technologie
Wir haben die Verwendung von PostgreSQL für alle Dienste, aber mit separaten Datenbanken in Betracht gezogen. Dies hätte Dienstisolation geboten und gleichzeitig den Betrieb vereinfacht, hätte es uns aber nicht ermöglicht, für verschiedene Datenzugriffsmuster zu optimieren.
### Vollständig verteilter NoSQL-Ansatz
Wir haben die Verwendung eines vollständig verteilten NoSQL-Ansatzes mit Technologien wie Cassandra oder MongoDB für die gesamte Datenspeicherung in Betracht gezogen. Dies hätte eine ausgezeichnete Skalierbarkeit geboten, hätte aber die Modellierung komplexer Beziehungen erschwert und hätte signifikante Änderungen an unseren Entwicklungspraktiken erfordert.
## Referenzen
- [Polyglot Persistence von Martin Fowler](https://martinfowler.com/bliki/PolyglotPersistence.html)
- [PostgreSQL Dokumentation](https://www.postgresql.org/docs/)
- [Redis Dokumentation](https://redis.io/documentation)
- [Apache Kafka Dokumentation](https://kafka.apache.org/documentation/)
- [Elasticsearch Dokumentation](https://www.elastic.co/guide/index.html)
@@ -0,0 +1,81 @@
# ADR-0005: Polyglot Persistence
## Status
Accepted
## Context
As part of our microservices architecture ([ADR-0003](0003-microservices-architecture.md)), we needed to determine the most appropriate data storage strategy. Different parts of our system have different data storage requirements:
1. Some data requires strong consistency and complex relationships
2. Some data needs to be accessed with very low latency
3. Some data is event-based and needs to be stored in a time-series format
4. Different services have different data access patterns
A one-size-fits-all approach to data storage would force compromises that could impact performance, scalability, or development productivity.
## Decision
We decided to implement a polyglot persistence strategy, using different data storage technologies for different use cases:
1. **PostgreSQL**: As the primary relational database for storing structured data with complex relationships
- Used by all domain services for their primary data storage
- Each service has its own database schema to maintain isolation
2. **Redis**: As a distributed cache for high-speed data access
- Used for caching frequently accessed data
- Used for session storage
- Used for rate limiting
3. **Kafka**: As an event store for event sourcing
- Used to store domain events for event sourcing
- Enables event replay for rebuilding state
4. **Elasticsearch** (planned): For full-text search capabilities
- Will be used for advanced search features across multiple domains
Each service is responsible for managing its own data storage, and services are not allowed to access each other's databases directly.
## Consequences
### Positive
- **Optimized performance**: Each type of data is stored in the most appropriate storage technology
- **Scalability**: Different storage technologies can be scaled independently based on their specific requirements
- **Flexibility**: Teams can choose the best storage technology for their specific use cases
- **Resilience**: Issues with one storage technology don't necessarily affect others
### Negative
- **Operational complexity**: Multiple storage technologies need to be deployed, monitored, and maintained
- **Data consistency challenges**: Maintaining consistency across different storage technologies requires careful design
- **Learning curve**: Teams need to be familiar with multiple storage technologies
- **Backup and recovery complexity**: Different storage technologies have different backup and recovery procedures
### Neutral
- **Data governance**: Comprehensive data governance is required across all storage technologies
- **Monitoring needs**: Each storage technology requires its own monitoring approach
## Alternatives Considered
### Single Database for All Services
We considered using a single PostgreSQL database with separate schemas for each service. This would have simplified operations but would have created a single point of failure and would not have allowed us to optimize for different data access patterns.
### Database per Service, Same Technology
We considered using PostgreSQL for all services but with separate databases. This would have provided service isolation while simplifying operations, but would not have allowed us to optimize for different data access patterns.
### Fully Distributed NoSQL Approach
We considered using a fully distributed NoSQL approach with technologies like Cassandra or MongoDB for all data storage. This would have provided excellent scalability but would have made it harder to model complex relationships and would have required significant changes to our development practices.
## References
- [Polyglot Persistence by Martin Fowler](https://martinfowler.com/bliki/PolyglotPersistence.html)
- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
- [Redis Documentation](https://redis.io/documentation)
- [Apache Kafka Documentation](https://kafka.apache.org/documentation/)
- [Elasticsearch Documentation](https://www.elastic.co/guide/index.html)
@@ -0,0 +1,81 @@
# ADR-0006: Authentifizierung und Autorisierung mit Keycloak
## Status
Akzeptiert
## Kontext
Als Teil unserer Microservices-Architektur ([ADR-0003](0003-microservices-architecture-de.md)) benötigten wir eine robuste und zentralisierte Lösung für Authentifizierung und Autorisierung. Zu den wichtigsten Anforderungen gehörten:
1. Single Sign-On (SSO) über alle Dienste und Anwendungen hinweg
2. Unterstützung für mehrere Authentifizierungsmethoden (Benutzername/Passwort, OAuth, SAML)
3. Feingranulare Autorisierung mit rollenbasierter Zugriffssteuerung (RBAC)
4. Benutzerverwaltungsfunktionen einschließlich Selbstregistrierung und Profilmanagement
5. Integration mit externen Identitätsanbietern
6. Sicherheits-Best-Practices einschließlich Passwortrichtlinien und Kontosperrung
7. Token-basierte Authentifizierung für die Kommunikation zwischen Diensten
Die Implementierung dieser Funktionen von Grund auf wäre zeitaufwändig und fehleranfällig und würde Ressourcen von unserer Kerngeschäftsfunktionalität abziehen.
## Entscheidung
Wir haben uns entschieden, Keycloak (Version 23.0) als unsere Identitäts- und Zugriffsverwaltungslösung zu verwenden. Keycloak ist eine Open-Source-Identitäts- und Zugriffsverwaltungslösung, die Folgendes bietet:
1. **Benutzerauthentifizierung**: Mehrere Authentifizierungsmethoden und -abläufe
2. **Benutzerföderation**: Integration mit LDAP, Active Directory und anderen Benutzerspeichern
3. **Identitätsvermittlung**: Integration mit externen Identitätsanbietern (Google, Facebook usw.)
4. **Single Sign-On**: Über alle Anwendungen und Dienste hinweg
5. **Feingranulare Autorisierung**: Rollen- und attributbasierte Zugriffssteuerung
6. **Benutzerverwaltung**: Selbstregistrierung, Profilmanagement, Passwortrichtlinien
7. **Token-basierte Authentifizierung**: JWT-Tokens für die Kommunikation zwischen Diensten
Unsere Implementierung umfasst:
- Keycloak-Server, der als containerisierter Dienst bereitgestellt wird
- Integration mit unserem API-Gateway für die Token-Validierung
- Client-Adapter für unsere Dienste und Anwendungen
- Benutzerdefinierte Themes und E-Mail-Vorlagen
- Rollen- und Gruppendefinitionen, die auf unser Domänenmodell abgestimmt sind
## Konsequenzen
### Positive
- **Umfassende Lösung**: Keycloak bietet eine vollständige Identitäts- und Zugriffsverwaltungslösung
- **Standards-Konformität**: Keycloak implementiert Industriestandards (OAuth 2.0, OpenID Connect, SAML)
- **Reduzierter Entwicklungsaufwand**: Wir müssen Authentifizierung und Autorisierung nicht von Grund auf implementieren
- **Sicherheit**: Keycloak folgt Sicherheits-Best-Practices und wird aktiv gewartet
- **Flexibilität**: Keycloak unterstützt mehrere Authentifizierungsmethoden und Identitätsanbieter
### Negative
- **Betriebliche Komplexität**: Keycloak fügt einen weiteren Dienst hinzu, der bereitgestellt und gewartet werden muss
- **Lernkurve**: Teams müssen Keycloak-Konzepte und APIs erlernen
- **Leistungsüberlegungen**: Die Token-Validierung fügt den Anfragen einen gewissen Overhead hinzu
- **Abhängigkeit**: Wir sind für Authentifizierung und Autorisierung von Keycloak abhängig
### Neutral
- **Konfigurationsbedarf**: Keycloak erfordert sorgfältige Konfiguration, um mit unseren Sicherheitsanforderungen übereinzustimmen
- **Upgrade-Management**: Keycloak-Upgrades müssen sorgfältig verwaltet werden
## Betrachtete Alternativen
### Eigener Authentifizierungsdienst
Wir haben in Betracht gezogen, unseren eigenen Authentifizierungsdienst zu entwickeln. Dies hätte uns vollständige Kontrolle über die Implementierung gegeben, hätte aber erheblichen Entwicklungsaufwand und laufende Wartung erfordert.
### Auth0
Wir haben die Verwendung von Auth0, einer kommerziellen Identity-as-a-Service (IDaaS)-Lösung, in Betracht gezogen. Auth0 hätte ähnliche Funktionen wie Keycloak mit weniger betrieblichem Overhead geboten, hätte aber laufende Kosten und potenzielle Anbieterabhängigkeit mit sich gebracht.
### Spring Security mit JWT
Wir haben die Verwendung von Spring Security mit JWT-Tokens für Authentifizierung und Autorisierung in Betracht gezogen. Dies hätte sich gut in unsere Spring-basierten Dienste integriert, hätte aber mehr Entwicklungsaufwand erfordert und hätte nicht die umfassenden Identitätsverwaltungsfunktionen von Keycloak geboten.
## Referenzen
- [Keycloak Dokumentation](https://www.keycloak.org/documentation)
- [OAuth 2.0 und OpenID Connect](https://oauth.net/2/)
- [JWT (JSON Web Tokens)](https://jwt.io/)
- [Absicherung von Microservices mit Keycloak](https://www.keycloak.org/docs/latest/securing_apps/)
@@ -0,0 +1,81 @@
# ADR-0006: Authentication and Authorization with Keycloak
## Status
Accepted
## Context
As part of our microservices architecture ([ADR-0003](0003-microservices-architecture.md)), we needed a robust and centralized solution for authentication and authorization. Key requirements included:
1. Single sign-on (SSO) across all services and applications
2. Support for multiple authentication methods (username/password, OAuth, SAML)
3. Fine-grained authorization with role-based access control (RBAC)
4. User management capabilities including self-registration and profile management
5. Integration with external identity providers
6. Security best practices including password policies and account lockout
7. Token-based authentication for service-to-service communication
Implementing these features from scratch would be time-consuming and error-prone, and would divert resources from our core business functionality.
## Decision
We decided to use Keycloak (version 23.0) as our identity and access management solution. Keycloak is an open-source identity and access management solution that provides:
1. **User Authentication**: Multiple authentication methods and flows
2. **User Federation**: Integration with LDAP, Active Directory, and other user stores
3. **Identity Brokering**: Integration with external identity providers (Google, Facebook, etc.)
4. **Single Sign-On**: Across all applications and services
5. **Fine-grained Authorization**: Role-based and attribute-based access control
6. **User Management**: Self-registration, profile management, password policies
7. **Token-based Authentication**: JWT tokens for service-to-service communication
Our implementation includes:
- Keycloak server deployed as a containerized service
- Integration with our API Gateway for token validation
- Client adapters for our services and applications
- Custom themes and email templates
- Role and group definitions aligned with our domain model
## Consequences
### Positive
- **Comprehensive solution**: Keycloak provides a complete identity and access management solution
- **Standards compliance**: Keycloak implements industry standards (OAuth 2.0, OpenID Connect, SAML)
- **Reduced development effort**: We don't need to implement authentication and authorization from scratch
- **Security**: Keycloak follows security best practices and is actively maintained
- **Flexibility**: Keycloak supports multiple authentication methods and identity providers
### Negative
- **Operational complexity**: Keycloak adds another service to deploy and maintain
- **Learning curve**: Teams need to learn Keycloak concepts and APIs
- **Performance considerations**: Token validation adds some overhead to requests
- **Dependency**: We are dependent on Keycloak for authentication and authorization
### Neutral
- **Configuration needs**: Keycloak requires careful configuration to align with our security requirements
- **Upgrade management**: Keycloak upgrades need to be managed carefully
## Alternatives Considered
### Custom Authentication Service
We considered building our own authentication service. This would have given us complete control over the implementation but would have required significant development effort and ongoing maintenance.
### Auth0
We considered using Auth0, a commercial identity as a service (IDaaS) solution. Auth0 would have provided similar capabilities to Keycloak with less operational overhead, but would have introduced ongoing costs and potential vendor lock-in.
### Spring Security with JWT
We considered using Spring Security with JWT tokens for authentication and authorization. This would have integrated well with our Spring-based services but would have required more development effort and would not have provided the comprehensive identity management features of Keycloak.
## References
- [Keycloak Documentation](https://www.keycloak.org/documentation)
- [OAuth 2.0 and OpenID Connect](https://oauth.net/2/)
- [JWT (JSON Web Tokens)](https://jwt.io/)
- [Securing Microservices with Keycloak](https://www.keycloak.org/docs/latest/securing_apps/)
@@ -0,0 +1,81 @@
# ADR-0007: API-Gateway-Muster
## Status
Akzeptiert
## Kontext
Mit unserer Microservices-Architektur ([ADR-0003](0003-microservices-architecture-de.md)) standen wir vor mehreren Herausforderungen im Zusammenhang mit der Client-Service-Kommunikation:
1. Clients müssten die Standorte und Schnittstellen mehrerer Dienste kennen
2. Verschiedene Clients (Web, Desktop, Mobil) müssten mehrere Aufrufe an verschiedene Dienste tätigen
3. Authentifizierung und Autorisierung müssten konsistent über alle Dienste hinweg implementiert werden
4. Querschnittsbelange wie Rate-Limiting, Logging und Monitoring müssten in jedem Dienst implementiert werden
5. API-Versionierung und Abwärtskompatibilität müssten über alle Dienste hinweg verwaltet werden
6. Die Netzwerksicherheit wäre komplexer, wenn mehrere Dienste direkt exponiert würden
Wir benötigten eine Lösung, die die Client-Service-Kommunikation vereinfachen und gleichzeitig diese Herausforderungen adressieren würde.
## Entscheidung
Wir haben uns entschieden, das API-Gateway-Muster mit Ktor als Framework zu implementieren. Das API-Gateway dient als einziger Eingangspunkt für alle Client-Anfragen und bietet die folgenden Funktionen:
1. **Anfrage-Routing**: Leitet Anfragen an die entsprechenden Microservices weiter
2. **Authentifizierung und Autorisierung**: Integriert sich mit Keycloak ([ADR-0006](0006-authentication-authorization-keycloak-de.md)), um Benutzer zu authentifizieren und Tokens zu validieren
3. **Rate-Limiting**: Verhindert Missbrauch durch Begrenzung der Anzahl von Anfragen von einem einzelnen Client
4. **Anfrage/Antwort-Transformation**: Transformiert Anfragen und Antworten nach Bedarf für verschiedene Clients
5. **Logging und Monitoring**: Bietet zentralisiertes Logging und Monitoring aller API-Anfragen
6. **Caching**: Speichert Antworten im Cache, um die Leistung zu verbessern
7. **API-Dokumentation**: Hostet OpenAPI-Dokumentation für alle Dienste
8. **Service-Discovery**: Entdeckt Dienstinstanzen dynamisch
Unsere Implementierung umfasst:
- Ein Ktor-basiertes API-Gateway, das als containerisierter Dienst bereitgestellt wird
- Integration mit Keycloak für Authentifizierung und Autorisierung
- Benutzerdefinierte Plugins für Rate-Limiting, Logging und Monitoring
- OpenAPI-Dokumentationsgenerierung
- Service-Discovery-Integration
## Konsequenzen
### Positive
- **Vereinfachte Client-Entwicklung**: Clients müssen nur mit einem einzigen Endpunkt kommunizieren
- **Konsistente Sicherheit**: Authentifizierung und Autorisierung werden konsistent gehandhabt
- **Zentralisierte Querschnittsbelange**: Rate-Limiting, Logging und Monitoring werden einmal implementiert
- **Verbesserte Sicherheit**: Interne Dienste werden nicht direkt Clients ausgesetzt
- **Flexibilität**: Das Gateway kann Anfragen und Antworten für verschiedene Clients anpassen
### Negative
- **Single Point of Failure**: Das Gateway wird zu einer kritischen Komponente, die hochverfügbar sein muss
- **Leistungs-Overhead**: Anfragen durchlaufen einen zusätzlichen Netzwerk-Hop
- **Komplexität**: Das Gateway muss eine breite Palette von Funktionalitäten handhaben
- **Entwicklungs-Engpass**: Änderungen am Gateway können Koordination über Teams hinweg erfordern
### Neutral
- **Deployment-Überlegungen**: Das Gateway muss angemessen bereitgestellt und skaliert werden
- **Versionierungsstrategie**: API-Versionierung muss immer noch verwaltet werden, wenn auch an einem Ort
## Betrachtete Alternativen
### Direkte Client-zu-Service-Kommunikation
Wir haben in Betracht gezogen, Clients die direkte Kommunikation mit Diensten zu ermöglichen. Dies hätte den Netzwerk-Hop durch das Gateway eliminiert, hätte aber die Client-Entwicklung komplexer gemacht und hätte die Implementierung von Querschnittsbelangen in jedem Dienst erfordert.
### Backend for Frontend (BFF)-Muster
Wir haben die Implementierung separater Backend for Frontend (BFF)-Dienste für jeden Client-Typ in Betracht gezogen. Dies hätte mehr clientspezifische Optimierungen ermöglicht, hätte aber den Entwicklungs- und Betriebsaufwand erhöht.
### Service Mesh
Wir haben die Verwendung eines Service Mesh wie Istio oder Linkerd zur Handhabung der Service-zu-Service-Kommunikation in Betracht gezogen. Dies hätte viele der gleichen Vorteile für die Service-zu-Service-Kommunikation geboten, hätte aber die Herausforderungen der Client-zu-Service-Kommunikation nicht so effektiv adressiert.
## Referenzen
- [API-Gateway-Muster](https://microservices.io/patterns/apigateway.html)
- [Ktor-Dokumentation](https://ktor.io/docs/)
- [Gateway-Routing-Muster](https://docs.microsoft.com/de-de/azure/architecture/patterns/gateway-routing)
- [Backend for Frontend-Muster](https://samnewman.io/patterns/architectural/bff/)
@@ -0,0 +1,81 @@
# ADR-0007: API Gateway Pattern
## Status
Accepted
## Context
With our microservices architecture ([ADR-0003](0003-microservices-architecture.md)), we faced several challenges related to client-service communication:
1. Clients would need to know the locations and interfaces of multiple services
2. Different clients (web, desktop, mobile) would need to make multiple calls to different services
3. Authentication and authorization would need to be implemented consistently across all services
4. Cross-cutting concerns like rate limiting, logging, and monitoring would need to be implemented in each service
5. API versioning and backward compatibility would need to be managed across all services
6. Network security would be more complex with multiple services exposed directly
We needed a solution that would simplify client-service communication while addressing these challenges.
## Decision
We decided to implement the API Gateway pattern using Ktor as the framework. The API Gateway serves as the single entry point for all client requests and provides the following functionality:
1. **Request Routing**: Routes requests to the appropriate microservices
2. **Authentication and Authorization**: Integrates with Keycloak ([ADR-0006](0006-authentication-authorization-keycloak.md)) to authenticate users and validate tokens
3. **Rate Limiting**: Prevents abuse by limiting the number of requests from a single client
4. **Request/Response Transformation**: Transforms requests and responses as needed for different clients
5. **Logging and Monitoring**: Provides centralized logging and monitoring of all API requests
6. **Caching**: Caches responses to improve performance
7. **API Documentation**: Hosts OpenAPI documentation for all services
8. **Service Discovery**: Discovers service instances dynamically
Our implementation includes:
- A Ktor-based API Gateway deployed as a containerized service
- Integration with Keycloak for authentication and authorization
- Custom plugins for rate limiting, logging, and monitoring
- OpenAPI documentation generation
- Service discovery integration
## Consequences
### Positive
- **Simplified client development**: Clients only need to communicate with a single endpoint
- **Consistent security**: Authentication and authorization are handled consistently
- **Centralized cross-cutting concerns**: Rate limiting, logging, and monitoring are implemented once
- **Improved security**: Internal services are not exposed directly to clients
- **Flexibility**: The gateway can adapt requests and responses for different clients
### Negative
- **Single point of failure**: The gateway becomes a critical component that must be highly available
- **Performance overhead**: Requests go through an additional network hop
- **Complexity**: The gateway needs to handle a wide range of functionality
- **Development bottleneck**: Changes to the gateway may require coordination across teams
### Neutral
- **Deployment considerations**: The gateway needs to be deployed and scaled appropriately
- **Versioning strategy**: API versioning still needs to be managed, albeit in one place
## Alternatives Considered
### Direct Client-to-Service Communication
We considered allowing clients to communicate directly with services. This would have eliminated the network hop through the gateway but would have made client development more complex and would have required implementing cross-cutting concerns in each service.
### Backend for Frontend (BFF) Pattern
We considered implementing separate Backend for Frontend (BFF) services for each client type. This would have allowed for more client-specific optimizations but would have increased development and operational overhead.
### Service Mesh
We considered using a service mesh like Istio or Linkerd to handle service-to-service communication. This would have provided many of the same benefits for service-to-service communication but would not have addressed the client-to-service communication challenges as effectively.
## References
- [API Gateway Pattern](https://microservices.io/patterns/apigateway.html)
- [Ktor Documentation](https://ktor.io/docs/)
- [Gateway Routing Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/gateway-routing)
- [Backend for Frontend Pattern](https://samnewman.io/patterns/architectural/bff/)
@@ -0,0 +1,84 @@
# ADR-0008: Multiplatform-Client-Anwendungen
## Status
Akzeptiert
## Kontext
Unser System benötigt Client-Anwendungen für verschiedene Benutzerrollen und Plattformen:
1. Desktop-Anwendungen für Administratoren und Veranstaltungsorganisatoren, die umfangreiche Funktionalität benötigen
2. Web-Anwendungen für Mitglieder und Pferdebesitzer, die von verschiedenen Geräten aus auf das System zugreifen müssen
3. Potenzielle zukünftige mobile Anwendungen für den Zugriff unterwegs
Die Entwicklung und Wartung separater Codebasen für jede Plattform würde erfordern:
- Doppelte Implementierung von Geschäftslogik und UI-Komponenten
- Mehrere Teams mit unterschiedlicher Plattformexpertise
- Koordination, um eine konsistente Benutzererfahrung über Plattformen hinweg zu gewährleisten
- Höhere Wartungskosten, da Funktionen und Fehlerbehebungen mehrfach implementiert werden müssten
Wir benötigten eine Lösung, die es uns ermöglicht, Code über Plattformen hinweg zu teilen und gleichzeitig auf jeder Plattform eine native Benutzererfahrung zu bieten.
## Entscheidung
Wir haben uns entschieden, Kotlin Multiplatform und Compose Multiplatform für unsere Client-Anwendungen zu verwenden:
1. **Kotlin Multiplatform**: Ermöglicht die gemeinsame Nutzung von Geschäftslogik, Datenmodellen und API-Client-Code über Plattformen hinweg
2. **Compose Multiplatform**: Bietet ein deklaratives UI-Framework, das auf Desktop-, Web- und mobilen Plattformen funktioniert
Unsere Implementierung umfasst:
- **common-ui**: Gemeinsame UI-Komponenten und Geschäftslogik
- **desktop-app**: Desktop-Anwendung für Administratoren und Veranstaltungsorganisatoren
- **web-app**: Web-Anwendung für Mitglieder und Pferdebesitzer
Die Architektur folgt einem Model-View-ViewModel (MVVM)-Muster:
- **Model**: Gemeinsame Datenmodelle und Repository-Implementierungen
- **ViewModel**: Gemeinsame Geschäftslogik und Zustandsverwaltung
- **View**: Plattformspezifische UI-Implementierungen mit Compose Multiplatform
Wir verwenden einen modularen Ansatz, bei dem plattformspezifischer Code minimiert wird und der größte Teil des Codes über Plattformen hinweg geteilt wird.
## Konsequenzen
### Positive
- **Code-Sharing**: Wesentliche Teile des Codes werden über Plattformen hinweg geteilt, was Duplizierung reduziert
- **Konsistente Benutzererfahrung**: UI-Komponenten und Verhalten sind über Plattformen hinweg konsistent
- **Einheitliche Sprache**: Kotlin wird für alle Plattformen verwendet, was die Entwicklung vereinfacht
- **Reduzierter Wartungsaufwand**: Fehlerbehebungen und Funktionen können einmal implementiert und über Plattformen hinweg angewendet werden
- **Team-Effizienz**: Entwickler können mit demselben Skillset an mehreren Plattformen arbeiten
### Negative
- **Lernkurve**: Kotlin Multiplatform und Compose Multiplatform haben eine Lernkurve
- **Reife**: Compose Multiplatform entwickelt sich noch weiter, besonders für Web-Targets
- **Leistungsüberlegungen**: Es kann im Vergleich zu plattformnativen Lösungen zu Leistungs-Overhead kommen
- **Plattformspezifische Funktionen**: Einige plattformspezifische Funktionen können schwieriger zu implementieren sein
- **Debugging-Komplexität**: Das Debugging über Plattformen hinweg kann komplexer sein
### Neutral
- **Komplexität des Build-Systems**: Das Build-System ist mit Multiplatform-Targets komplexer
- **Abhängigkeitsverwaltung**: Die Verwaltung von Abhängigkeiten über Plattformen hinweg erfordert sorgfältige Überlegung
## Betrachtete Alternativen
### Separate native Anwendungen
Wir haben die Entwicklung separater nativer Anwendungen für jede Plattform in Betracht gezogen (Java/JavaFX für Desktop, JavaScript/React für Web). Dies hätte die beste Leistung und Zugriff auf Plattformfunktionen geboten, hätte aber eine doppelte Implementierung von Geschäftslogik und UI-Komponenten erfordert.
### React Native
Wir haben die Verwendung von React Native für Mobile und Web mit einer separaten Desktop-Anwendung in Betracht gezogen. Dies hätte Code-Sharing zwischen Mobile und Web ermöglicht, hätte aber immer noch eine separate Desktop-Lösung erfordert und hätte JavaScript-Expertise erfordert.
### Flutter
Wir haben die Verwendung von Flutter für alle Plattformen in Betracht gezogen. Flutter bietet gute plattformübergreifende Unterstützung, hätte aber das Erlernen von Dart erfordert und hätte weniger Integration mit unseren Kotlin-basierten Backend-Diensten gehabt.
## Referenzen
- [Kotlin Multiplatform Dokumentation](https://kotlinlang.org/docs/multiplatform.html)
- [Compose Multiplatform Dokumentation](https://www.jetbrains.com/lp/compose-multiplatform/)
- [MVVM-Architekturmuster](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel)
- [Kotlin Multiplatform Mobile](https://kotlinlang.org/lp/mobile/)
@@ -0,0 +1,84 @@
# ADR-0008: Multiplatform Client Applications
## Status
Accepted
## Context
Our system requires client applications for different user roles and platforms:
1. Desktop applications for administrators and event organizers who need rich functionality
2. Web applications for members and horse owners who need to access the system from various devices
3. Potential future mobile applications for on-the-go access
Developing and maintaining separate codebases for each platform would require:
- Duplicate implementation of business logic and UI components
- Multiple teams with different platform expertise
- Coordination to ensure consistent user experience across platforms
- Higher maintenance costs as features and fixes would need to be implemented multiple times
We needed a solution that would allow us to share code across platforms while still providing a native-like experience on each platform.
## Decision
We decided to use Kotlin Multiplatform and Compose Multiplatform for our client applications:
1. **Kotlin Multiplatform**: Allows sharing of business logic, data models, and API client code across platforms
2. **Compose Multiplatform**: Provides a declarative UI framework that works across desktop, web, and mobile platforms
Our implementation includes:
- **common-ui**: Shared UI components and business logic
- **desktop-app**: Desktop application for administrators and event organizers
- **web-app**: Web application for members and horse owners
The architecture follows a Model-View-ViewModel (MVVM) pattern:
- **Model**: Shared data models and repository implementations
- **ViewModel**: Shared business logic and state management
- **View**: Platform-specific UI implementations using Compose Multiplatform
We use a modular approach where platform-specific code is minimized and most of the code is shared across platforms.
## Consequences
### Positive
- **Code sharing**: Significant portions of code are shared across platforms, reducing duplication
- **Consistent user experience**: UI components and behavior are consistent across platforms
- **Single language**: Kotlin is used for all platforms, simplifying development
- **Reduced maintenance**: Fixes and features can be implemented once and applied across platforms
- **Team efficiency**: Developers can work on multiple platforms with the same skill set
### Negative
- **Learning curve**: Kotlin Multiplatform and Compose Multiplatform have a learning curve
- **Maturity**: Compose Multiplatform is still evolving, especially for web targets
- **Performance considerations**: There may be performance overhead compared to platform-native solutions
- **Platform-specific features**: Some platform-specific features may be harder to implement
- **Debugging complexity**: Debugging across platforms can be more complex
### Neutral
- **Build system complexity**: The build system is more complex with multiplatform targets
- **Dependency management**: Managing dependencies across platforms requires careful consideration
## Alternatives Considered
### Separate Native Applications
We considered developing separate native applications for each platform (Java/JavaFX for desktop, JavaScript/React for web). This would have provided the best performance and access to platform features but would have required duplicate implementation of business logic and UI components.
### React Native
We considered using React Native for mobile and web, with a separate desktop application. This would have allowed code sharing between mobile and web but would still have required a separate desktop solution and would have required JavaScript expertise.
### Flutter
We considered using Flutter for all platforms. Flutter provides good cross-platform support but would have required learning Dart and would have had less integration with our Kotlin-based backend services.
## References
- [Kotlin Multiplatform Documentation](https://kotlinlang.org/docs/multiplatform.html)
- [Compose Multiplatform Documentation](https://www.jetbrains.com/lp/compose-multiplatform/)
- [MVVM Architecture Pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel)
- [Kotlin Multiplatform Mobile](https://kotlinlang.org/lp/mobile/)