Zum Inhalt

XOLIB Soll-Schema — Datenarchitektur Blaupause

Version: 1.6 Datum: 2026-03-21 Status: Referenzdokument — jedes Feature wird gegen dieses Schema gebaut Aktueller Stand (Soll): 109 Models, 30 Enums, ~2400 Felder, Qualität 7/10 Ist-Stand (22.03.2026): 140 Models, 65 Enums, 4990 Schema-Zeilen Review: Extern bewertet — "Für Pre-Launch außergewöhnlich stark", 6 strategische Lücken identifiziert


Prinzipien

  1. Single Source of Truth — Jeder Datenpunkt hat genau einen Ort. Keine Doppelpflege.
  2. Historisierung — Jede Änderung wird protokolliert. Wir wissen nicht nur WAS ist, sondern WAS WAR.
  3. AI-First — Jedes Model muss Daten liefern die der KI helfen. Mehr Felder = bessere Vorhersagen.
  4. Data Moat — Jeder Mandant macht das System schlauer. Event Sourcing + Feedback Loops + Cross-Tenant Benchmarks.
  5. Workflow-Moat — Daten allein sind kein Moat. Wer den operativen Workflow kontrolliert (Zahlungsfluss, Compliance, Dokumentation), macht den Wechsel schmerzhaft — nicht wegen Features, sondern wegen operativer Abhängigkeit.
  6. DSGVO by Design — PII verschluesselt, Loeschfristen definiert, k-Anonymität bei Aggregation.
  7. English Field Names — Code ist Englisch. Deutsche Begriffe nur in Enums wo fachlich noetig.
  8. Enums statt Strings — Jedes Feld mit festen Werten bekommt ein Enum.
  9. Kein Binary in DB — Bilder und PDFs in Object Storage, nur URLs in der DB.
  10. Max 30 Felder pro Model — Groessere Models werden gesplittet. JSON nur fuer volatile Settings.
  11. Jedes Model hat tenantId + RLS — Multi-Tenancy wird auf DB-Ebene erzwungen — tenantId auf jedem Model + PostgreSQL Row-Level Security. App-Code kann vergessen zu filtern, RLS nicht.
  12. Platform-First — Xolib ist nicht nur Software, sondern Infrastruktur auf der andere bauen (API-as-Product, Webhooks, OpenImmo).
  13. Human-in-the-Loop — KI empfiehlt, der Mensch entscheidet. Kein AI-Agent darf eine Aktion mit rechtlicher oder finanzieller Wirkung auf eine natuerliche Person automatisch ausfuehren (Art. 22 DSGVO). Jede solche Aktion erfordert requiresHumanApproval: true. Verstoesse: bis zu 20 Mio EUR oder 4% Jahresumsatz.
  14. PII-Stripping vor LLM-Calls — Alle AI-Agent-Calls die personenbezogene Daten enthalten, muessen vor dem API-Call durch eine PII-Stripping-Middleware laufen. Mieternamen → Pseudonyme, IBANs → maskiert, Adressen → PLZ-Ebene. Zuordnung bleibt lokal bei Xolib. Loest DSGVO-Compliance UND Vendor-Lock-in.

Loesch-Strategie (Soft-Delete-Standard)

Datentyp Strategie Beispiele
Stammdaten Soft-Delete (isDeleted, deletedAt, deleteReason) Property, Unit, Owner, User
Transaktionsdaten NIEMALS loeschen (§257 HGB, 7 Jahre) Payment, BankTransaction, Lease
Kommunikation Soft-Delete, nach DataRetentionPolicy pseudonymisieren Ticket, Message, Letter
Events Append-only, archivieren nach 12-24 Monaten XolibEvent, ChangeLog, AuditLog
AI-Daten Behalten (Training Data) AgentFeedback, AgentOutcome, EntityMemory
Bilder/Dokumente Soft-Delete, nach 90 Tagen physisch loeschen PropertyImage, PropertyDocument

Cascade-Regeln: - Property geloescht → Units werden NICHT geloescht (Soft-Delete auf Property reicht) - Unit geloescht → Pruefen ob aktiver Lease existiert (blockiert) - User geloescht → Pseudonymisierung: Name→"Geloeschter Nutzer", Email→hash, Phone→null - Tenant geloescht → Alles loeschen (CASCADE) — Mandant komplett weg


Schema-Evolution (Zero-Downtime-Migrationen)

  1. Neue Felder immer optional (nullable) — nie required in einem Schritt
  2. Zwei-Phasen-Deploys:
  3. Phase 1: Schema-Migration (neues Feld hinzufuegen, optional)
  4. Phase 2: Code-Deploy (nutzt neues Feld, schreibt Defaults)
  5. Phase 3: Cleanup (altes Feld entfernen — erst wenn alle Daten migriert)
  6. Enum-Erweiterungen: Neue Werte hinzufuegen ist safe. Werte umbenennen/entfernen NIE ohne Migration.
  7. Prisma db push fuer Dev, prisma migrate deploy fuer Prod — nie migrate dev auf Prod
  8. Backup vor jeder Migrationpg_dump vor jedem Schema-Change auf dem Server

Domains & Models

Domain 1: CORE (Tenant, User, Auth)

Tenant (ERWEITERN — Lifecycle + Settings)

  • Aktuell: 65+ Relations, settings JSON catch-all
  • Aenderung: settings JSON bleibt, aber dokumentiert welche Keys existieren
  • Neue Felder (Lifecycle):
  • status (Enum: TRIAL, ONBOARDING, ACTIVE, CHURNING, SUSPENDED, DELETED)
  • trialEndsAt DateTime?
  • onboardedAt DateTime? — wann Import abgeschlossen
  • churnRiskScore Float? — 0.0-1.0, berechnet aus Nutzungsdaten (Feature Store)
  • lastActiveAt DateTime? — letzter Login irgendeines Users
  • Prioritaet: Phase C (Churn-Prevention)

User (SPLITTEN)

  • Aktuell: 50+ Felder — Auth + Profil + Demographie + Praeferenzen gemischt
  • Soll:
Model Felder Zweck
User id, tenantId, email, passwordHash, totpSecret, role, isActive, lastLoginAt, lang Auth & Identity
UserProfile userId, name, phone, dateOfBirth, nationality, incomeRange, householdType, photo Persoenliche Daten (PII — verschluesselt)
UserPreferences userId, preferences (JSON) UI-Einstellungen, KPI-Auswahl, Insight-Settings
  • Migration: UserProfile und UserPreferences aus bestehenden User-Feldern befuellen
  • Prioritaet: Bei Personen-Modul V6

