# RoggioApp - Analyse: Die Person-Entität (Hard vs. Flexible) ## Die Herausforderung Was gehört zwingend als harte, durchsuchbare SQL-Spalte in die Tabelle `Person`, und was lagern wir als flexible Eigenschaft (Trait) in das JSONB-Feld oder in Relationen aus? ## Sevs Entwurf & Claras Analyse ### 1. Originär (Die Person selbst) * **Vorname & Nachname:** ✅ Absolut notwendig. Harte SQL-Spalten. (Ggf. um `nickname` als Spalte erweitern). * **Geburtsdatum:** ✅ Harte Spalte (für Altersberechnungen, Kurtaxe, Meldescheine extrem wichtig). * **Mobile-Nr & Private E-Mail:** ⚠️ *Diskussion:* Sind die eindeutig? * *Realität:* Oft buchen Ehepaare oder Familien mit nur *einer* gemeinsamen Mailadresse oder Handynummer. * *Lösung:* Wir dürfen `email` und `phone` **nicht** auf `UNIQUE` (Einzigartig) in der Datenbank setzen! Sie bleiben harte SQL-Spalten für die schnelle Suche, aber Doppelungen sind erlaubt. ### 2. Relational / Flexibel (Die Eigenschaften) * **Anrede (Pronouns / Title):** -> JSONB (`traits`). (Ändert sich gesellschaftlich, JSONB ist hier flexibler als ein harter SQL-Enum). * **Identity-Dokument (Perso/Passport):** -> JSONB (`traits`). * *Warum:* Die Felder variieren stark je nach Land (Ausgestellt in, Gültig bis, Behörde). Ein JSONB-Objekt `identity_document: { type: "passport", number: "...", valid_until: "..." }` ist hier perfekt. * **Geburtsort:** -> JSONB (`traits`). * **Adresse (Wohnort):** -> ⚠️ *Eigene Tabelle!* * *Warum:* Eine Adresse (Straße, Stadt, Land) kann von 5 Familienmitgliedern oder einer Firma geteilt werden. Wir bauen eine Tabelle `Address` und verknüpfen die Person(en) per ID damit. Das spart immense Redundanz. * **Finanz-Infos (IBAN etc.):** -> JSONB (`traits`). ### 3. Logisch durch den Graphen gelöst (Architektur-Bonus) * **Buchungsverlauf:** Wird *nicht* in der Person gespeichert. Ergibt sich zu 100% aus den `Event`-Knoten, die mit der Person (oder ihrer Gruppe) verknüpft sind. * **Loginrights:** Werden nicht gespeichert. Das übernimmt die Nextcloud (SSO). Wir speichern nur die `ssoId`. * **Type (Kollektiv / Client / Personal):** Wird *nicht* als hartes Feld gespeichert! Ergibt sich aus den `GroupMembership`-Verknüpfungen (z.B. wenn Person A Mitglied in der Operator-Gruppe ist, ist sie Personal). * **Groups & Groupfunction:** Wird 1:1 durch unsere Pivot-Tabelle `GroupMembership` (inkl. `role`) abgebildet. ## Claras Vorschlag für die finale Prisma-Tabelle `Person` ```prisma model Person { id String @id @default(uuid()) firstName String @db.VarChar(100) lastName String @db.VarChar(100) nickname String? @db.VarChar(100) birthDate DateTime? @db.Date // Kontaktdaten (NICHT UNIQUE!) email String? @db.VarChar(255) phone String? @db.VarChar(50) // Verknüpfung zur Authentifizierung ssoId String? @unique @db.VarChar(255) // Die Wohnadresse (Relational) addressId String? address Address? @relation(fields: [addressId], references: [id]) // Alles andere (Pass, Finanzen, Anrede, Geburtsort, Allergien) traits Json @default("{}") groupMemberships GroupMembership[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } ```