(vision) SCS/DDD
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
@startuml
|
||||
title "Context Map: ÖTO Meldestellen-System"
|
||||
|
||||
!theme vibrant
|
||||
|
||||
' Definition der Bounded Contexts
|
||||
package "Personen & Vereine" as PersonenContext {
|
||||
[Personenstamm]
|
||||
[Vereinsstamm]
|
||||
}
|
||||
|
||||
package "Lizenzen & Qualifikationen" as LizenzContext {
|
||||
[Lizenznehmer]
|
||||
[Qualifikationen]
|
||||
}
|
||||
|
||||
package "Veranstaltungsplanung" as VeranstaltungsContext {
|
||||
[VeranstaltungsRahmen]
|
||||
[Turnier]
|
||||
[Prüfung (Bewerb)]
|
||||
}
|
||||
|
||||
package "Nennungsabwicklung" as NennungsContext {
|
||||
[Nennung]
|
||||
[Startliste]
|
||||
}
|
||||
|
||||
package "Ergebnisdienst" as ErgebnisContext {
|
||||
[Ergebnis]
|
||||
[Rangliste]
|
||||
}
|
||||
|
||||
package "ZNS-Import (ACL)" as ZNS_ACL {
|
||||
[ZNS Datentransfer]
|
||||
}
|
||||
|
||||
|
||||
' Beziehungen (Upstream/Downstream) und Kommunikationsmuster
|
||||
' Der Pfeil zeigt in Richtung des Downstream-Partners (Konsument)
|
||||
|
||||
' ZNS ist der Upstream-Partner für Personen- und Vereinsdaten
|
||||
ZNS_ACL ..> PersonenContext : Upstream/Downstream (Anti-Corruption Layer)
|
||||
|
||||
' Personen- und Veranstaltungsdaten sind Upstream für Nennungen
|
||||
PersonenContext ..> NennungsContext : "Reiter-, Pferdebesitzerdaten" (Consumer)
|
||||
VeranstaltungsContext ..> NennungsContext : "Verfügbare Prüfungen" (Consumer)
|
||||
LizenzContext ..> NennungsContext : "Lizenz- & Qualifikationsstatus" (Consumer)
|
||||
|
||||
' Nennungen sind Upstream für Ergebnisse
|
||||
NennungsContext ..> ErgebnisContext : "Angenommene Starter" (Consumer)
|
||||
|
||||
' Ergebnisdaten können wieder andere Kontexte beeinflussen (z.B. durch Events)
|
||||
ErgebnisContext ..> LizenzContext : Event: "Erfolg für Lizenz-Upgrade erzielt"
|
||||
ErgebnisContext ..> VeranstaltungsContext : Event: "Ergebnis für Siegerehrung verfügbar"
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,163 @@
|
||||
@startuml
|
||||
title "Detailliertes Datenmodell: Ergebnis_Context"
|
||||
|
||||
!theme vibrant
|
||||
|
||||
' Externe Referenzen werden der Übersichtlichkeit halber als vereinfachte Entitäten dargestellt.
|
||||
package "Externe Referenzen (Andere Kontexte)" {
|
||||
class Nennungs_Context_API
|
||||
class Veranstaltungs_Context_API
|
||||
class Personen_Context_API
|
||||
}
|
||||
|
||||
package "Ergebnisdienst" as ErgebnisContext {
|
||||
|
||||
' #################### Aggregate Root: Bewerbsergebnis ####################
|
||||
' Bündelt alle Ergebnisse einer Prüfungsabteilung zu einer konsistenten Einheit.
|
||||
class Bewerbsergebnis <<(A,red) Aggregate Root>> {
|
||||
+ bewerbsergebnisId : UUID <<PK>>
|
||||
--
|
||||
' Referenz zur Prüfung, für die dieses Ergebnis gilt.
|
||||
+ pruefung : PruefungsReferenzVO
|
||||
' Liste der eingesetzten Richter/Funktionäre gem. C-Satz
|
||||
+ eingesetzteOffizielle : List<OffiziellerReferenzVO>
|
||||
' Der aktuelle Zustand des Gesamtergebnisses.
|
||||
+ status : ErgebnisStatusVO
|
||||
}
|
||||
|
||||
' #################### Entitäten und VOs innerhalb des Aggregates ####################
|
||||
' Repräsentiert die Teilnahme und das Ergebnis eines einzelnen Starters.
|
||||
entity Einzelergebnis {
|
||||
+ einzelergebnisId : UUID <<PK>>
|
||||
--
|
||||
' Referenz zur Nennung, zu der dieses Ergebnis gehört.
|
||||
+ nennung : NennungsReferenzVO
|
||||
' Die berechnete finale Platzierung.
|
||||
' D-Satz, Stelle 2-4
|
||||
+ platz : integer
|
||||
' Status des einzelnen Starters.
|
||||
' Abgeleitet aus PLATZ und AUSSCHLUSS (D-Satz)
|
||||
+ teilnahmeStatus: TeilnahmeStatusVO
|
||||
' D-Satz, Stelle 145 ('*')
|
||||
+ istPlatziert : boolean
|
||||
' D-Satz, Stelle 136-141
|
||||
+ geldpreis : GeldbetragVO
|
||||
' D-Satz, Stelle 176-183
|
||||
+ link_id_ergebnis : VARCHAR(8)
|
||||
' Die konkrete, spartenspezifische Leistung.
|
||||
+ leistung : LeistungVO
|
||||
}
|
||||
|
||||
' Polymorphes Value Object für die eigentliche Leistung
|
||||
abstract class LeistungVO <<VO>> {
|
||||
}
|
||||
' Konkrete Ausprägungen der Leistung je nach Sparte
|
||||
LeistungVO <|-- DressurLeistungVO
|
||||
LeistungVO <|-- SpringenLeistungVO
|
||||
LeistungVO <|-- VielseitigkeitLeistungVO
|
||||
LeistungVO <|-- ReitervierkampfLeistungVO
|
||||
|
||||
class DressurLeistungVO {
|
||||
' D-Satz, Stelle 121-126
|
||||
+ wertnote : decimal
|
||||
' D-Satz, Stelle 127-131
|
||||
+ prozent : decimal
|
||||
}
|
||||
|
||||
class SpringenLeistungVO {
|
||||
' D-Satz, Stelle 121-126
|
||||
+ fehlerpunkte : decimal
|
||||
' D-Satz, Stelle 127-131
|
||||
+ zeit : decimal
|
||||
' D-Satz, Stelle 132-135
|
||||
+ stechen_info : string
|
||||
}
|
||||
|
||||
class VielseitigkeitLeistungVO {
|
||||
+ minuspunkte_dressur : decimal
|
||||
+ minuspunkte_gelaende_hindernis : decimal
|
||||
+ minuspunkte_gelaende_zeit : decimal
|
||||
+ minuspunkte_springen : decimal
|
||||
+ gesamt_minuspunkte : decimal
|
||||
}
|
||||
|
||||
class ReitervierkampfLeistungVO {
|
||||
+ punkte_dressur : INTEGER
|
||||
+ punkte_springen : INTEGER
|
||||
+ punkte_laufen : INTEGER
|
||||
+ punkte_schwimmen : INTEGER
|
||||
+ gesamt_punkte : INTEGER
|
||||
}
|
||||
|
||||
|
||||
' #################### Value Objects für Referenzen und Beschreibungen ####################
|
||||
class PruefungsReferenzVO <<VO>> {
|
||||
' Referenz zur originalen Abteilung
|
||||
+ pruefungAbteilungDbId : UUID
|
||||
' Relevante Daten zum Zeitpunkt der Ergebniserfassung
|
||||
+ bewerbBezeichnung : string
|
||||
+ abteilungBezeichnung: string
|
||||
}
|
||||
|
||||
class NennungsReferenzVO <<VO>> {
|
||||
' Referenz zur originalen Nennung
|
||||
+ nennungDbId : UUID
|
||||
' Redundante Daten für die Ergebnisliste, wie im D-Satz spezifiziert
|
||||
+ reiterName : string
|
||||
+ pferdName : string
|
||||
+ kopfnummer : string
|
||||
+ nationCode : string ' D-Satz, Stelle 142-144
|
||||
}
|
||||
|
||||
class OffiziellerReferenzVO <<VO>> {
|
||||
' Referenz zur Person
|
||||
+ oepsSatzNrPerson: VARCHAR(6)
|
||||
' Rolle gemäß C-Satz
|
||||
+ rolle: string ' z.B. "Richter-1", "Parcoursbau"
|
||||
}
|
||||
|
||||
class GeldbetragVO <<VO>> {
|
||||
+ wert : decimal
|
||||
+ waehrung : string
|
||||
}
|
||||
|
||||
enum ErgebnisStatusVO {
|
||||
IN_ERFASSUNG
|
||||
VORLAEUFIG
|
||||
FINAL
|
||||
KORRIGIERT
|
||||
}
|
||||
|
||||
enum TeilnahmeStatusVO {
|
||||
GESTARTET_GEWERTET
|
||||
AUSGESCHIEDEN ' Code "A" aus D-Satz, Stelle 120
|
||||
DISQUALIFIZIERT ' Code "D" aus D-Satz, Stelle 120
|
||||
TEILNAHMEVERZICHT ' Code "T" aus D-Satz, Stelle 120
|
||||
}
|
||||
|
||||
' #################### Beziehungen ####################
|
||||
' Ein Bewerbsergebnis besteht aus vielen Einzelergebnissen (Komposition)
|
||||
Bewerbsergebnis "1" *-- "1..*" Einzelergebnis : "enthält"
|
||||
|
||||
' Jedes Einzelergebnis hat genau eine spezifische Leistung (Komposition)
|
||||
Einzelergebnis "1" *-- "1" LeistungVO : "hat Leistung"
|
||||
}
|
||||
|
||||
' Beziehungen zu externen Kontexten (dargestellt als API-Aufrufe oder Events)
|
||||
ErgebnisContext.Bewerbsergebnis ..> Nennungs_Context_API : "holt Starterliste"
|
||||
ErgebnisContext.Bewerbsergebnis ..> Veranstaltungs_Context_API : "holt Prüfungsdetails"
|
||||
ErgebnisContext.Bewerbsergebnis ..> Personen_Context_API : "holt Details zu Offiziellen"
|
||||
|
||||
|
||||
note right of Bewerbsergebnis
|
||||
**Aggregate Root: Bewerbsergebnis**
|
||||
* **Verantwortung:** Dieses Aggregat garantiert die Konsistenz der gesamten Rangliste einer Abteilung.
|
||||
* **Logik:** Eine Methode `berechneRangliste()` würde alle zugehörigen `Einzelergebnis`-Objekte anhand der Regeln der jeweiligen Sparte sortieren und die `platz`-Attribute neu vergeben.
|
||||
* **Datenherkunft:** Die Liste der `eingesetzteOffizielle` wird aus dem C-Satz der Ergebnisdatei befüllt.
|
||||
end note
|
||||
|
||||
note bottom of LeistungVO
|
||||
**Polymorphe Leistung**
|
||||
Das abstrakte `LeistungVO` ermöglicht eine saubere Modellierung der unterschiedlichen Ergebnisstrukturen. Je nach Disziplin der Prüfung (Information aus `PruefungsReferenzVO`) wird ein `Einzelergebnis` mit einem der konkreten Leistungs-Objekte (`DressurLeistungVO`, `SpringenLeistungVO` etc.) instanziiert. Die Daten dafür stammen primär aus den Feldern `PUNKTE/WERTNOTE`, `ZEIT/PROZENT` und `STECHEN` des D-Satzes.
|
||||
end note
|
||||
@enduml
|
||||
@@ -0,0 +1,93 @@
|
||||
@startuml
|
||||
title "Datenmodell: Lizenzen_und_Qualifikationen_Context"
|
||||
|
||||
!theme vibrant
|
||||
|
||||
' Der Bounded Context wird als Paket dargestellt.
|
||||
package "Lizenzen & Qualifikationen" as LizenzContext {
|
||||
|
||||
' Das Aggregate Root: Der Lizenznehmer ist die zentrale Entität.
|
||||
class Lizenznehmer <<(A,violet) Aggregate Root>> {
|
||||
' Referenz zum Personen-Context, KEINE vollständige Kopie der Person.
|
||||
+ oepsSatzNrPerson : VARCHAR(6)
|
||||
--
|
||||
' Minimal notwendige Daten zur Identifikation im Fachkontext.
|
||||
name : string
|
||||
vorname : string
|
||||
--
|
||||
' Geschäftslogik wird hier gekapselt.
|
||||
+ hatLizenz(lizenzTypCode) : boolean
|
||||
+ hatQualifikation(qualTypCode) : boolean
|
||||
+ fuegeLizenzHinzu(lizenzDetails)
|
||||
+ fuegeQualifikationHinzu(qualDetails)
|
||||
}
|
||||
|
||||
' Eine Entität innerhalb des Lizenznehmer-Aggregates.
|
||||
entity Lizenz {
|
||||
+ lizenzId : UUID
|
||||
--
|
||||
' Bezieht sich auf den Typ der Lizenz.
|
||||
# lizenzTypCode : VARCHAR(4) <<FK>>
|
||||
' Daten aus LIZENZ01.dat.
|
||||
+ gueltigBis : Date
|
||||
+ ausgestelltAm : Date
|
||||
+ bezahltImJahr : INTEGER
|
||||
}
|
||||
|
||||
' Eine weitere Entität innerhalb des Lizenznehmer-Aggregates.
|
||||
entity Qualifikation {
|
||||
+ qualifikationId : UUID
|
||||
--
|
||||
' Bezieht sich auf den Typ der Qualifikation.
|
||||
# qualifikationsTypCode : VARCHAR <<FK>>
|
||||
' Daten aus RICHT01.dat (indirekt).
|
||||
+ erworbenAm : Date
|
||||
+ gueltigBis : Date
|
||||
}
|
||||
|
||||
' Die Definitionen der Lizenz- und Qualifikationstypen
|
||||
' kommen aus einem anderen Bounded Context (OeTO-Verwaltung).
|
||||
' Hier werden sie als Referenz oder Value Object verwendet.
|
||||
' Wir stellen sie hier vereinfacht dar, um die Beziehung zu zeigen.
|
||||
class LizenzTyp_Referenz <<VO>> {
|
||||
+ code : string
|
||||
+ bezeichnung : string
|
||||
+ sparte : string
|
||||
}
|
||||
|
||||
class QualifikationsTyp_Referenz <<VO>> {
|
||||
+ code : string
|
||||
+ bezeichnung : string
|
||||
+ sparte : string
|
||||
}
|
||||
|
||||
' -- Beziehungen --
|
||||
' Der Lizenznehmer besitzt seine Lizenzen und Qualifikationen (Komposition).
|
||||
Lizenznehmer "1" *-- "0..*" Lizenz
|
||||
Lizenznehmer "1" *-- "0..*" Qualifikation
|
||||
|
||||
' Jede Lizenz und Qualifikation ist von einem bestimmten Typ.
|
||||
' Dies ist eine Assoziation, da die Typen außerhalb des Aggregates existieren.
|
||||
Lizenz ..> LizenzTyp_Referenz : "ist vom Typ"
|
||||
Qualifikation ..> QualifikationsTyp_Referenz : "ist vom Typ"
|
||||
}
|
||||
|
||||
note right of Lizenznehmer
|
||||
**Aggregate Root: Lizenznehmer**
|
||||
* **Verantwortung:** Stellt sicher, dass ein Lizenznehmer nur gültige und konsistente Lizenzen/Qualifikationen besitzen kann.
|
||||
* **Referenz:** Die `oepsSatzNrPerson` ist der Schlüssel zur Verknüpfung mit dem `Personen_und_Vereine_Context`.
|
||||
* **Isolation:** Dieser Context speichert bewusst *keine* Adress- oder Kontaktdaten. Wenn diese benötigt werden, müssen sie vom `Personen_und_Vereine_Context` angefragt werden.
|
||||
end note
|
||||
|
||||
note top of LizenzTyp_Referenz
|
||||
**Referenzen zu anderen Contexts**
|
||||
`LizenzTyp_Referenz` und `QualifikationsTyp_Referenz`
|
||||
sind keine Entitäten, die *hier* verwaltet werden.
|
||||
Sie repräsentieren die Daten, die aus dem
|
||||
`Service_OeTO_Verwaltung` stammen und hier
|
||||
zur Beschreibung von Lizenzen und Qualifikationen
|
||||
genutzt werden.
|
||||
end note
|
||||
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,66 @@
|
||||
@startuml
|
||||
title "Datenmodell: Lizenzen_und_Qualifikationen_Context"
|
||||
|
||||
!theme vibrant
|
||||
|
||||
package "Lizenzen & Qualifikationen" {
|
||||
|
||||
' Das Aggregate Root: Der Lizenznehmer ist die zentrale Entität,
|
||||
' die Konsistenz für ihre Lizenzen und Qualifikationen sicherstellt.
|
||||
class Lizenznehmer <<(A,violet) Aggregate Root>> {
|
||||
' Referenz zum Personen-Context, keine vollständige Person
|
||||
+ oepsSatzNrPerson : VARCHAR(6)
|
||||
--
|
||||
name : string
|
||||
vorname : string
|
||||
' Methode zur Überprüfung der Startberechtigung
|
||||
+ hatStartberechtigungFuer(anforderungen) : boolean
|
||||
}
|
||||
|
||||
' Entität innerhalb des Aggregates
|
||||
class Lizenz {
|
||||
+ gueltigBis : Date
|
||||
+ ausgestelltAm : Date
|
||||
+ bezahltImJahr : INTEGER
|
||||
}
|
||||
|
||||
' Entität innerhalb des Aggregates
|
||||
class Qualifikation {
|
||||
+ erworbenAm : Date
|
||||
+ bemerkung : string
|
||||
}
|
||||
|
||||
' Value Object: Beschreibt einen Lizenztyp, hat keine eigene Identität
|
||||
class LizenzTyp <<VO>> {
|
||||
+ code : string
|
||||
+ bezeichnung : string
|
||||
+ sparte: string
|
||||
}
|
||||
|
||||
' Value Object: Beschreibt einen Qualifikationstyp
|
||||
class QualifikationsTyp <<VO>> {
|
||||
+ code : string
|
||||
+ bezeichnung : string
|
||||
+ sparte : string
|
||||
}
|
||||
|
||||
' Beziehungen innerhalb des Aggregates
|
||||
Lizenznehmer "1" *-- "0..*" Lizenz : "besitzt"
|
||||
Lizenznehmer "1" *-- "0..*" Qualifikation : "hat"
|
||||
|
||||
' Beziehungen zu Value Objects
|
||||
Lizenz "1" -- "1" LizenzTyp
|
||||
Qualifikation "1" -- "1" QualifikationsTyp
|
||||
}
|
||||
|
||||
note right of Lizenznehmer
|
||||
**Aggregate Root: Lizenznehmer**
|
||||
Alle Änderungen an Lizenzen oder
|
||||
Qualifikationen einer Person
|
||||
sollten über das Lizenznehmer-Objekt
|
||||
laufen, um die Geschäftsregeln
|
||||
(z.B. "Darf diese Lizenz haben?")
|
||||
zu wahren.
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,59 @@
|
||||
@startuml
|
||||
title "Prozess & Event-Flow: Nennung wird abgegeben"
|
||||
|
||||
!theme vibrant
|
||||
|
||||
actor Reiter
|
||||
|
||||
' Die Teilnehmer sind unsere Bounded Contexts (Services) und ein Message Bus für Events.
|
||||
participant "API Gateway / Frontend" as Gateway
|
||||
participant "Nennungs_Context" as Nenn
|
||||
participant "Veranstaltungs_Context" as Veranst
|
||||
participant "Lizenzen_und_Qualifikationen_Context" as Lizenz
|
||||
queue "Message Bus" as Bus
|
||||
participant "Benachrichtigungs_Service" as Notify
|
||||
|
||||
Reiter -> Gateway : POST /nennungen\n(reiterId, pferdId, pruefungAbteilungId)
|
||||
|
||||
' 1. Command wird an den zuständigen Context gesendet
|
||||
Gateway -> Nenn : **Command:** NennungAbgeben(daten)
|
||||
activate Nenn
|
||||
note right of Nenn: Empfängt den Befehl,\neine neue Nennung zu erstellen.
|
||||
|
||||
' 2. Synchrone Queries zur Validierung
|
||||
Nenn -> Veranst : **Query:** getPruefungsAnforderungen(pruefungAbteilungId)
|
||||
activate Veranst
|
||||
Veranst --> Nenn : anforderungen {erf. Lizenzen, erf. Alter, ...}
|
||||
deactivate Veranst
|
||||
note left of Nenn: Holt die aktuellen Anforderungen\nfür die genannte Prüfung.
|
||||
|
||||
Nenn -> Lizenz : **Query:** hatStartberechtigung(reiterId, anforderungen)
|
||||
activate Lizenz
|
||||
Lizenz --> Nenn : {istBerechtigt: true}
|
||||
deactivate Lizenz
|
||||
note left of Nenn: Prüft die Startberechtigung des Reiters\ngegen die Anforderungen.
|
||||
|
||||
' 3. Interne Verarbeitung und Zustandsänderung
|
||||
alt Startberechtigung erteilt
|
||||
Nenn -> Nenn : Nennungs-Aggregat erstellen\n(Status: EINGEGANGEN)
|
||||
note right of Nenn: Die Nennung wird intern gespeichert.\nDie Transaktion ist hier abgeschlossen.
|
||||
|
||||
' 4. Asynchrones Event wird veröffentlicht
|
||||
Nenn ->> Bus : **Event:** NennungWurdeEingereicht {nennungId, reiterId, ...}
|
||||
note left of Bus: Das Event wird auf den Bus gelegt.\nDer Nennungs-Context ist nun fertig\nund muss nicht auf die Verarbeitung\ndes Events warten.
|
||||
|
||||
Gateway --> Reiter : HTTP 202 Accepted (Nennung wird verarbeitet)
|
||||
|
||||
' 5. Andere Services reagieren auf das Event
|
||||
Bus ->> Notify : **Event:** NennungWurdeEingereicht
|
||||
activate Notify
|
||||
Notify -> Notify : Sende Bestätigungs-E-Mail an Reiter
|
||||
deactivate Notify
|
||||
|
||||
else Startberechtigung nicht erteilt
|
||||
Nenn -> Gateway : Fehler: Startberechtigung fehlt (z.B. HTTP 400)
|
||||
Gateway --> Reiter : Fehlermeldung
|
||||
end
|
||||
deactivate Nenn
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,132 @@
|
||||
@startuml
|
||||
title "Detailliertes Datenmodell: Nennungs_Context"
|
||||
|
||||
!theme vibrant
|
||||
|
||||
' Externe Referenzen werden der Übersichtlichkeit halber als vereinfachte Entitäten dargestellt.
|
||||
package "Externe Referenzen (Andere Kontexte)" {
|
||||
class Veranstaltungs_Context_API
|
||||
class Personen_Context_API
|
||||
class Lizenzen_Context_API
|
||||
}
|
||||
|
||||
package "Nennungsabwicklung" as NennungsContext {
|
||||
|
||||
' #################### Aggregate Root: Nennung ####################
|
||||
class Nennung <<(A,blue) Aggregate Root>> {
|
||||
+ nennungId : UUID <<PK>>
|
||||
--
|
||||
' -- Snapshots von Daten aus anderen Kontexten --
|
||||
' Daten zur Prüfung/Abteilung zum Zeitpunkt der Nennung
|
||||
+ pruefung : PruefungsReferenzVO
|
||||
' Daten zum Reiter zum Zeitpunkt der Nennung
|
||||
+ reiter : ReiterReferenzVO
|
||||
' Daten zum Pferd zum Zeitpunkt der Nennung
|
||||
+ pferd : PferdeReferenzVO
|
||||
' Optionaler Ersatzreiter gem. KKARTEI-Satz
|
||||
+ ersatzreiter : ReiterReferenzVO
|
||||
--
|
||||
' -- Nennungsspezifische Attribute --
|
||||
+ nennungsZeitpunkt : timestamp
|
||||
+ status : NennungsStatusVO
|
||||
' Kopfnummer gem. KKARTEI-Satz
|
||||
+ zugewieseneKopfnummer : VARCHAR(4)
|
||||
' Nenn- und Startgeld
|
||||
+ nenngebuehr : GeldbetragVO
|
||||
' Zahlungsstatus der Nenngebühr
|
||||
+ bezahlStatus : BezahlStatusVO
|
||||
' Betrag, der lt. Nennliste eingezahlt wurde
|
||||
' KKARTEI, Stelle 161-165
|
||||
+ bezahltBetragKontrolle : GeldbetragVO
|
||||
' Betrag, der mit dem Veranstalter verrechnet wird
|
||||
' KKARTEI, Stelle 118-122
|
||||
+ accontoBetrag : GeldbetragVO
|
||||
' Box bestellt? Gem. KKARTEI-Satz
|
||||
+ istStallReserviert : boolean
|
||||
' Grund, falls die Nennung abgelehnt wurde
|
||||
+ ablehnungsGrund : string
|
||||
}
|
||||
|
||||
' #################### Value Objects (VOs) für Snapshots und Beschreibungen ####################
|
||||
' Snapshot der wichtigsten Prüfungsdaten aus dem Veranstaltungs_Context.
|
||||
class PruefungsReferenzVO <<VO>> {
|
||||
' Referenz zur originalen Abteilung
|
||||
+ pruefungAbteilungDbId : UUID
|
||||
' Relevante Daten zum Zeitpunkt der Nennung
|
||||
+ turnierName : string
|
||||
+ bewerbBezeichnung : string
|
||||
+ abteilungBezeichnung : string
|
||||
' Anforderungsprofil, das zum Zeitpunkt der Nennung galt
|
||||
+ anforderungsProfil : AnforderungsProfilVO
|
||||
}
|
||||
|
||||
' Snapshot der wichtigsten Reiterdaten aus dem Personen_Context.
|
||||
class ReiterReferenzVO <<VO>> {
|
||||
' Referenz zur originalen Person
|
||||
+ oepsSatzNrPerson : VARCHAR(6)
|
||||
' Relevante Daten zum Zeitpunkt der Nennung
|
||||
+ name : string
|
||||
+ vereinsName : string
|
||||
' Snapshot der Lizenzen zur Validierung
|
||||
+ lizenzSnapshot : List<string>
|
||||
}
|
||||
|
||||
' Snapshot der wichtigsten Pferdedaten aus dem Personen_Context.
|
||||
class PferdeReferenzVO <<VO>> {
|
||||
' Referenz zum originalen Pferd
|
||||
+ oepsSatzNrPferd : VARCHAR(10)
|
||||
' Relevante Daten zum Zeitpunkt der Nennung
|
||||
+ name : string
|
||||
}
|
||||
|
||||
' Kapselt die Anforderungen, die zum Zeitpunkt der Nennung gültig waren.
|
||||
class AnforderungsProfilVO <<VO>> {
|
||||
+ erlaubteAltersklassen : List<string>
|
||||
+ erforderlicheLizenzen : List<string>
|
||||
}
|
||||
|
||||
class GeldbetragVO <<VO>> {
|
||||
+ wert : decimal
|
||||
+ waehrung : string
|
||||
}
|
||||
|
||||
' Enum für den Lebenszyklus einer Nennung.
|
||||
enum NennungsStatusVO {
|
||||
EINGEGANGEN
|
||||
IN_PRUEFUNG
|
||||
STARTBERECHTIGT_BESTAETIGT
|
||||
ABGELEHNT
|
||||
ZURUECKGEZOGEN
|
||||
}
|
||||
|
||||
' Enum für den Zahlungsstatus.
|
||||
enum BezahlStatusVO {
|
||||
OFFEN
|
||||
BEZAHLT
|
||||
}
|
||||
|
||||
|
||||
' #################### Beziehungen ####################
|
||||
' Die Nennung ist das einzige Aggregat und enthält ihre beschreibenden VOs (Komposition).
|
||||
Nennung "1" o-- "1" PruefungsReferenzVO
|
||||
Nennung "1" o-- "1" ReiterReferenzVO
|
||||
Nennung "1" o-- "1" PferdeReferenzVO
|
||||
Nennung "1" o-- "0..1" ReiterReferenzVO : "Ersatzreiter"
|
||||
Nennung "1" o-- "1" GeldbetragVO : "Nenngebühr"
|
||||
Nennung "1" -- "1" NennungsStatusVO
|
||||
Nennung "1" -- "1" BezahlStatusVO
|
||||
}
|
||||
|
||||
' Beziehungen zu externen Kontexten (dargestellt als API-Aufrufe)
|
||||
Nennung ..> Veranstaltungs_Context_API : "holt Prüfungsdetails"
|
||||
Nennung ..> Personen_Context_API : "holt Reiter-/Pferdedetails"
|
||||
Nennung ..> Lizenzen_Context_API : "prüft Startberechtigung"
|
||||
|
||||
note right of Nennung
|
||||
**Aggregate Root: Nennung**
|
||||
* **Verantwortung:** Eine Nennung ist eine unteilbare, transaktionale Einheit. Sie repräsentiert den "Vertrag" zwischen Reiter und Veranstalter für die Teilnahme an einer Prüfung.
|
||||
* **Datenherkunft:** Viele Attribute sind direkt auf den `KKARTEI`-Satz im OEPS Pflichtenheft zurückzuführen, z.B. `ERSATZREITER` , `ACCONTO` , `STALL` und `BEZAHLT`.
|
||||
* **Validierungslogik:** Bei der Erstellung oder Prüfung einer Nennung ruft das Aggregat andere Kontexte auf, um die aktuellen Daten zu verifizieren (z.B. "Ist die Lizenz des Reiters noch gültig?"). Die Entscheidung ("Akzeptiert" / "Abgelehnt") wird aber hier im `Nennungs_Context` getroffen und gespeichert.
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,98 @@
|
||||
@startuml
|
||||
title "Datenmodell: Personen_und_Vereine_Context"
|
||||
|
||||
!theme vibrant
|
||||
|
||||
package "Personen & Vereine" as PersonenContext {
|
||||
|
||||
' Eine Person ist ein Aggregate Root, das seine persönlichen Details,
|
||||
' Adressen, Kontakte und Mitgliedschaften bündelt.
|
||||
class Person <<(A,green) Aggregate Root>> {
|
||||
' Primärschlüssel aus LIZENZ01.DAT
|
||||
+ oepsSatzNrPerson : VARCHAR(6)
|
||||
--
|
||||
+ name : PersonenNameVO
|
||||
+ geburtsdatum : Date
|
||||
+ geschlecht : GeschlechtVO
|
||||
+ nationalitaet : NationalitaetVO [cite: 6, 149, 181]
|
||||
+ feiId : VARCHAR(10)
|
||||
+ status : PersonenStatusVO ' z.B. Aktiv, Gesperrt
|
||||
--
|
||||
' Methoden des Aggregates
|
||||
+ aendereAdresse(neueAdresse)
|
||||
+ fuegeMitgliedschaftHinzu(verein, mitgliedsNr)
|
||||
+ setzeStatus(neuerStatus)
|
||||
}
|
||||
|
||||
' Ein Verein ist ebenfalls ein Aggregate Root.
|
||||
class Verein <<(A,green) Aggregate Root>> {
|
||||
' Primärschlüssel aus VEREIN01.DAT
|
||||
+ oepsVereinsNr : VARCHAR(4)
|
||||
--
|
||||
+ name : VARCHAR(50)
|
||||
+ bundesland : BundeslandVO
|
||||
' Weitere Vereinsdetails wie Adresse, Kontakt...
|
||||
}
|
||||
|
||||
' Eine Entität innerhalb des Person-Aggregates. Sie hat eine eigene Identität,
|
||||
' wird aber immer über die Person verwaltet.
|
||||
entity Mitgliedschaft {
|
||||
+ mitgliedschaftId : UUID
|
||||
--
|
||||
' Referenz zum Verein-Aggregat
|
||||
# oepsVereinsNr : VARCHAR(4) <<FK>>
|
||||
' MITGLIEDSNUMMER aus LIZENZ01.DAT
|
||||
+ mitgliedsNrImVerein : VARCHAR(8)
|
||||
+ istHauptmitgliedschaft : boolean
|
||||
+ von : Date
|
||||
+ bis : Date
|
||||
}
|
||||
|
||||
' -- Value Objects (VOs) --
|
||||
' VOs haben keine eigene Identität, sie beschreiben Eigenschaften.
|
||||
|
||||
class PersonenNameVO <<VO>> {
|
||||
+ familienname : string
|
||||
+ vorname : string
|
||||
}
|
||||
|
||||
class AdresseVO <<VO>> {
|
||||
+ strasse : string
|
||||
+ hausnummer: string
|
||||
+ plz: string
|
||||
+ ort: string
|
||||
+ land: string
|
||||
}
|
||||
|
||||
class KontaktVO <<VO>> {
|
||||
+ typ: KontaktTyp ' Email, Telefon, Mobil
|
||||
+ wert: string
|
||||
}
|
||||
|
||||
' -- Beziehungen --
|
||||
' Das Person-Aggregat besitzt seine Mitgliedschaften (Komposition).
|
||||
Person "1" *-- "0..*" Mitgliedschaft
|
||||
' Das Person-Aggregat nutzt Value Objects zur Beschreibung.
|
||||
Person "1" o-- "1" PersonenNameVO
|
||||
Person "1" o-- "0..*" AdresseVO
|
||||
Person "1" o-- "0..*" KontaktVO
|
||||
|
||||
' Die Mitgliedschaft verweist auf das Verein-Aggregat.
|
||||
' Dies ist eine lose Kopplung über die ID, keine Komposition.
|
||||
Mitgliedschaft ..> Verein : "bezieht sich auf"
|
||||
}
|
||||
|
||||
note right of Person
|
||||
**Aggregate Root: Person**
|
||||
Dieses Objekt ist der zentrale Einstiegspunkt
|
||||
für alle Operationen, die eine Person betreffen.
|
||||
|
||||
**Beispiel:**
|
||||
Um eine Mitgliedschaft hinzuzufügen, ruft man
|
||||
`person.fuegeMitgliedschaftHinzu(...)` auf.
|
||||
Das `Person`-Objekt stellt sicher, dass z.B.
|
||||
nur eine Hauptmitgliedschaft existiert.
|
||||
Man ändert nicht direkt das Mitgliedschafts-Objekt.
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,27 @@
|
||||
@startuml
|
||||
title "Service-Interaktion: Nennung wird validiert"
|
||||
|
||||
actor Reiter
|
||||
|
||||
participant "Nennungs_Context" as Nenn
|
||||
participant "Veranstaltungs_Context" as Veranst
|
||||
participant "Lizenzen_und_Qualifikationen_Context" as Lizenz
|
||||
|
||||
Reiter -> Nenn : Nennung abgeben für Prüfung "A-Dressur"
|
||||
activate Nenn
|
||||
|
||||
Nenn -> Veranst : anfrage: getPruefungsAnforderungen("A-Dressur")
|
||||
activate Veranst
|
||||
Veranst --> Nenn : antwort: {erf. Lizenz: "R1", erf. Alter: "U21"}
|
||||
deactivate Veranst
|
||||
|
||||
Nenn -> Lizenz : anfrage: hatStartberechtigung(Reiter-ID, {erf. Lizenz: "R1", erf. Alter: "U21"})
|
||||
activate Lizenz
|
||||
Lizenz --> Nenn : antwort: {status: "OK"}
|
||||
deactivate Lizenz
|
||||
|
||||
Nenn -> Nenn : Nennung speichern (Status: "Angenommen")
|
||||
Nenn --> Reiter : Bestätigung: Nennung erfolgreich!
|
||||
deactivate Nenn
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,109 @@
|
||||
# Dokumentation: Service-orientiertes Datenbankmodell ÖTO
|
||||
|
||||
**Stand:** 02. Juli 2025
|
||||
|
||||
## 1. Einleitung und Überblick
|
||||
|
||||
Dieses Dokument beschreibt die Architektur und das Datenmodell für eine moderne, service-orientierte Meldestellen-Software. Der Entwurf basiert auf den Prinzipien des **Domain-Driven Design (DDD)** und einer Architektur von **Self-Contained Systems (SCS)**.
|
||||
|
||||
Das Ziel ist ein robustes, wartbares und erweiterbares System, das die komplexen Anforderungen der Österreichischen Turnierordnung (ÖTO) abbildet und die Datenflüsse des OEPS (Zentrales Nennservice, ZNS) sauber integriert. Die Datenstrukturen basieren maßgeblich auf dem **OEPS Pflichtenheft 2021 (Version 2.4 vom 28.07.2021)**.
|
||||
|
||||
## 2. Systemarchitektur: Bounded Contexts
|
||||
|
||||
Das System ist in fachliche, voneinander abgegrenzte Domänen, sogenannte **Bounded Contexts**, aufgeteilt. Jeder Context hat eine klare Verantwortung und ein eigenes, darauf optimiertes Datenmodell. Die Kommunikation zwischen den Contexts erfolgt über klar definierte Schnittstellen (APIs) und asynchrone Domänen-Events.
|
||||
|
||||
Die Kern-Contexte sind:
|
||||
|
||||
* **`ZNS_Import_ACL`**: Ein "Anti-Corruption Layer", der als Pufferzone und Übersetzer für die externen ZNS-Daten dient.
|
||||
* **`Personen_und_Vereine_Context`**: Die Quelle der Wahrheit für alle Stammdaten von Personen und Vereinen.
|
||||
* **`Lizenzen_und_Qualifikationen_Context`**: Verwaltet die Startberechtigungen (Lizenzen, Qualifikationen) von Personen.
|
||||
* **`Veranstaltungs_Context`**: Dient der Planung und Definition von Veranstaltungen, Turnieren und Bewerben.
|
||||
* **`Nennungs_Context`**: Wickelt den gesamten Nennungsprozess bis zur Erstellung der Startliste ab.
|
||||
* **`Ergebnis_Context`**: Ist für die Erfassung, Berechnung und Veröffentlichung der Ergebnisse zuständig.
|
||||
|
||||
*(In einer vollständigen Dokumentation würde hier eine visuelle Context Map die Beziehungen zwischen diesen Services darstellen.)*
|
||||
|
||||
---
|
||||
|
||||
## 3. Detaillierte Modelle der Bounded Contexts
|
||||
|
||||
### 3.1. ZNS-Import als Anti-Corruption Layer (ACL)
|
||||
* **Verantwortung:** Schutz des internen Domänenmodells vor den Details der externen ZNS-Schnittstelle. Dieser Context liest die `.dat`-Dateien ein, validiert und übersetzt sie in eine saubere, strukturierte Form für die anderen Services.
|
||||
* **Aggregate Roots:** Dieser Context hat keine eigenen Aggregate, da er primär ein prozeduraler Übersetzer ist.
|
||||
* **Kernentitäten:**
|
||||
* [cite_start]**`ZNS_*_dat_Satz`**: Eine Reihe von Entitäten (`ZNS_LIZENZ01_dat_Satz`, `ZNS_PFERDE01_dat_Satz` etc.), die exakte Abbilder der Zeilen aus den jeweiligen OEPS-Dateien sind[cite: 20]. Sie speichern die Rohdaten.
|
||||
* **`ZNS_Daten_Uebersetzer`**: Eine konzeptionelle Komponente, die die Logik zur Transformation der Rohdaten in Domänen-Events oder Objekte für die internen Kontexte kapselt.
|
||||
|
||||
### 3.2. Personen_und_Vereine_Context
|
||||
* **Verantwortung:** Die zentrale Verwaltung der Stammdaten aller Akteure (Personen, Vereine).
|
||||
* **Aggregate Roots:** `Person`, `Verein`.
|
||||
* **Kernentitäten:**
|
||||
* **`Person`**: Bündelt alle persönlichen Daten. [cite_start]Der Primärschlüssel `oepsSatzNrPerson` ist die 6-stellige Satznummer aus den ZNS-Dateien[cite: 149]. Enthält Value Objects wie `PersonenNameVO` und `AdresseVO` sowie eine Liste von `Mitgliedschaft`-Entitäten.
|
||||
* **`Verein`**: Verwaltet Vereinsstammdaten. [cite_start]Der Primärschlüssel `oepsVereinsNr` ist die 4-stellige Nummer aus `VEREIN01.DAT`[cite: 189].
|
||||
* **`Mitgliedschaft`**: Eine Entität innerhalb des `Person`-Aggregates, die die Beziehung einer Person zu einem `Verein` beschreibt.
|
||||
|
||||
### 3.3. Lizenzen_und_Qualifikationen_Context
|
||||
* **Verantwortung:** Verwaltung der Startberechtigungen und offiziellen Qualifikationen einer Person.
|
||||
* **Aggregate Roots:** `Lizenznehmer`.
|
||||
* **Kernentitäten:**
|
||||
* **`Lizenznehmer`**: Repräsentiert eine Person im Kontext von Berechtigungen. Wird durch die `oepsSatzNrPerson` identifiziert. Bündelt alle `Lizenz`- und `Qualifikation`-Objekte einer Person und stellt deren Konsistenz sicher.
|
||||
* **`Lizenz`**: Eine konkrete Lizenzinstanz einer Person, die sich auf einen `LizenzTyp_OEPS` (definiert im OeTO-Verwaltungs-Context) bezieht. [cite_start]Daten wie `bezahltImJahr` werden aus dem Feld `LIZENZINFO` der `LIZENZ01.dat` abgeleitet[cite: 181].
|
||||
* **`Qualifikation`**: Eine konkrete Qualifikation (z.B. als Richter), die sich auf einen `QualifikationsTyp` bezieht. [cite_start]Die Daten stammen aus der Interpretation des `QUALIFIKATIONEN`-Feldes der `RICHT01.dat`[cite: 166].
|
||||
|
||||
### 3.4. Veranstaltungs_Context
|
||||
* **Verantwortung:** Detaillierte Planung und Definition aller reitsportlichen Veranstaltungen.
|
||||
* **Aggregate Roots:** `VeranstaltungsRahmen`, `Turnier_OEPS`, `Meisterschaft_Cup_Serie`.
|
||||
* **Veranstaltungshierarchie:**
|
||||
1. **`VeranstaltungsRahmen`**: Die oberste Ebene, eine konkrete Veranstaltung an Ort und Zeit (z.B. "Reitertage 2025").
|
||||
2. [cite_start]**`Turnier_OEPS`**: Ein offizielles OEPS-Turnier innerhalb des Rahmens, identifiziert durch die 5-stellige `oepsTurnierNr` aus dem A-Satz[cite: 144, 193].
|
||||
3. [cite_start]**`Pruefung_OEPS` (Bewerb)**: Ein Bewerb innerhalb eines Turniers, identifiziert durch die 3-stellige `oepsBewerbNr` aus dem B-Satz[cite: 100, 151].
|
||||
4. **`Pruefung_Abteilung`**: Eine Unterteilung einer Prüfung, für die separat genannt und gewertet werden kann. [cite_start]Gemäß Pflichtenheft ist für jede Abteilung ein eigener B-Satz zu stellen[cite: 197].
|
||||
* **Weitere Entitäten:**
|
||||
* **`Meisterschaft_Cup_Serie`**: Ein Aggregat zur Verwaltung von übergreifenden Wettbewerben.
|
||||
* **`MCS_Wertungspruefung`**: Verknüpft Meisterschaften/Cups mit den spezifischen Prüfungsabteilungen, die als Wertungsprüfungen zählen.
|
||||
* **`PruefungsAnforderungen`**: Definiert die Startberechtigungen (Lizenzen, Altersklassen) für eine Prüfung oder Abteilung.
|
||||
* **Spartenspezifische Erweiterungen (`...Spezifika`)**: Erweitern `Pruefung_OEPS` um disziplinspezifische Details.
|
||||
|
||||
### 3.5. Nennungs_Context
|
||||
* **Verantwortung:** Abwicklung des gesamten Nennungsprozesses von der Abgabe bis zur finalen Startliste.
|
||||
* **Aggregate Roots:** `Nennung`, `Startliste`.
|
||||
* **Kernentitäten:**
|
||||
* **`Nennung`**: Eine unteilbare Transaktion, die ein Reiter-Pferd-Paar für eine `Pruefung_Abteilung` anmeldet. Verwendet Value Objects als **Snapshots**, um die Daten zum Zeitpunkt der Nennung historisch korrekt zu speichern. [cite_start]Attribute wie `ersatzreiter`, `accontoBetrag` oder `istStallReserviert` sind direkt auf den `KKARTEI`-Satz des Pflichtenhefts zurückzuführen[cite: 160].
|
||||
* **`Startliste`**: Ein eigenständiges Aggregat, das die offizielle, geordnete Startreihenfolge für eine `Pruefung_Abteilung` verwaltet. Es stellt die Konsistenz der Startnummern sicher.
|
||||
* **`Starter`**: Eine Entität innerhalb des `Startliste`-Aggregates; repräsentiert eine Zeile auf der Startliste mit `startnummer`, optionaler `startzeit` und einem Snapshot der Nennungsdaten.
|
||||
|
||||
### 3.6. Ergebnis_Context
|
||||
* **Verantwortung:** Erfassung, Berechnung, Platzierung und Veröffentlichung der Ergebnisse.
|
||||
* **Aggregate Roots:** `Bewerbsergebnis`.
|
||||
* **Kernentitäten:**
|
||||
* **`Bewerbsergebnis`**: Bündelt alle `Einzelergebnis`-Objekte einer `Pruefung_Abteilung`. Seine Hauptaufgabe ist die Berechnung der finalen, konsistenten `Rangliste`. [cite_start]Es speichert auch die Referenzen auf die eingesetzten Richter und Funktionäre gemäß C-Satz[cite: 201].
|
||||
* **`Einzelergebnis`**: Repräsentiert das Ergebnis eines einzelnen Starters. [cite_start]Die Attribute (`platz`, `ausschluss_disq_code`, `geldpreis` etc.) sind direkt auf den **D-Satz** des Pflichtenhefts abgebildet[cite: 210].
|
||||
* **`LeistungVO` (polymorph)**: Ein flexibles Value Object, das die je nach Sparte unterschiedlichen Leistungsdaten (z.B. Wertnote in der Dressur, Fehler/Zeit im Springen) kapselt. [cite_start]Die Daten stammen aus den Feldern `PUNKTE/WERTNOTE` und `ZEIT/PROZENT` im D-Satz[cite: 210].
|
||||
|
||||
---
|
||||
|
||||
## 4. Prozess- und Interaktions-Beispiele
|
||||
|
||||
Die Stärke dieses Designs liegt im Zusammenspiel der entkoppelten Services. Wir haben zwei zentrale Prozesse modelliert:
|
||||
|
||||
* **Nennung wird abgegeben:**
|
||||
* Ein **Command** `NennungAbgeben` wird an den `Nennungs_Context` gesendet.
|
||||
* Dieser validiert die Anfrage durch synchrone **Queries** an den `Veranstaltungs_Context` und den `Lizenzen_und_Qualifikationen_Context`.
|
||||
* Nach erfolgreicher interner Speicherung wird ein asynchrones **Event** `NennungWurdeEingereicht` veröffentlicht, auf das z.B. ein Benachrichtigungs-Service reagieren kann.
|
||||
|
||||
* **Startliste wird erstellt:**
|
||||
* Ein **Command** `ErstelleStartliste` von der **Meldestelle** an den `Nennungs_Context` stößt den Prozess an.
|
||||
* Der Context sammelt alle akzeptierten Nennungen, wendet die Sortier-/Loslogik an und erstellt das `Startliste`-Aggregat.
|
||||
* Ein asynchrones **Event** `StartlisteWurdeFinalisiert` wird veröffentlicht.
|
||||
* Der `Ergebnis_Context` abonniert dieses Event und bereitet sich proaktiv auf die Ergebniserfassung vor, indem er für jeden Starter einen leeren `Einzelergebnis`-Datensatz anlegt.
|
||||
|
||||
## 5. Schlussbemerkung und Ausblick
|
||||
|
||||
Dieses Dokument fasst ein umfassendes, service-orientiertes Domänenmodell zusammen, das als Blaupause für die Entwicklung einer modernen, robusten und erweiterbaren Meldestellen-Software dient.
|
||||
|
||||
Die Architektur mit klar definierten Bounded Contexts, Aggregates und der ereignisgesteuerten Kommunikation ermöglicht es, die hohe fachliche Komplexität des Turniersports beherrschbar zu machen.
|
||||
|
||||
Die nächsten Schritte in einem realen Projekt wären:
|
||||
* Die weitere Detaillierung der Attribute und Methoden in jedem Context.
|
||||
* Die genaue Definition der APIs (Commands, Queries) und Event-Strukturen.
|
||||
* Die Modellierung weiterer unterstützender Kontexte (z.B. für die Bezahlungsabwicklung oder das Berichtswesen).
|
||||
@@ -0,0 +1,54 @@
|
||||
@startuml
|
||||
title "Prozess & Event-Flow: Startlisten-Generierung"
|
||||
|
||||
!theme vibrant
|
||||
|
||||
actor Meldestelle
|
||||
|
||||
participant "API Gateway / Frontend" as Gateway
|
||||
participant "Nennungs_Context" as Nenn
|
||||
queue "Message Bus" as Bus
|
||||
participant "Ergebnis_Context" as Ergebnis
|
||||
|
||||
Meldestelle -> Gateway : POST /startlisten\n(fuer pruefungAbteilungId)
|
||||
|
||||
' 1. Command wird an den zuständigen Context gesendet
|
||||
Gateway -> Nenn : **Command:** ErstelleStartliste(pruefungAbteilungId)
|
||||
activate Nenn
|
||||
note right of Nenn: Empfängt den Befehl,\ndie Startliste für eine\nPrüfungsabteilung zu generieren.
|
||||
|
||||
' 2. Interne Datenbeschaffung und Logik
|
||||
Nenn -> Nenn : getAkzeptierteNennungen(pruefungAbteilungId)
|
||||
note right of Nenn: Holt alle Nennungen mit Status\n"STARTBERECHTIGT_BESTAETIGT"\naus der eigenen Datenbank.
|
||||
|
||||
Nenn -> Nenn : wendeSortierUndLosverfahrenAn(nennungen)
|
||||
note right of Nenn: Hier findet die Kernlogik statt:\n- Zufälliges Losen\n- oder Setzen nach Rangliste\n- Vergabe der Startnummern
|
||||
|
||||
Nenn -> Nenn : Startlisten-Aggregat erstellen\nund speichern (Status: Final)
|
||||
|
||||
note left of Nenn
|
||||
**Neues Aggregat: Startliste**
|
||||
Die erstellte Startliste könnte
|
||||
ein eigenes Aggregate Root im
|
||||
Nennungs_Context sein, das
|
||||
eine geordnete Liste von
|
||||
Startern enthält.
|
||||
end note
|
||||
|
||||
' 3. Asynchrones Event wird veröffentlicht
|
||||
Nenn ->> Bus : **Event:** StartlisteWurdeFinalisiert {startlisteId, pruefungAbteilungId, starter[]}
|
||||
note right of Bus: Das Event informiert das restliche\nSystem über die finale Startliste.\nEs enthält alle nötigen Daten,\n damit die Empfänger nicht extra\nzurückfragen müssen.
|
||||
|
||||
Gateway --> Meldestelle : HTTP 200 OK (Startliste wurde erstellt)
|
||||
deactivate Nenn
|
||||
|
||||
|
||||
' 4. Der Ergebnis-Context reagiert auf das Event
|
||||
Bus ->> Ergebnis : **Event:** StartlisteWurdeFinalisiert
|
||||
activate Ergebnis
|
||||
Ergebnis -> Ergebnis : erstelleBewerbsergebnis(event.pruefungAbteilungId)
|
||||
Ergebnis -> Ergebnis : erstelleEinzelergebnisProStarter(event.starter[])
|
||||
note right of Ergebnis: Der Ergebnis-Context bereitet sich vor.\nEr legt das `Bewerbsergebnis`-Aggregat an\nund erzeugt für jeden Starter einen leeren\n`Einzelergebnis`-Eintrag, der nun auf\ndie Eingabe der Leistung wartet.
|
||||
deactivate Ergebnis
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,114 @@
|
||||
@startuml
|
||||
title "Detailliertes Datenmodell: Nennungs_Context (inkl. Startliste)"
|
||||
|
||||
!theme vibrant
|
||||
|
||||
package "Nennungsabwicklung" as NennungsContext {
|
||||
|
||||
' #################### Aggregat 1: Nennung ####################
|
||||
' (Bekannt aus dem vorherigen Schritt, hier zur Veranschaulichung der Beziehung)
|
||||
class Nennung <<(A,blue) Aggregate Root>> {
|
||||
+ nennungId : UUID <<PK>>
|
||||
--
|
||||
+ pruefung : PruefungsReferenzVO
|
||||
+ reiter : ReiterReferenzVO
|
||||
+ pferd : PferdeReferenzVO
|
||||
+ status : NennungsStatusVO
|
||||
' ... weitere Attribute
|
||||
}
|
||||
|
||||
' #################### Aggregat 2: Startliste ####################
|
||||
class Startliste <<(A,green) Aggregate Root>> {
|
||||
+ startlisteId : UUID <<PK>>
|
||||
--
|
||||
' Referenz auf die Prüfung/Abteilung, für die diese Liste gilt
|
||||
+ pruefung : PruefungsReferenzVO
|
||||
' Zeitstempel der Erstellung und letzten Änderung
|
||||
+ erstellungsZeitpunkt : timestamp
|
||||
+ letzteAenderung : timestamp
|
||||
' Version zur Nachverfolgung von Änderungen
|
||||
+ version : integer
|
||||
' Status der Liste
|
||||
+ status : StartlistenStatusVO
|
||||
}
|
||||
|
||||
' Eine Entität innerhalb des Startlisten-Aggregates
|
||||
entity Starter {
|
||||
+ starterId : UUID <<PK>>
|
||||
--
|
||||
' Die zugewiesene Startnummer, muss innerhalb der Liste eindeutig sein
|
||||
+ startnummer : integer
|
||||
' Die zugewiesene Startzeit (optional)
|
||||
+ startzeit : time
|
||||
' Status des Starters (z.B. falls jemand nach der Erstellung zurückzieht)
|
||||
+ status : StarterStatusVO
|
||||
' Snapshot der Nennungsdaten, die für die Liste relevant sind
|
||||
+ nennungsdaten : NennungsReferenzVO
|
||||
}
|
||||
|
||||
|
||||
' #################### Value Objects (VOs) für Snapshots und Beschreibungen ####################
|
||||
|
||||
' Enum für den Lebenszyklus einer Startliste
|
||||
enum StartlistenStatusVO {
|
||||
VORLAEUFIG
|
||||
FINAL
|
||||
GEAENDERT
|
||||
STORNIERT
|
||||
}
|
||||
|
||||
' Enum für den Status eines einzelnen Starters auf der Liste
|
||||
enum StarterStatusVO {
|
||||
GELISTET
|
||||
ZURUECKGEZOGEN
|
||||
NACHGERUECKT
|
||||
DISQUALIFIZIERT_VOR_START
|
||||
}
|
||||
|
||||
' Ein Snapshot der wichtigsten Nennungsdaten für einen Starter
|
||||
class NennungsReferenzVO <<VO>> {
|
||||
' Referenz zur originalen Nennung
|
||||
+ nennungDbId : UUID
|
||||
' Relevante Daten für die Anzeige auf der Startliste
|
||||
+ reiterName : string
|
||||
+ pferdName : string
|
||||
+ kopfnummer : string
|
||||
+ vereinName : string
|
||||
+ nationCode : string
|
||||
}
|
||||
|
||||
' Referenz auf die Prüfung (bekannt aus Nennung, hier wiederverwendet)
|
||||
class PruefungsReferenzVO <<VO>> {
|
||||
+ pruefungAbteilungDbId : UUID
|
||||
+ bewerbBezeichnung : string
|
||||
+ abteilungBezeichnung : string
|
||||
}
|
||||
|
||||
|
||||
' #################### Beziehungen ####################
|
||||
' Eine Startliste besteht aus einer geordneten Liste von Startern (Komposition)
|
||||
Startliste "1" *-- "1..*" Starter : "enthält geordnet"
|
||||
|
||||
' Ein Starter-Eintrag enthält einen Snapshot der ursprünglichen Nennungsdaten
|
||||
Starter "1" o-- "1" NennungsReferenzVO : "basiert auf"
|
||||
|
||||
' Eine Startliste gehört zu genau einer Prüfung/Abteilung
|
||||
Startliste "1" o-- "1" PruefungsReferenzVO : "für"
|
||||
|
||||
' Status-Beziehungen
|
||||
Startliste "1" -- "1" StartlistenStatusVO
|
||||
Starter "1" -- "1" StarterStatusVO
|
||||
}
|
||||
|
||||
note top of Startliste
|
||||
**Aggregate Root: Startliste**
|
||||
* **Verantwortung:** Dieses Aggregat garantiert die Integrität der Startreihenfolge. Geschäftslogik wie `aendereStartnummer()` oder `zieheStarterZurueck()` wird hier implementiert, um sicherzustellen, dass die Liste konsistent bleibt (z.B. keine doppelten Startnummern).
|
||||
* **Lebenszyklus:** Eine Startliste wird typischerweise als `VORLAEUFIG` erstellt, kann dann `FINAL` gesetzt und bei Bedarf (z.B. durch Ausfälle) `GEAENDERT` werden.
|
||||
end note
|
||||
|
||||
note right of Starter
|
||||
**Entität: Starter**
|
||||
Ein `Starter`-Objekt ist kein Aggregat, da es nicht ohne seine `Startliste` existieren kann. Es repräsentiert eine Zeile auf der Startliste. Es enthält mit der `NennungsReferenzVO` alle Informationen, die für die Anzeige oder den Ausdruck der Liste benötigt werden, ohne dass die ursprüngliche `Nennung` oder andere Services erneut abgefragt werden müssen.
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,96 @@
|
||||
# To-Do-Liste: Ausbaustufen für das ÖTO-Meldestellen-System
|
||||
|
||||
**Stand:** 02. Juli 2025
|
||||
|
||||
Dies ist eine Übersicht der nächsten logischen Schritte zur Vervollständigung des Systemdesigns, aufbauend auf dem bestehenden DDD-Modell.
|
||||
|
||||
---
|
||||
|
||||
## Bounded Context: `Abrechnung`
|
||||
|
||||
Dieser Context ist entscheidend für alle finanziellen Transaktionen und fehlt bisher komplett.
|
||||
|
||||
- [ ] **Datenmodell für Einnahmen entwerfen**
|
||||
- [ ] Entität `Rechnung` für Nenngelder, Stallgebühren etc. erstellen.
|
||||
- [ ] Entität `Zahlungseingang` zur Verfolgung von bezahlten Beträgen modellieren.
|
||||
- [ ] Prozess zur Verknüpfung von Zahlungen mit Nennungen definieren.
|
||||
|
||||
- [ ] **Datenmodell für Ausgaben (Preisgelder) entwerfen**
|
||||
- [ ] Entität `Preisgeldauszahlung` erstellen.
|
||||
- [ ] Prozess für die Berechnung und Zuordnung von Preisgeldern basierend auf der `Ergebnis`-Entität modellieren.
|
||||
- [ ] Statusverfolgung für Auszahlungen (z.B. "offen", "ausbezahlt") definieren.
|
||||
|
||||
- [ ] **Prozess für die Veranstalter-Abrechnung modellieren**
|
||||
- [ ] Logik zur Erstellung einer Endabrechnung (Einnahmen vs. Ausgaben) für den Veranstalter entwerfen.
|
||||
|
||||
---
|
||||
|
||||
## Funktionalität: Mannschaftswertungen
|
||||
|
||||
Die aktuelle Modellierung deckt nur Einzelnennungen ab.
|
||||
|
||||
- [ ] **Datenmodell für Mannschaften erstellen**
|
||||
- [ ] Entität `Mannschaft` definieren (Name, Verein, etc.).
|
||||
- [ ] Entität `Mannschaftsmitglied` als M:N-Beziehung zwischen `Mannschaft` und `Nennung` modellieren.
|
||||
- [ ] Prozess für die Mannschaftsnennung im `Nennungs_Context` entwerfen.
|
||||
|
||||
- [ ] **Modell für Mannschaftsergebnisse definieren**
|
||||
- [ ] Entität `Mannschaftsergebnis` im `Ergebnis_Context` erstellen.
|
||||
- [ ] Geschäftsregeln für die Berechnung von Mannschaftsergebnissen festlegen (z.B. Streichergebnisse).
|
||||
|
||||
---
|
||||
|
||||
## Bounded Context: `Identität & Zugriff`
|
||||
|
||||
Ein detailliertes Berechtigungssystem ist für den Betrieb unerlässlich.
|
||||
|
||||
- [ ] **Rollenkonzept definieren**
|
||||
- [ ] Rollen identifizieren (z.B. Meldestelle, Veranstalter, Richter, Zeitnehmer, OEPS-Admin).
|
||||
- [ ] Rechte pro Rolle granular festlegen (z.B. "darf Ergebnisse eintragen", "darf Turnier anlegen").
|
||||
|
||||
- [ ] **Datenmodell für Benutzer und Rechte erstellen**
|
||||
- [ ] Entität `Benutzer` für den Systemzugang definieren.
|
||||
- [ ] Entitäten für `Rollen` und `Berechtigungen` erstellen und mit `Benutzer` verknüpfen.
|
||||
|
||||
---
|
||||
|
||||
## Funktionalität: Detaillierte Zeitplanung (Zeiteinteilung)
|
||||
|
||||
Die Erstellung eines exakten Zeitplans ist ein komplexer Prozess.
|
||||
|
||||
- [ ] **Ressourcenmodell entwerfen**
|
||||
- [ ] Entitäten für Veranstaltungs-Ressourcen wie `Reitplatz` oder `Abreiteplatz` erstellen.
|
||||
- [ ] Modell zur Planung der Verfügbarkeit von Richtern und Funktionären entwickeln.
|
||||
|
||||
- [ ] **Planungslogik definieren**
|
||||
- [ ] Geschäftsregeln zur Berechnung von Prüfungsdauern (basierend auf Starterzahl) festlegen.
|
||||
- [ ] Logik für die Planung von Pausen, Umbauzeiten und Parallelnutzung von Ressourcen modellieren.
|
||||
|
||||
---
|
||||
|
||||
## Erweiterung: `Nennungs_Context` (Spezialfälle)
|
||||
|
||||
Das OEPS Pflichtenheft beschreibt Spezialfälle, die noch nicht vollständig im Modell abgebildet sind.
|
||||
|
||||
- [ ] **Prozess für Pferdetausch modellieren**
|
||||
- [ ] Methode `tauschePferd()` im `Nennung`-Aggregat entwerfen.
|
||||
- [ ] [cite_start]Logik zur Abbildung des **T-Satzes** für die Ergebnisdatei definieren. [cite: 209, 215]
|
||||
|
||||
- [ ] **Prozess für Nachnennungen modellieren**
|
||||
- [ ] Regeln für Nachnennungen (z.B. erhöhte Gebühren, Fristen) definieren.
|
||||
- [ ] [cite_start]Logik zur Abbildung des **N-Satzes** für die Ergebnisdatei entwerfen. [cite: 211, 215]
|
||||
|
||||
---
|
||||
|
||||
## Funktionalität: Berichtswesen & Dokumentation
|
||||
|
||||
Ein System muss diverse Ausgaben generieren können.
|
||||
|
||||
- [ ] **Design für Standard-Dokumente erstellen**
|
||||
- [ ] Layout und Datenanforderungen für druckfertige Startlisten definieren.
|
||||
- [ ] Layout und Datenanforderungen für offizielle Ergebnislisten definieren.
|
||||
- [ ] Design für weitere Dokumente wie Boxenschilder oder Richterzettel entwerfen.
|
||||
|
||||
- [ ] **Konzept für Berichte entwickeln**
|
||||
- [ ] Datenanforderungen für Finanzberichte für den Veranstalter spezifizieren.
|
||||
- [ ] Konzept für statistische Auswertungen (z.B. Teilnehmer pro Prüfung, erfolgreichste Pferde/Reiter) entwickeln.
|
||||
@@ -0,0 +1,180 @@
|
||||
@startuml
|
||||
title "Detailliertes Datenmodell: Veranstaltungs_Context"
|
||||
|
||||
!theme vibrant
|
||||
|
||||
' Externe Referenzen werden der Übersichtlichkeit halber als vereinfachte Entitäten dargestellt.
|
||||
package "Externe Referenzen (Andere Kontexte)" {
|
||||
class Verein_ZNS
|
||||
class Person_ZNS
|
||||
class OETORegelReferenz
|
||||
class Sportfachliche_Stammdaten
|
||||
}
|
||||
|
||||
package "Veranstaltungsplanung" as VeranstaltungsContext {
|
||||
|
||||
' #################### Aggregate Root: VeranstaltungsRahmen ####################
|
||||
class VeranstaltungsRahmen <<(A,orange) Aggregate Root>> {
|
||||
+ veranstaltungsRahmenId : UUID
|
||||
--
|
||||
+ name : string ' z.B. "Reitertage Musterhof Frühling 2025"
|
||||
+ zeitraum : DatumsbereichVO
|
||||
+ austragungsort : AdresseVO
|
||||
' FK zu Service_ZNS_Daten.Verein_ZNS
|
||||
# hauptveranstalter_verein_nr : VARCHAR(4) <<FK>>
|
||||
}
|
||||
|
||||
' #################### Aggregate Root: Turnier_OEPS ####################
|
||||
class Turnier_OEPS <<(A,orange) Aggregate Root>> {
|
||||
' A-Satz, Stelle 2-6
|
||||
+ oepsTurnierNr : VARCHAR(5) <<PK>>
|
||||
--
|
||||
' FK zu VeranstaltungsRahmen
|
||||
# veranstaltungsRahmenId : UUID <<FK>>
|
||||
' A-Satz, Stelle 7-31
|
||||
+ name_ort : VARCHAR(25)
|
||||
' A-Satz, Stelle 32-47
|
||||
+ zeitraum : DatumsbereichVO
|
||||
' A-Satz, Stelle 48-72
|
||||
+ kategorie_text_turnier : VARCHAR(25)
|
||||
+ turnierart_sparte : string ' z.B. CDN-A, CSN-B*
|
||||
' A-Satz (Ergebnis), Stelle 73-75
|
||||
+ protokoll_version_oeps : VARCHAR(3)
|
||||
' A-Satz (Ergebnis), Stelle 76-95
|
||||
+ meldestelle_software_version : VARCHAR(20)
|
||||
' A-Satz (Ergebnis), Stelle 96-103
|
||||
+ link_id_turnier : VARCHAR(8)
|
||||
}
|
||||
|
||||
' #################### Aggregate Root: Meisterschaft_Cup_Serie ####################
|
||||
class Meisterschaft_Cup_Serie <<(A,orange) Aggregate Root>> {
|
||||
+ mcsId : UUID <<PK>>
|
||||
--
|
||||
+ name : string ' z.B. "XYZ Sommercup 2025"
|
||||
+ typ : string ' Meisterschaft, Cup, Serie
|
||||
+ jahr : INTEGER
|
||||
+ sparte : string
|
||||
# oetoRegelRefId : UUID <<FK>>
|
||||
}
|
||||
|
||||
' #################### Entitäten innerhalb des Turnier_OEPS Aggregats ####################
|
||||
' Ein Bewerb innerhalb eines Turniers
|
||||
entity Pruefung_OEPS {
|
||||
+ pruefungId : UUID <<PK>>
|
||||
--
|
||||
' B-Satz, Stelle 61-63 (3-stellig)
|
||||
+ oepsBewerbNr : VARCHAR(3)
|
||||
' B-Satz, Stelle 5-39
|
||||
+ name_text_pruefung : VARCHAR(35)
|
||||
' B-Satz, Stelle 40-43
|
||||
+ klasse_text : VARCHAR(4)
|
||||
' B-Satz, Stelle 44-51
|
||||
+ kategorie_text_pruefung : VARCHAR(8)
|
||||
' B-Satz, Stelle 52-59
|
||||
+ datumPruefung : Date
|
||||
' Zur Steuerung der spartenspezifischen Logik
|
||||
+ artDisziplin : DisziplinVO
|
||||
' B-Satz (Ergebnis), Stelle 64-71
|
||||
+ link_id_pruefung : VARCHAR(8)
|
||||
}
|
||||
|
||||
' Eine Abteilung eines Bewerbs
|
||||
entity Pruefung_Abteilung {
|
||||
+ abteilungId : UUID <<PK>>
|
||||
--
|
||||
' B-Satz, Stelle 4
|
||||
+ oepsAbteilungNr : INTEGER
|
||||
+ bezeichnung : string ' z.B. "Abt. 1: Junioren"
|
||||
' B-Satz (Ergebnis), Stelle 52-54
|
||||
+ anzahl_starter_gemeldet : INTEGER
|
||||
' B-Satz (Ergebnis), Stelle 55-60 (ohne Komma)
|
||||
+ geldpreis_summe : DECIMAL
|
||||
}
|
||||
|
||||
' Entität zur Speicherung der konkreten Anforderungen für eine Prüfung/Abteilung
|
||||
entity PruefungsAnforderungen {
|
||||
+ anforderungenId: UUID <<PK>>
|
||||
--
|
||||
' Kann sich auf eine Pruefung_OEPS oder eine Pruefung_Abteilung beziehen
|
||||
# bezugs_id: UUID <<FK>>
|
||||
' FK zu Service_OeTO_Verwaltung.OETORegelReferenz
|
||||
# oeto_regel_ref_id: UUID <<FK>>
|
||||
}
|
||||
|
||||
entity Anforderung_Lizenz {
|
||||
# anforderungenId: UUID <<FK>>
|
||||
# lizenzTypCode: VARCHAR(4) <<FK>>
|
||||
}
|
||||
|
||||
entity Anforderung_Altersklasse {
|
||||
# anforderungenId: UUID <<FK>>
|
||||
# altersklasseCode: VARCHAR(4) <<FK>>
|
||||
}
|
||||
|
||||
' #################### Spartenspezifische Erweiterungen ####################
|
||||
package "Sportfachliche Details für Prüfungen" {
|
||||
' Alle Spezifika sind 1:1 mit Pruefung_OEPS verbunden
|
||||
entity DressurPruefungSpezifika {
|
||||
# pruefungId : UUID <<PK>> <<FK>>
|
||||
--
|
||||
' FK zu Service_OeTO_Verwaltung.Sportfachliche_Stammdaten
|
||||
# aufgabe_stammdatum_id : UUID <<FK>>
|
||||
+ platz_groesse_code : string ' z.B. 20x40, 20x60
|
||||
}
|
||||
entity SpringenPruefungSpezifika {
|
||||
# pruefungId : UUID <<PK>> <<FK>>
|
||||
--
|
||||
' FK zu Service_ZNS_Daten.Person_ZNS
|
||||
# parcours_designer_person_id : VARCHAR(6) <<FK>>
|
||||
+ anzahl_hindernisse : INTEGER
|
||||
+ hoehe_max_cm : INTEGER
|
||||
+ erlaubte_zeit_sek : INTEGER
|
||||
' FK zu Service_OeTO_Verwaltung.Sportfachliche_Stammdaten
|
||||
# wertungs_verfahren_stammdatum_id : UUID <<FK>>
|
||||
+ anzahl_umlaeufe : INTEGER
|
||||
+ anzahl_stechen : INTEGER
|
||||
}
|
||||
' (Vielseitigkeit und RVK Spezifika hier analog)
|
||||
}
|
||||
|
||||
' #################### Value Objects ####################
|
||||
class DatumsbereichVO <<VO>> {
|
||||
+von: Date,
|
||||
+bis: Date
|
||||
}
|
||||
class AdresseVO <<VO>> {
|
||||
+strasse: string,
|
||||
+plz: string,
|
||||
+ort: string
|
||||
}
|
||||
class DisziplinVO <<VO>> {
|
||||
+name: string
|
||||
}
|
||||
|
||||
' #################### Beziehungen ####################
|
||||
' Aggregat-Hierarchie
|
||||
VeranstaltungsRahmen "1" -- "1..*" Turnier_OEPS : "beinhaltet"
|
||||
Turnier_OEPS "1" *-- "1..*" Pruefung_OEPS : "besteht aus"
|
||||
Pruefung_OEPS "1" *-- "1..*" Pruefung_Abteilung : "unterteilt in"
|
||||
|
||||
' Anforderungen zuordnen
|
||||
Pruefung_OEPS "1" -- "1" PruefungsAnforderungen : "hat"
|
||||
Pruefung_Abteilung "1" -- "0..1" PruefungsAnforderungen : "hat spezielle"
|
||||
PruefungsAnforderungen "1" -- "*" Anforderung_Lizenz
|
||||
PruefungsAnforderungen "1" -- "*" Anforderung_Altersklasse
|
||||
|
||||
' Sparten-Spezifika zuordnen (Vererbung/Erweiterung)
|
||||
Pruefung_OEPS <|-- DressurPruefungSpezifika
|
||||
Pruefung_OEPS <|-- SpringenPruefungSpezifika
|
||||
|
||||
' Meisterschaft/Cup Beziehungen
|
||||
class MCS_Wertungspruefung
|
||||
(Meisterschaft_Cup_Serie, MCS_Wertungspruefung) .up. Pruefung_Abteilung
|
||||
}
|
||||
|
||||
' Beziehungen zu externen Kontexten
|
||||
VeranstaltungsContext.VeranstaltungsRahmen -- Verein_ZNS : "veranstaltet von"
|
||||
VeranstaltungsContext.SpringenPruefungSpezifika -- Person_ZNS : "Parcoursdesigner"
|
||||
' Weitere Beziehungen zu OETORegelReferenz, Stammdaten etc.
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,134 @@
|
||||
@startuml
|
||||
title "Bounded Context: ZNS-Import als Anti-Corruption Layer (ACL)"
|
||||
|
||||
!theme vibrant
|
||||
|
||||
' Der Bounded Context wird als Paket mit dem Stereotyp <<ACL>> dargestellt
|
||||
package "ZNS_Import_ACL" <<ACL>> {
|
||||
|
||||
' Eine Komponente, die die Übersetzungslogik kapselt
|
||||
component ZNS_Daten_Uebersetzer [
|
||||
+ importiere_zns_zip()
|
||||
+ uebersetze_Personen()
|
||||
+ uebersetze_Pferde()
|
||||
+ uebersetze_Vereine()
|
||||
]
|
||||
|
||||
' Die folgenden Entitäten repräsentieren die 1:1 Abbildung der Zeilen aus den ZNS .dat-Dateien.
|
||||
' Sie sind "anämisch" und enthalten keine Geschäftslogik.
|
||||
|
||||
package "Rohdaten-Struktur aus ZNS-Dateien" {
|
||||
' Basierend auf VEREIN01.DAT
|
||||
entity ZNS_VEREIN01_dat_Satz {
|
||||
' VEREIN (Nummer), Stelle 1-4 [cite: 177]
|
||||
+ verein_nummer : VARCHAR(4)
|
||||
' VEREINSNAME, Stelle 5-54 [cite: 177]
|
||||
+ vereinsname : VARCHAR(50)
|
||||
}
|
||||
|
||||
' Basierend auf PFERDE01.DAT
|
||||
entity ZNS_PFERDE01_dat_Satz {
|
||||
' SATZNUMMER DES PFERDES, Stelle 202-211 [cite: 164]
|
||||
+ satznummer_des_pferdes : VARCHAR(10)
|
||||
' PFERDENAME, Stelle 5-34 [cite: 164]
|
||||
+ pferdename : VARCHAR(30)
|
||||
' LEBENSNUMMER, Stelle 35-43 [cite: 164]
|
||||
+ lebensnummer : VARCHAR(9)
|
||||
' GEB.JAHR, Stelle 45-48 [cite: 164]
|
||||
+ geb_jahr : VARCHAR(4)
|
||||
' ... (weitere Felder gemäß Pflichtenheft S.8)
|
||||
}
|
||||
|
||||
' Basierend auf RICHT01.DAT
|
||||
entity ZNS_RICHT01_dat_Satz {
|
||||
' ID, Stelle 1 ('X' oder 'Y') [cite: 154, 162]
|
||||
+ id_satzart : CHAR(1)
|
||||
' SATZNUMMER, Stelle 2-7 [cite: 154, 162]
|
||||
+ satznummer : VARCHAR(6)
|
||||
' NAME, Stelle 8-82 [cite: 154, 162]
|
||||
+ name_komplett : VARCHAR(75)
|
||||
' QUALIFIKATIONEN, Stelle 83-112 [cite: 154, 162]
|
||||
+ qualifikationen_text : VARCHAR(30)
|
||||
}
|
||||
|
||||
' Basierend auf LIZENZ01.DAT
|
||||
entity ZNS_LIZENZ01_dat_Satz {
|
||||
' SATZNUMMER DES REITERS, Stelle 1-6
|
||||
+ satznummer_des_reiters : VARCHAR(6)
|
||||
' FAMILIENNAME, Stelle 7-56
|
||||
+ familienname : VARCHAR(50)
|
||||
' VORNAME, Stelle 57-81
|
||||
+ vorname : VARCHAR(25)
|
||||
' VEREINSNAME, Stelle 84-133
|
||||
+ vereinsname_text : VARCHAR(50)
|
||||
' REITERLIZENZ, Stelle 137-140
|
||||
+ reiterlizenz_code : VARCHAR(4)
|
||||
' FAHRLIZENZ, Stelle 142-143
|
||||
+ fahrlizenz_code : VARCHAR(2)
|
||||
' LIZENZINFO, Stelle 201-210
|
||||
+ lizenzinfo_text : VARCHAR(10)
|
||||
' GEBURTSDATUM, Stelle 182-189
|
||||
+ geburtsdatum_text : VARCHAR(8)
|
||||
' ... (weitere Felder gemäß Pflichtenheft S.8)
|
||||
}
|
||||
}
|
||||
|
||||
' Die Pfeile zeigen den Datenfluss: Der Übersetzer konsumiert die Rohdaten-Sätze.
|
||||
ZNS_Daten_Uebersetzer ..> ZNS_VEREIN01_dat_Satz : "liest"
|
||||
ZNS_Daten_Uebersetzer ..> ZNS_PFERDE01_dat_Satz : "liest"
|
||||
ZNS_Daten_Uebersetzer ..> ZNS_RICHT01_dat_Satz : "liest"
|
||||
ZNS_Daten_Uebersetzer ..> ZNS_LIZENZ01_dat_Satz : "liest"
|
||||
}
|
||||
|
||||
' Außerhalb des ACLs liegt unser sauberes, internes Domänenmodell.
|
||||
' Der ACL übersetzt die Rohdaten in diese Ziel-Strukturen (oder Events, die diese erzeugen).
|
||||
package "Internes Domänenmodell (Ziel)" {
|
||||
' Beispielhaft: das Ziel-Objekt im Personen-Context
|
||||
class Personenstamm <<Aggregate Root>> {
|
||||
+ oepsSatzNrPerson : VARCHAR(6)
|
||||
--
|
||||
+ name : FamiliennameVO
|
||||
+ geburtsdatum : Date
|
||||
+ hauptverein : VereinsReferenz
|
||||
+ hatLizenz(lizenzTyp) : boolean
|
||||
}
|
||||
|
||||
' Beispielhaft: das Ziel-Objekt im Lizenz-Context
|
||||
class Lizenznehmer <<Aggregate Root>> {
|
||||
+ oepsSatzNrPerson : VARCHAR(6)
|
||||
--
|
||||
+ lizenzen : List<Lizenz>
|
||||
+ qualifikationen: List<Qualifikation>
|
||||
}
|
||||
}
|
||||
|
||||
' Der Übersetzer im ACL erzeugt/aktualisiert die internen Domänenobjekte.
|
||||
' Dies geschieht oft über Events oder direkte Service-Aufrufe.
|
||||
ZNS_Daten_Uebersetzer ..> Personenstamm : "erzeugt/aktualisiert"
|
||||
ZNS_Daten_Uebersetzer ..> Lizenznehmer : "erzeugt/aktualisiert"
|
||||
|
||||
note right of ZNS_Import_ACL
|
||||
**Anti-Corruption Layer (ACL)**
|
||||
|
||||
Dieser Bounded Context hat eine einzige
|
||||
Verantwortlichkeit: Er schützt das
|
||||
System vor den Details und der
|
||||
Komplexität der externen ZNS-Schnittstelle.
|
||||
|
||||
1. **Einlesen:** Die `.dat`-Dateien werden 1:1 in die
|
||||
`ZNS_*_dat_Satz`-Entitäten eingelesen.
|
||||
2. **Übersetzen:** Die Komponente `ZNS_Daten_Uebersetzer`
|
||||
transformiert diese Rohdaten. Sie löst
|
||||
Codes auf (z.B. Bundesland), normalisiert
|
||||
Daten (z.B. kommaseparierte Lizenzen)
|
||||
und validiert die Daten.
|
||||
3. **Veröffentlichen:** Das Ergebnis der Übersetzung
|
||||
wird an die zuständigen internen Bounded
|
||||
Contexts weitergegeben, z.B. durch das
|
||||
Auslösen von Domänen-Events wie
|
||||
`PersonenStammdatenAktualisiert` oder
|
||||
`NeueLizenzInformationVerfügbar`.
|
||||
end note
|
||||
|
||||
|
||||
@enduml
|
||||
Reference in New Issue
Block a user