Owner (ERWEITERN)

  • Aktuell: 9 Felder, IBAN unverschluesselt
  • Aenderung: bankAccountIBAN verschluesseln (AES-256-GCM wie BankAccount)
  • Neue Felder: ownerType (PRIVAT, GBR, GMBH, WEG, ERBENGEMEINSCHAFT), taxId, ustIdNr
  • Prioritaet: Bei Finanzen-Modul

Domain 2: PROPERTY (Gebaeude & Einheiten)

Property (SPLITTEN — 75 Felder → 3 Models)

Model Felder Zweck
Property id, tenantId, propertyNumber, name, street, zipCode, city, stadtteil, bundesland, latitude, longitude, propertyType (Enum), houseType (Enum), objectCondition (Enum), buildingYear, isNeubau, buildingPhase, isDenkmalschutz, isDeleted, deletedAt, deleteReason, imageUrl, notes, ownerId, verwaltungsArt, verwaltungsgebiet Stammdaten & Standort
PropertyBuilding propertyId (1:1), totalFloors, plotSizeSqm, totalHeatedSqm, roofType, facadeType, windowType, parkingSpaceCount, garageSpaces, elevatorAvailable, isBarrierFree, hasDisabledParking, entranceWidthCm, hasFiber, internetProvider, internetMaxSpeed, hasAC, ventilationType, gardenAvailable, lastRenovationYear, roofRenovationYear, windowsRenovationYear Gebaeudedaten & Technik
PropertyEnergy propertyId (1:1), heatingType (Enum), heatingYear, heatingBrand, heatingNotes, energyCarrier (Enum), warmWaterType, hasWarmWaterCirculation, energyAusweisType, energyValue, energyEfficiencyClass, energyAusweisExpiry, hasSolarPanels, solarKwp, hasEVCharger, hasSmartMeter Energie & Heizung
PropertyFinance propertyId (1:1), bankAccountIBAN (encrypted), bankAccountBIC, bankName, grundsteuerAmount, gebaeudeversicherung, hausgeld, mietspiegel, bodenrichtwert Finanzen & Verwaltung
  • Migration: Felder aus Property in neue 1:1 Models kopieren, API-Response bleibt flach (JOIN)
  • Prioritaet: JETZT (Properties V6 laeuft gerade)

Unit (BEREINIGEN — Equipment normalisieren)

Model Felder Zweck
Unit id, tenantId, propertyId, number, floor, position, sqm, rooms, unitType (Enum), condition, mea, notes Stammdaten
UnitEquipment unitId (1:1), hasBalcony, sqmBalcony, hasTerrace, sqmTerrace, hasGarden, sqmGarden, hasKeller, sqmKeller, hasEBK, hasGuestWC, hasAbstellraum, hasWashingConn, hasStellplatz, bathroomCount, floorType, windowType, ceilingHeight, loadCapacity Ausstattung
UnitAccessibility unitId (1:1), isBarrierFree, doorWidthCm, hasFlushShower, hasGrabBars, hasShowerSeat Barrierefreiheit
UnitRentalTerms unitId (1:1), isWBS, furnishing (Enum), petsAllowed (Enum), lastRenovation Mietkonditionen
  • Deprecated Felder auf Unit (Schritt 4): currentRent, nkVorschuss, currentTenantId → kommen aus Lease
  • Migration: Equipment-Felder in UnitEquipment kopieren
  • Prioritaet: JETZT (Einheiten-Tab wird gerade gebaut)

ParkingSpace (NEU — bereits erstellt ✅)

  • Eigenes Model fuer Stellplaetze mit Nummer, Typ, Miete, EV-Charger
  • Relation zu Property + optional Unit

PropertyImage (AENDERN — Binary raus)

  • Aktuell: data Bytes in DB
  • Soll: url String → Object Storage (S3/Cloudflare R2)
  • Prioritaet: Spaeter (funktioniert, nur Skalierbarkeit)

Domain 3: LEASE & PAYMENTS (Mietvertraege & Zahlungen)

Lease (SPLITTEN bei Bedarf)

  • Aktuell: 40+ Felder — Wohnraum + Gewerbe gemischt
  • Soll: Vorerst beibehalten — die Trennung Wohnraum/Gewerbe ueber leaseType Enum funktioniert
  • Neue Felder: Keine noetig
  • Wichtig: rentIncreaseHistory JSON → eigenes Model

RentAdjustment (NEU)

Model RentAdjustment {
  id, leaseId, tenantId
  type: Enum (INDEX, STAFFEL, MIETSPIEGEL, VERGLEICHSMIETE, MODERNISIERUNG)
  previousRent, newRent
  effectiveDate
  reason, legalBasis (z.B. "§558 BGB")
  status: Enum (BERECHNET, ANGEKUENDIGT, WIRKSAM, WIDERSPRUCH)
  notificationDate, notificationLetterUrl
  createdAt, createdBy
}
- Zweck: Rechtsichere Dokumentation jeder Mietaenderung - Data Moat: Cross-Tenant lernt welche Mietanpassungen durchgehen, welche Widerspruch bekommen - Prioritaet: Beim Mietanpassungs-Feature

Payment (ERWEITERN)

  • Neue Felder: matchCorrected Boolean, matchCorrectedAt DateTime (Feedback fuer Bank-Matching-KI)
  • Prioritaet: Beim Banking-Modul V2

Domain 4: HISTORISIERUNG (NEU — Kernstück des Umbaus)

ChangeLog (NEU — JETZT BAUEN)

Model ChangeLog {
  id          String   @id @default(cuid())
  tenantId    String
  entityId    String
  field       String   // "baseRent", "energyClass", "condition"
  oldValue    String?  // JSON stringified
  newValue    String?  // JSON stringified
  changedBy   String   // userId
  changedAt   DateTime @default(now())
  reason      String?  // "Mietanpassung Index 2026", "Sanierung abgeschlossen"
  source       ChangeSource      // Enum: API, IMPORT, AGENT, MIGRATION, WEBHOOK, SYSTEM, MANUAL
  entityType   ChangeEntityType  // Enum: PROPERTY, UNIT, LEASE, PAYMENT, OWNER, TICKET, etc.
  containsPII  Boolean @default(false) // DSGVO: true wenn personenbezogene Daten betroffen → faellt unter Loeschanfragen

  @@index([tenantId, entityType, entityId])
  @@index([tenantId, changedAt])
  @@index([entityType, field])
}
- Implementierung: Middleware-Hook in der API — bei jedem PATCH/UPDATE automatisch ChangeLog schreiben - Data Moat: Wir wissen wie sich jedes Objekt ueber die Zeit entwickelt hat - Cross-Tenant: "Objekte die saniert wurden hatten danach 12% hoehere Miete" — nur moeglich mit Historie - Prioritaet: SOFORT — vor allen anderen Aenderungen, damit ab jetzt alles getrackt wird

Partitionierung & Archivierung: - PostgreSQL Native Partitioning nach changedAt (quartalsweise) - Archivierung nach 24 Monaten in ChangeLogArchive (gleiche Struktur, separate Tabelle) - Retention an DataRetentionPolicy koppeln - Geschaetztes Volumen: ~100 Mio Eintraege/Jahr bei 10.000 Mandanten

Abgrenzung ChangeLog ↔ XolibEvent: - XolibEvent = "Was ist passiert?" (Domain-Ebene) — z.B. "Mieter hat Ticket erstellt", "Agent hat Mietanpassung empfohlen" - ChangeLog = "Was hat sich geaendert?" (Daten-Ebene) — z.B. "baseRent: 650 → 690", "energyClass: D → C" - Beide zusammen ergeben den vollstaendigen Kontext fuer die KI: Das Event erklaert das WARUM, der ChangeLog zeigt das WAS


Domain 5: CROSS-TENANT INTELLIGENCE (NEU)

SystemBenchmark (NEU)

Model SystemBenchmark {
  id            String   @id @default(cuid())
  metricKey     String   // "operating_costs_sqm", "vacancy_rate", "payment_ratio"
  period        String   // "2026-Q1", "2026-03"
  cohortCity    String?  // "Berlin", "Muenchen" (generalized)
  cohortType    String?  // "MFH", "EFH" (building type)
  cohortSize    String?  // "KLEIN", "MITTEL", "GROSS" (unit count class)
  tenantCount   Int      // Must be >= 5 (k-anonymity)
  sampleCount   Int
  mean          Float
  median        Float
  p10           Float
  p25           Float
  p75           Float
  p90           Float
  stdDev        Float?
  expiresAt     DateTime?  // createdAt + 2 Quartale — danach ungueltig
  isStale       Boolean    @default(false) // true wenn Kohorte unter k faellt
  createdAt     DateTime @default(now())

  @@unique([metricKey, period, cohortCity, cohortType, cohortSize])
}

SystemModelParam (NEU)

Model SystemModelParam {
  id             String   @id @default(cuid())
  modelKey       String   // "bank_matching_weights", "score_normalization"
  version        Int
  parameters     Json     // { amount: 0.35, date: 0.20, reference: 0.25, name: 0.20 }
  trainingSize   Int
  accuracy       Float?
  validFrom      DateTime
  validUntil     DateTime?
  isActive       Boolean  @default(true)
  createdAt      DateTime @default(now())

  @@unique([modelKey, version])
}

SystemTemplate (NEU)

Model SystemTemplate {
  id              String   @id @default(cuid())
  templateType    String   // "MAHNUNG", "MIETERHOHUNG", "WILLKOMMEN"
  templateKey     String   // "mahnung_stufe1_freundlich"
  content         String
  usageCount      Int      @default(0)
  successRate     Float?   // 0.0-1.0
  avgOutcomeDays  Float?   // Days until positive outcome
  tenantCount     Int      @default(0)
  isRecommended   Boolean  @default(false)
  createdAt       DateTime @default(now())
  updatedAt       DateTime @updatedAt

  @@unique([templateType, templateKey])
}

TenantTemplate (NEU — mandantenspezifische Overrides)

Model TenantTemplate {
  id               String   @id @default(cuid())
  tenantId         String
  systemTemplateId String?  // NULL = komplett eigenes Template
  templateType     String
  templateKey      String
  content          String   // Mandanten-Override
  usageCount       Int      @default(0)
  createdAt        DateTime @default(now())
  updatedAt        DateTime @updatedAt

  @@unique([tenantId, templateType, templateKey])
}
- Logik: Zuerst TenantTemplate pruefen, dann SystemTemplate als Fallback. successRate pro Mandant getrackt, Cross-Tenant-Rate auf SystemTemplate.

  • Prioritaet: Phase F (Cross-Tenant Templates)

Domain 6: EVENT SOURCING & AI

XolibEvent (BEHALTEN — gut aufgebaut ✅)

  • Append-only, 52+ Event-Typen
  • Erweiterung: Archivierung nach 12 Monaten in Cold Storage

ChangeLog (siehe Domain 4)

AgentFeedback + AgentOutcome (BEHALTEN + ERWEITERN)

  • Feedback Loops funktionieren
  • Erweiterung AgentOutcome:
  • eurDelta Float? — ROI-Berechnung
  • requiresHumanApproval Boolean @default(false) — Art. 22 DSGVO Pflicht
  • approvedBy String? — userId des genehmigenden Sachbearbeiters
  • approvedAt DateTime?
  • autoExecuted Boolean @default(false) — true = ohne menschliche Freigabe ausgefuehrt
  • modelVersion Int? — welche Modell-Version hat entschieden (A/B Testing)
  • experimentId String? — welches Experiment laeuft
  • Art. 22 Risiko-Klassifizierung:
  • HOCH: Bank-Matching → Mahnung, Mietanpassung → Mieterhoehung, Churn-Score → Kuendigung
  • MITTEL: Ticket-Eskalation → Handwerkerkosten, Xolib Score → externe Kreditentscheidung
  • NIEDRIG: Ticket-Kategorisierung, Uebersetzung, Zusammenfassung

Episodic Memory (NEU — Phase 2)

Model EntityMemory {
  id          String   @id @default(cuid())
  tenantId    String
  entityType  String   // "Property", "Unit", "Tenant", "Owner"
  entityId    String
  memoryType  String   // "EPISODIC", "SEMANTIC", "PROCEDURAL"
  memoryKey   String   // "sanierung_2024", "mieterstreit_2025", "general"
  content     String   // Compressed text summary
  containsPII Boolean  @default(false) // DSGVO: true wenn Mieterdaten enthalten → Loeschanfragen
  embedding   Unsupported("vector(1536)")?
  confidence  Float?
  hitCount    Int      @default(0)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  @@unique([tenantId, entityType, entityId, memoryType, memoryKey])
  @@index([tenantId, entityType])
}
- Zweck: Komprimiertes Wissen pro Entity — KI kennt die Geschichte jedes Objekts/Mieters - Switching Cost: Wer Xolib verlaesst, verliert Jahre an institutionellem Wissen - Prioritaet: Phase C — PARALLEL zu Cross-Tenant (vorgezogen von Phase F, da EntityMemory die Caching-Schicht fuer KI-Kontext ist — ohne sie muessen Agents durch tausende Events scrollen)


Domain 7: TICKETS & KOMMUNIKATION (BEREINIGEN)

Ticket (BEHALTEN — gut aufgebaut ✅)

  • Aenderung: ticketNumber auf @@unique([tenantId, ticketNumber]) umstellen
  • Prioritaet: Beim Nummern-Umbau

Message (LEICHT AENDERN)

  • translationDE Feld entfernen → translatedText + targetLang reicht
  • Prioritaet: Niedrig

Domain 8: BETRIEBSKOSTEN & HEIZKOSTEN (BEHALTEN)

  • OperatingCostSettlement + Item + UnitResult: Gut normalisiert ✅
  • HeatingCostSettlement + UnitResult: Gut, CO2-Allokation korrekt ✅
  • Aenderung: OperatingCostItem.costType String → Enum
  • Prioritaet: Beim BK-Modul V2

Domain 9: HANDWERKER & SERVICE (BEHALTEN)

  • ServiceProvider, ServiceOrder, SchedulingRequest: Gut aufgebaut ✅
  • Aenderung: ServiceProvider.trade String → Enum (aus TicketCategory)
  • Neues Feld: globalProviderId String? — Verknuepfung zu einem globalen Handwerker-Profil (mandantenuebergreifend). Ermoeglicht: "Firma Mueller arbeitet fuer 12 Verwaltungen — Reaktionszeit 2.3 Tage, Folgeticket-Rate 8%"
  • Prioritaet: globalProviderId bei Phase C, trade Enum niedrig

Domain 10: WEG (BEHALTEN)

  • 9 WEG-Models: Sehr gut aufgebaut ✅
  • Hausgelder, Beschluesse, Wirtschaftsplaene, Jahresabrechnungen
  • Keine Aenderungen noetig

Domain 11: GEWERBE (BEHALTEN)

  • 5 Gewerbe-Models: Index, Staffel, Umsatz, Kaution, Nebenkosten
  • Aenderung: GewerbeNebenkostenConfig.includedCosts JSON → normalisieren
  • Prioritaet: Beim Gewerbe-Modul V2

Domain 12: XOLIB SCORE (BEHALTEN — exzellent ✅)

  • Hash-Chaining, Tamper Detection, 5-Kategorien-Modell
  • Erweiterung: benchmarkMedian, benchmarkPercentile auf PropertyScore
  • Prioritaet: Bei Cross-Tenant Phase 1

Domain 13: BANKING (BEHALTEN)

  • BankAccount, BankTransaction: Gut ✅
  • Aenderung: counterpartIban verschluesseln
  • Erweiterung: Payment.matchCorrected fuer Modell-Training
  • Prioritaet: Bei Banking V2

Domain 14: GDPR & COMPLIANCE (ERWEITERN)

DataRetentionPolicy (NEU)

Model DataRetentionPolicy {
  id               String @id @default(cuid())
  entityType       String // "User", "Payment", "Ticket"
  retentionDays    Int    // 2555 (7 Jahre fuer Buchhaltung)
  pseudonymizeAfter Int?  // Tage bis Pseudonymisierung
  deleteAfter      Int?   // Tage bis Loeschung
  legalBasis       String // "§257 HGB", "Art. 17 DSGVO"
  isActive         Boolean @default(true)
}

DataExportRequest (NEU)

Model DataExportRequest {
  id          String   @id @default(cuid())
  tenantId    String
  userId      String
  status      String   // REQUESTED, PROCESSING, READY, EXPIRED
  format      String   // JSON, CSV
  fileUrl     String?
  requestedAt DateTime @default(now())
  completedAt DateTime?
  expiresAt   DateTime?
}

AdminAction (NEU — Super-Admin Audit Trail)

Model AdminAction {
  id             String   @id @default(cuid())
  adminUserId    String
  targetTenantId String?
  actionType     String   // "IMPERSONATE", "DATA_EXPORT", "TENANT_SUSPEND", "CONFIG_CHANGE"
  details        Json?
  ipAddress      String?
  createdAt      DateTime @default(now())

  @@index([adminUserId, createdAt])
  @@index([targetTenantId, createdAt])
}
- Zweck: Protokolliert privilegierte Operationen — getrennt von ChangeLog (Daten) und XolibEvent (Domain) - Compliance: Enterprise-Kunden und Auditoren fragen nicht nur "Haben Sie eine Policy?" sondern "Koennen Sie beweisen?"

DataRetentionExecution (NEU — Enforcement-Nachweis)

Model DataRetentionExecution {
  id                    String   @id @default(cuid())
  policyId              String
  executedAt            DateTime @default(now())
  entityType            String
  recordsScanned        Int
  recordsDeleted        Int
  recordsPseudonymized  Int
  status                String   // "SUCCESS", "PARTIAL", "FAILED"
  errorLog              Json?

  @@index([policyId, executedAt])
}
- Zweck: Beweist dass Retention-Policies durchgesetzt werden — nicht nur definiert

  • Prioritaet: Vor Go-Live (rechtlich noetig)

Domain 15: SAAS & BILLING (BEHALTEN)

  • Plan, Feature, Subscription, Invoice: Funktioniert ✅
  • Aenderung: Plan.features JSON → PlanFeature Junction-Tabelle (spaeter)

Domain 16: LEGACY (ENTFERNEN)

  • ExternalFirm → durch ServiceProvider ersetzt
  • WorkOrder → durch ServiceOrder ersetzt
  • Aktion: Soft-Delete, dann nach 3 Monaten loeschen
  • Prioritaet: Aufraeum-Sprint

Sicherheit — Sofort-Massnahmen

Feld Problem Loesung Prioritaet
Alle tenant-scoped Tables Isolation nur per App-Code PostgreSQL Row-Level Security Policies HOCH
Owner.bankAccountIBAN Unverschluesselt AES-256-GCM (wie BankAccount) HOCH
BankTransaction.counterpartIban Unverschluesselt AES-256-GCM HOCH
User.dateOfBirth Unverschluesselt In UserProfile mit Encryption MITTEL
PropertyImage.data Binary in DB S3/R2 mit URL NIEDRIG (funktioniert)
Rate Limiting Kein Echtzeit-Enforcement Redis/in-memory, NICHT ueber DB-Queries MITTEL

Fehlende Indexes

Query Pattern Index Prioritaet
Unbezahlte Mieten Payment [leaseId, status, dueDate] HOCH
Offene Tickets nach Kategorie Ticket [tenantId, category, status] HOCH
Units mit Mieter Unit [propertyId, currentTenantId] MITTEL
Ablaufende Dokumente PropertyDocument [tenantId, expiresAt] MITTEL
ChangeLog Abfragen ChangeLog [tenantId, entityType, entityId] HOCH (neu)

Migrations-Reihenfolge

Phase A: Historisierung (JETZT — vor allem anderen)

  1. ChangeLog Model erstellen
  2. API-Middleware: Automatisches Logging bei jedem Update
  3. Ab sofort wird jede Aenderung getrackt

Phase B: Properties Domain (JETZT — V6 Sprint)

  1. PropertyBuilding, PropertyEnergy, PropertyFinance Models
  2. UnitEquipment, UnitAccessibility, UnitRentalTerms Models
  3. Daten migrieren (additive INSERTs)
  4. API umstellen (JOINs, flache Response)
  5. Unit Deprecated Fields entfernen (Schritt 4)

Phase C: Cross-Tenant + EntityMemory (naechster Sprint)

  1. SystemBenchmark + SystemAggregationJob
  2. EntityMemory (Episodic, Semantic, Procedural) — PARALLEL, nicht danach
  3. Naechtliche SQL-Aggregation (5 Metriken)
  4. API: GET /api/v1/benchmarks/:metric
  5. KPI-Panels zeigen "Ihr Wert vs. Markt"

Phase D: Sicherheit (vor Go-Live)

  1. IBAN-Verschluesselung (Owner, BankTransaction)
  2. DataRetentionPolicy + DataExportRequest
  3. Fehlende Indexes
  4. Legacy Models entfernen
  5. Differential Privacy auf SystemBenchmark (Laplace-Noise)

Phase E: User Domain (bei Personen V6)

  1. User → User + UserProfile + UserPreferences splitten
  2. PII verschluesseln

Phase F: Erweiterte Intelligence

  1. SystemModelParam (gelernte Parameter) + A/B Testing
  2. SystemTemplate (Template-Performance)
  3. RentAdjustment Model
  4. BenchmarkAccess Model (API-Zugang fuer externe Partner — Daten-Monetarisierung)

Enums — Fehlende Definitionen

Diese String-Felder sollten Enums werden:

Feld Werte Prioritaet
ChangeLog.source (ChangeSource) API, IMPORT, AGENT, MIGRATION, WEBHOOK, SYSTEM, MANUAL SOFORT (Phase A)
ChangeLog.entityType (ChangeEntityType) PROPERTY, UNIT, LEASE, PAYMENT, OWNER, TICKET, USER, SERVICE_ORDER SOFORT (Phase A)
Property.propertyType WOHNUNG, HAUS, GEWERBE, GRUNDSTUECK JETZT
Property.houseType EFH, RH, DHH, MFH, VILLA, BUNGALOW JETZT
Property.objectCondition ERSTBEZUG, GEPFLEGT, SANIERT, RENOVIERUNGSBEDUERFTIG JETZT
Property.energyCarrier GAS, OEL, FERNWAERME, STROM, PELLETS, SOLAR JETZT
Unit.furnishing FURNISHED, PARTIALLY, UNFURNISHED JETZT
Unit.petsAllowed YES, NO, BY_ARRANGEMENT JETZT
PropertyComponent.category DACH, FASSADE, FENSTER, HEIZUNG, AUFZUG, etc. MITTEL
OperatingCostItem.costType WASSER, ABWASSER, MUELL, STROM, etc. MITTEL
ServiceProvider.trade HEIZUNG, SANITAER, ELEKTRIK, etc. NIEDRIG

Data Moat Checkliste

Jedes neue Feature muss diese Fragen beantworten:

  1. Welche Daten sammelt es? (Events, Stammdaten, Transaktionen)
  2. Wie fuettert es die KI? (Feedback, Outcomes, Training Data)
  3. Was lernt das System daraus? (Patterns, Benchmarks, Predictions)
  4. Was ist der Switching Cost? (Historische Daten die nur bei uns existieren)
  5. Welche Xolib Score Kategorie beeinflusst es? (Substanz, Technologie, Ertrag, Compliance, Instandhaltung)
  6. Ist es Cross-Tenant aggregierbar? (k-Anonymitaet + Differential Privacy, DSGVO-konform)
  7. Bindet es den Mandanten tiefer in einen operativen Workflow ein? (Zahlungsfluss, Compliance, Dokumentation — Workflow-Moat)
  8. Erzeugt es Daten oder Events die ueber Webhooks/API fuer Drittanbieter wertvoll sind? (Platform-Moat)

Feature Store Konzept (Architekturentscheidung)

Die Architektur definiert wo Daten gespeichert werden — aber auch wie sie in trainierbare Features transformiert werden. Ohne eine Zwischenschicht muss jeder Agent seine eigene Datenaufbereitung machen.

Konzept: Ein Feature Store produziert aus ChangeLog + XolibEvent + Stammdaten aggregierte, normalisierte Features:

Roh-Daten (ChangeLog, XolibEvent, Lease, Payment, ...)
    ↓ Naechtliche Aggregation (SystemAggregationJob)
Feature Store (EntityMemory + SystemBenchmark + SystemModelParam)
    ↓ Agents konsumieren normalisierte Features
KI-Entscheidungen (AgentRun → AgentOutcome → Feedback)
    ↓ Feedback fliesst zurueck
Bessere Features + Modelle

Kein eigenes Model noetig — der Feature Store ist die Kombination aus EntityMemory (pro Entity), SystemBenchmark (Cross-Tenant), und SystemModelParam (gelernte Gewichte). Die Aggregation laeuft ueber SystemAggregationJob.


A/B Testing fuer AI-Modelle

SystemModelParam bekommt zwei zusaetzliche Felder:

experimentId   String?  // "bank_matching_v2_test"
isControl      Boolean  @default(false) // Control vs. Variant

AgentOutcome bekommt:

modelVersion   Int?     // Welche Version des Modells hat entschieden?
experimentId   String?  // Welches Experiment laeuft?

So koennen zwei Modellversionen parallel laufen und gemessen werden.


Cross-Tenant Privacy (erweitert)

k-Anonymitaet (k≥5) ist notwendig aber nicht hinreichend. Zusaetzlich:

  1. Differential Privacy — Laplace-Noise auf Aggregatwerte (Phase D)
  2. l-Diversity — Mindestens l verschiedene Werte pro sensitives Attribut in jeder Kohorte
  3. Dynamisches Kohorten-Merging — Wenn eine Kohorte (z.B. "Villen in Flensburg, GROSS") k<5 hat, wird sie in die naechsthoehere Kohorte gemergt ("Villen in Schleswig-Holstein")
  4. Benchmarks sind IMMER retrospektiv (min. 1 Quartal alt) und deskriptiv (nie preskriptiv) — Kartellrecht (RealPage-Praezedenz)

DSGVO-Compliance (Architekturentscheidungen)

Verarbeitungsverzeichnis (Art. 30 DSGVO)

Verarbeitungstaetigkeit Rechtsgrundlage Betroffene Loeschfrist
Mieterverwaltung (User, Lease, Payment) Art. 6 (1) b — Vertragserfuellung Mieter 10 Jahre nach Vertragsende (§257 HGB)
Aenderungsprotokollierung (ChangeLog) Art. 6 (1) f — berechtigtes Interesse Mieter, Eigentuemer Wie Stammdaten
KI-Optimierung (EntityMemory, AgentFeedback) Art. 6 (1) f — berechtigtes Interesse Mieter (indirekt) Bei Mandanten-Loeschung
Cross-Tenant Benchmarks (SystemBenchmark) Nicht anwendbar (anonymisiert) Keine Unbegrenzt
Banking (BankTransaction, Payment) Art. 6 (1) b + c — Vertrag + gesetzlich Mieter, Eigentuemer 10 Jahre (§257 HGB)

AVV Cross-Tenant-Klausel (Pflicht im Auftragsverarbeitungsvertrag)

"Anonymisierte, aggregierte Daten werden mandantenuebergreifend fuer Benchmarks und KI-Optimierung genutzt. Die Anonymisierung erfolgt durch k-Anonymitaet (k≥5), Differential Privacy und dynamisches Kohorten-Merging. Eine Re-Identifizierung einzelner Mandanten oder Personen ist ausgeschlossen."

Interessenabwaegung fuer KI-Features (Art. 6 (1) f)

Muss dokumentiert werden fuer: EntityMemory, EntityInsight, AgentFeedback, ChangeLog wo containsPII=true. Die Abwaegung, einschliesslich Argumentation und Ergebnis, muss schriftlich dokumentiert werden (Art. 5 Abs. 2 DSGVO Rechenschaftspflicht).

EU Data Act (seit 12.09.2025 verbindlich)

  • Mandanten muessen ihre Daten exportieren und zu einem Wettbewerber mitnehmen koennen
  • Das DataExportRequest-Model ist ein Anfang, aber es muss ein Self-Service-Datenexport im Produkt existieren — nicht nur auf Anfrage
  • Gesetzliche Pflicht seit September 2025 — muss vor Go-Live umgesetzt sein

Datenschutz-Folgenabschaetzung (DPIA — Art. 35, PFLICHT vor Go-Live)

Model DataProtectionImpactAssessment {
  id               String   @id @default(cuid())
  processingName   String   // "Cross-Tenant Benchmarking", "AI Bank Matching"
  description      String
  necessity        String   // Warum ist die Verarbeitung erforderlich?
  risks            Json     // [{risk: "Re-Identifizierung", likelihood: "LOW", impact: "HIGH"}]
  mitigations      Json     // [{measure: "k-Anonymitaet k≥5", status: "IMPLEMENTED"}]
  residualRisk     String   // "LOW", "MEDIUM", "HIGH"
  approvedBy       String?  // Datenschutzbeauftragter
  approvedAt       DateTime?
  reviewDate       DateTime // Naechste Ueberpruefung
  createdAt        DateTime @default(now())
  updatedAt        DateTime @updatedAt
}
Pflicht weil: systematische Bewertung von Mietern (Zahlung, Tickets, Churn), umfangreiche Verarbeitung (10.000+ Mandanten), neue Technologien (8 KI-Agents). Mindestens 3 DPIAs noetig: Cross-Tenant Intelligence, AI Bank Matching, Xolib Score.

EU AI Act — Risiko-Klassifizierung

AI-Feature Risiko-Klasse Anforderung Status
Ticket-KI (Kategorisierung, Routing) Minimal Transparenzpflicht ✅ Machbar
Bank-Matching-KI Begrenzt Transparenz + Logging ⚠️ AgentOutcome reicht
Mietanpassungs-Agent Begrenzt/Hoch Human-in-the-Loop + Logging ⚠️ requiresHumanApproval noetig
Xolib Score (extern nutzbar) Potenziell Hoch Konformitaetsbewertung, Audit ❌ Nicht adressiert
Cross-Tenant Benchmarks Minimal Keine besonderen Anforderungen

OpenAI als Subprocessor

  • OpenAI muss als Subprocessor im AVV gelistet sein
  • PII-Stripping-Middleware PFLICHT (Prinzip 14): Mieternamen → Pseudonyme, IBANs → maskiert
  • Zuordnung (Pseudonym → Realdaten) bleibt lokal bei Xolib
  • Bestehende Implementierung: pii-mask.ts — muss auf Vollstaendigkeit geprueft werden

DataExportRequest (erweitert fuer Art. 20 + EU Data Act)

Neue Felder:

exportScope    String   // "TENANT_FULL", "TENANT_FINANCIAL", "USER_PERSONAL", "PROPERTY_SINGLE"
includeHistory Boolean  @default(false) // Inkl. ChangeLog-Eintraege?
includeAI      Boolean  @default(false) // Inkl. EntityMemory, Insights?
- Art. 20 DSGVO: Nur Daten die der Betroffene selbst bereitgestellt hat (Vertrag, Zahlungen, Tickets) - EU Data Act: Breiter — alle Mandantendaten, historisch, in importierbarem Format - EntityMemory + SystemModelParam sind proprietaer und muessen NICHT exportiert werden

DSGVO-Risikomatrix

Architektur-Element Risiko Status Massnahme
ChangeLog Mittel ⚠️ containsPII + DataRetentionPolicy koppeln
SystemBenchmark Niedrig Anonymisierung wie geplant
EntityMemory Hoch ⚠️ containsPII + Interessenabwaegung dokumentieren
EntityInsight Mittel ⚠️ containsPII + Loeschkonzept
H3 Spatial Kein Risiko Keine Massnahmen noetig
Webhooks/API Mittel ⚠️ Subprocessor-Dokumentation, IntegrationLog Retention
OpenImmo Niedrig Kontaktdaten im Export beachten
External Data Kein Risiko Alles oeffentliche Daten
Datenexport (EU Data Act) Hoch Self-Service-Export vor Go-Live
Art. 22 Human-in-the-Loop Kritisch requiresHumanApproval auf AgentOutcome
EU AI Act Xolib Score Hoch Konformitaetsbewertung wenn extern nutzbar
DPIA (Art. 35) Pflicht 3 DPIAs vor Go-Live
OpenAI Subprocessor Hoch ⚠️ PII-Stripping (pii-mask.ts) pruefen + AVV

API-Versionierung (Architekturentscheidung)

  • URL-basierte Versionierung: /api/v1/, /api/v2/
  • ApiClient bekommt: apiVersion String @default("v1")
  • Deprecation-Policy: Alte API-Version mindestens 12 Monate nach Release der neuen verfuegbar
  • IntegrationLog trackt Version ueber den Endpoint-Pfad

Observability (Architekturentscheidung)

Monitoring-Daten gehoeren NICHT in die Anwendungs-DB — sie werden in spezialisierten Systemen gespeichert:

  • Agent-Performance (Latenz, Fehlerrate, Feedback-Score) → AgentOutcome + Dashboard-View
  • System-Metriken (API-Latenz, DB-Query-Time, Queue-Depth) → Extern (Sentry, Grafana)
  • Tenant-Nutzungsmetriken (Logins/Tag, API-Calls/Monat, Features used) → Aggregation auf IntegrationLog oder leichtgewichtiges TenantUsageMetric
  • Alerting: Wenn Agents langsamer werden oder haeufiger Fehler machen → sofort sichtbar

Zusammenfassung

Bereich Status Handlungsbedarf
Tenant/User 6/10 User splitten, PII verschluesseln
Property/Unit 5/10 Property splitten, Unit bereinigen, Equipment normalisieren
Lease/Payment 8/10 RentAdjustment fehlt, matchCorrected fehlt
Historisierung 0/10 ChangeLog SOFORT bauen
Cross-Tenant 3/10 Event Sourcing da, Benchmarks fehlen
Tickets/Komm. 8/10 Nummern-Umbau, Translation bereinigen
BK/HK 9/10 costType Enum
Handwerker 8/10 trade Enum
WEG 9/10 Keine Aenderungen
Gewerbe 8/10 Nebenkosten normalisieren
Score 10/10 Benchmark-Extension
Banking 7/10 Verschluesselung, matchCorrected
GDPR 5/10 RetentionPolicy, ExportRequest
SaaS/Billing 7/10 Plan.features normalisieren
AI/Events 9/10 EntityMemory (Phase C)
Spatial Intelligence 0/10 H3 auf Property (2 Felder, Phase B+)
Ecosystem/Integrations 0/10 ApiClient, Webhooks (Phase D+)
OpenImmo 0/10 Mapping-Model + ImportJob (Phase C+)
External Data 0/10 ExternalDataSource (Phase F+)
Entity Insights 0/10 EntityInsight (Phase F+)

Gesamtbewertung aktuell: 7/10 Zielbewertung nach Umbau: 9.5/10


Domain 17: SPATIAL INTELLIGENCE (NEU)

H3 Geospatial Index auf Property

Zwei Felder auf Property — kostet fast nichts, ermoeglicht alles Spaetere:

h3IndexRes8    String?   // "881f1d4883fffff" — Stadtteil (~460m)
h3IndexRes9    String?   // "891f1d4883bffff" — Block (~175m)

@@index([h3IndexRes8])
@@index([h3IndexRes9])

Ermoeglicht: - Nachbarschafts-Benchmarks ohne teure Spatial Joins - "Was kosten Betriebskosten im gleichen Block?" in Millisekunden - Cross-Tenant-Aggregation auf raeumlicher Ebene (neues cohortH3Res8 auf SystemBenchmark) - Spaeter: Heatmaps fuer Mietpreisentwicklung, Leerstandsprognose, Sanierungswellen - Prioritaet: MIT Phase B (2 Felder hinzufuegen kostet nichts)


Domain 18: ECOSYSTEM & INTEGRATIONS (NEU — Platform-Architektur)

ApiClient (NEU)

Model ApiClient {
  id             String   @id @default(cuid())
  tenantId       String
  name           String   // "DATEV Export", "ImmoScout Sync"
  clientId       String   @unique
  clientSecret   String   // encrypted
  scopes         String[] // ["properties:read", "payments:read"]
  webhookUrl     String?
  isActive       Boolean  @default(true)
  rateLimitTier  String   // "STANDARD", "PREMIUM", "PARTNER"
  createdAt      DateTime @default(now())
}

WebhookSubscription (NEU)

Model WebhookSubscription {
  id              String    @id @default(cuid())
  tenantId        String
  apiClientId     String
  eventType       String    // "property.updated", "payment.received"
  targetUrl       String
  secret          String    // HMAC signature
  isActive        Boolean   @default(true)
  failCount       Int       @default(0)
  maxRetries      Int       @default(5)
  retryBackoffMs  Int       @default(60000) // Exponential backoff
  lastTriggeredAt DateTime?
  lastFailedAt    DateTime?
  lastErrorCode   Int?
  isSuspended     Boolean   @default(false) // Auto-suspend nach maxRetries
}

WebhookDeliveryLog (NEU — Dead Letter Queue)

Model WebhookDeliveryLog {
  id               String   @id @default(cuid())
  subscriptionId   String
  eventType        String
  payload          Json
  attempt          Int      @default(1)
  statusCode       Int?
  responseTimeMs   Int?
  success          Boolean
  createdAt        DateTime @default(now())

  @@index([subscriptionId, createdAt])
}

IntegrationLog (NEU)

Model IntegrationLog {
  id             String   @id @default(cuid())
  tenantId       String
  apiClientId    String
  endpoint       String
  method         String
  statusCode     Int
  latencyMs      Int
  createdAt      DateTime @default(now())

  @@index([tenantId, apiClientId, createdAt])
}
  • Warum Moat: Sobald DATEV, ImmoScout24, Bank und Handwerkerportal ueber Webhooks synchronisiert sind, wird Xolib zum Nervensystem des Betriebs
  • Prioritaet: MIT Phase D (Grundstein fuer Oekosystem)

Domain 19: OPENIMMO (NEU — Daten-Ingestion)

OpenImmoMapping (NEU)

Model OpenImmoMapping {
  id                String   @id @default(cuid())
  tenantId          String
  xolibEntityType   String   // "Property", "Unit", "Lease"
  xolibField        String   // "sqm", "baseRent", "heatingType"
  openImmoPath      String   // "flaechen.wohnflaeche", "preise.nettokaltmiete"
  transformRule     String?  // "DIRECT", "ENUM_MAP", "CUSTOM"
  customMapping     Json?

  @@unique([tenantId, xolibEntityType, xolibField])
}
  • Zweck: Mandantenspezifische Feldmappings — verschiedene Portale erwarten verschiedene OpenImmo-Felder
  • Switching Cost: Jedes konfigurierte Mapping ist ein weiterer Wechselkostenpunkt
  • Prioritaet: MIT Phase C (Onboarding-Turbo, Daten-Ingestion)

ImportJob (NEU — Onboarding Massen-Import)

Model ImportJob {
  id            String    @id @default(cuid())
  tenantId      String
  sourceSystem  String    // "DOMUS", "AAREON", "EXCEL", "OPENIMMO", "CSV"
  status        String    // "UPLOADED", "VALIDATING", "IMPORTING", "DONE", "FAILED"
  totalRows     Int?
  importedRows  Int?      @default(0)
  errorRows     Int?      @default(0)
  errorLog      Json?     // [{row: 42, field: "zip", error: "invalid"}]
  fileUrl       String?
  startedAt     DateTime?
  completedAt   DateTime?
  createdAt     DateTime  @default(now())

  @@index([tenantId, status])
}
  • Zweck: Massen-Import beim Wechsel von DOMUS/Aareon/Excel — kritischster Moment im Kundenlebenszyklus
  • ChangeLog-Integration: Jeder Import-Datensatz bekommt source: "IMPORT" — KI weiss welche Daten importiert vs. organisch entstanden sind
  • Prioritaet: MIT Phase C+

Domain 20: EXTERNAL DATA ENRICHMENT (NEU)

ExternalDataSource (NEU)

Model ExternalDataSource {
  id            String    @id @default(cuid())
  sourceKey     String    @unique  // "boris_bodenrichtwert", "mietspiegel_berlin"
  sourceType    String    // "API", "SCRAPE", "MANUAL", "OPENDATA"
  baseUrl       String?
  lastSyncAt    DateTime?
  syncInterval  String?   // "DAILY", "WEEKLY", "QUARTERLY", "YEARLY"
  dataVersion   String?
  isActive      Boolean   @default(true)
}

ExternalDataPoint (NEU)

Model ExternalDataPoint {
  id            String   @id @default(cuid())
  sourceId      String
  h3IndexRes8   String   // Spatial Reference — verbindet externe Daten mit Properties
  dataKey       String   // "bodenrichtwert_eur_sqm", "laerm_db_tag"
  value         String   // JSON stringified
  validFrom     DateTime
  validUntil    DateTime?
  confidence    Float?    @default(1.0) // 0.0-1.0 — API_LIVE=1.0, SCRAPED=0.7, MANUAL=0.5
  sourceMethod  String?   // "API_LIVE", "API_CACHED", "MANUAL", "SCRAPED"
  createdAt     DateTime @default(now())

  @@index([h3IndexRes8, dataKey])
  @@index([sourceId, dataKey])
}

Oeffentliche Datenquellen: - Bodenrichtwerte (BORIS-D, Gutachterausschuesse) - Mietspiegel (kommunale Veroeffentlichungen) - Grundsteuerdaten (BORIS) - Energieausweise (DENA-Datenbank) - Demografische Daten (Destatis, Zensus) - OEPNV-Anbindung (GTFS-Feeds) - Laermkarten, Hochwasserrisiko (Umweltbundesamt)

  • Warum H3 hier kritisch ist: h3IndexRes8 verbindet externe Daten mit internen Properties. Bodenrichtwerte, Mietspiegel und Laermkarten koennen alle auf H3-Zellen gemappt und mit Property-Daten geJOINed werden
  • Prioritaet: Phase F erweitern

Domain 21: ENTITY INSIGHTS (NEU — Relationale KI-Erkenntnisse)

EntityInsight (NEU)

Model EntityInsight {
  id             String   @id @default(cuid())
  tenantId       String?  // NULL = Cross-Tenant Insight
  sourceType     String   // "ServiceProvider"
  sourceId       String?
  targetType     String   // "Ticket"
  targetId       String?
  insightType    String   // "CORRELATION", "ANOMALY", "PREDICTION", "RECOMMENDATION"
  insightKey     String   // "follow_up_ticket_rate"
  value          Json     // { rate: 0.4, benchmark: 0.15, confidence: 0.87 }
  containsPII    Boolean  @default(false) // DSGVO: true wenn personenbezogen
  isActive       Boolean  @default(true)
  hitCount       Int      @default(0)
  createdAt      DateTime @default(now())
  updatedAt      DateTime @updatedAt

  @@index([sourceType, insightKey])
  @@index([tenantId, insightType])
}
  • Zweck: Bruecke zwischen EntityMemory (pro Entity) und SystemBenchmark (Aggregat) — speichert relationale Insights die keine der beiden abdeckt
  • Beispiel: "Handwerkerfirma X: 40% hoehere Folgetickets als Firma Y" — existiert implizit in den Daten, aber kein Model speichert solche Cross-Entity-Erkenntnisse
  • Prioritaet: Phase F erweitern

Xolib Flywheel (Architekturprinzip)

Mandant nutzt Xolib fuer Verwaltung
    ↓
Daten fliessen in ChangeLog + XolibEvent
    ↓
EntityMemory komprimiert Kontext pro Objekt/Mieter
    ↓
Cross-Tenant Benchmarks + External Data Enrichment
    ↓
KI wird besser (Matching, Scoring, Predictions)
    ↓
Mandant sieht "Ihr Wert vs. Markt" + KI-Empfehlungen
    ↓
Mandant bindet DATEV/ImmoScout/Bank ueber API/Webhooks
    ↓
Wechselkosten steigen, Retention steigt
    ↓
Mehr Mandanten → mehr Cross-Tenant Intelligence
    ↓
Netzwerkeffekt: Jeder neue Mandant macht das System schlauer
    ↓
Branchenstandard: Drittanbieter bauen auf Xolib-API

Erweiterte Migrations-Reihenfolge

Phase Was Wann
A ChangeLog (Historisierung) SOFORT
B Property/Unit Split JETZT
B+ H3 Index auf Property (2 Felder) MIT Phase B
C Cross-Tenant + EntityMemory Naechster Sprint
C+ OpenImmo Import/Export MIT Phase C
D Sicherheit + DSGVO Vor Go-Live
D+ ApiClient + Webhooks (Oekosystem-Grundstein) MIT Phase D
E User Split Bei Personen V6
F Extended Intelligence Nach Go-Live
F+ ExternalDataSource + EntityInsight Phase F erweitern

Gesamtbewertung aktuell: 7/10 Zielbewertung nach Umbau: 9.5/10