wiki/mandates/pek/datenmodell/README.md
ValueOn AG 15f0e51bd0 pek
2025-10-24 21:42:47 +02:00

409 lines
9.6 KiB
Markdown

# Architektur-Planungs-App - Datenmodell
## Übersicht
Dieses Repository enthält das vollständige Datenmodell für eine Schweizer Architektur-Planungs-Applikation zur Verwaltung von Bauprojekten, Parzellen, Dokumenten und regulatorischen Informationen.
## 📁 Dateien
### 1. **DATENMODELL_DOKUMENTATION.md**
Umfassende Dokumentation mit:
- Detaillierte Beschreibung aller Entitäten
- Beziehungsdiagramme
- Implementierungshinweise
- Use Cases
- Best Practices
- Offene Fragen
**Empfohlene Lesereihenfolge: Zuerst diese Datei lesen!**
### 2. **datenmodell.mermaid**
Visuelles ER-Diagramm zur Darstellung der Entitäten und Beziehungen.
**Verwendung:**
```bash
# In Visual Studio Code mit Mermaid Extension
# Oder online: https://mermaid.live/
# Datei öffnen und als Diagramm anzeigen
```
### 3. **datenmodell-schema.json**
JSON Schema Definition im JSON Schema Draft-07 Format.
**Verwendung:**
- API-Dokumentation mit Swagger/OpenAPI
- Validierung von JSON-Payloads
- Code-Generierung für verschiedene Sprachen
```bash
# JSON Schema validieren
npm install -g ajv-cli
ajv validate -s datenmodell-schema.json -d beispiel-daten.json
```
### 4. **models.py**
Python SQLAlchemy Implementation mit PostGIS-Unterstützung.
**Verwendung:**
```bash
# Installation
pip install sqlalchemy geoalchemy2 psycopg2-binary --break-system-packages
# Datenbank erstellen
python models.py
# In eigener Anwendung verwenden
from models import Projekt, Parzelle, Dokument
```
**Tech Stack:**
- Python 3.10+
- SQLAlchemy 2.0+
- PostgreSQL 15+ mit PostGIS 3.4+
- GeoAlchemy2
### 5. **schema.prisma**
Prisma Schema für TypeScript/JavaScript Backend.
**Verwendung:**
```bash
# Installation
npm install prisma @prisma/client
# Datenbank migrieren
npx prisma migrate dev --name init
# Prisma Client generieren
npx prisma generate
# Prisma Studio öffnen
npx prisma studio
```
**Tech Stack:**
- Node.js 18+
- Prisma 5+
- PostgreSQL 15+ mit PostGIS
### 6. **migration_001_initial_schema.sql**
SQL-Migrationsskript für direkte PostgreSQL-Verwendung.
**Verwendung:**
```bash
# PostgreSQL Datenbank erstellen
createdb architektur_app
# Migration ausführen
psql -d architektur_app -f migration_001_initial_schema.sql
# Verbinden und testen
psql architektur_app
\dt # Tabellen anzeigen
```
## 🚀 Quick Start
### Option 1: Python mit SQLAlchemy
```bash
# 1. PostgreSQL mit PostGIS aufsetzen
docker run --name postgis -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgis/postgis:15-3.4
# 2. Dependencies installieren
pip install sqlalchemy geoalchemy2 psycopg2-binary --break-system-packages
# 3. Models verwenden
python models.py
```
### Option 2: TypeScript mit Prisma
```bash
# 1. Projekt initialisieren
npm init -y
npm install prisma @prisma/client
# 2. Prisma konfigurieren
cp schema.prisma prisma/schema.prisma
# 3. Database URL setzen
echo "DATABASE_URL=\"postgresql://user:password@localhost:5432/architektur_app\"" > .env
# 4. Migration ausführen
npx prisma migrate dev --name init
```
### Option 3: Direkt mit SQL
```bash
# 1. Datenbank erstellen
createdb architektur_app
# 2. Migration ausführen
psql -d architektur_app -f migration_001_initial_schema.sql
# 3. Daten einfügen und abfragen
psql architektur_app
```
## 📊 Datenmodell-Struktur
### Kern-Entitäten
```
PROJEKT (Bauprojekt)
├── Perimeter (Parzellen)
├── Dokumente (Bauherrschaft & Planung)
├── Baulinie (geografisch)
└── Kontext-Informationen
PARZELLE (Grundstück)
├── Geografischer Kontext (Land, Kanton, Gemeinde)
├── Bauliche Parameter (AZ, BZ, Vollgeschosse, etc.)
├── Schutzzonen
├── Nachbarparzellen
└── Dokumente & Kontext
DOKUMENT (Datei oder URL)
├── Versionierung
├── Tags
└── Format
Administrative Hierarchie:
LAND → KANTON → GEMEINDE
```
### Geografische Daten
Das Modell verwendet das **Schweizer Landessystem LV95 (EPSG:2056)**:
- Ostwert (X): 2'480'000 - 2'840'000
- Nordwert (Y): 1'070'000 - 1'300'000
- Höhe (Z): Meter über Meer
## 🗺️ Koordinatensystem
Alle geografischen Daten verwenden **LV95 (Swiss LV95 / EPSG:2056)**.
**Beispiel-Koordinaten (Zürich Hauptbahnhof):**
```
X (Ost): 2'683'140
Y (Nord): 1'247'850
Z (Höhe): 408 m ü. M.
```
## 🔍 Wichtige Entscheidungen
### 1. Polymorphe Beziehungen
Das `Kontext`-Objekt kann zu verschiedenen Entitäten gehören (Projekt, Parzelle, Land, Kanton, Gemeinde). Dies ermöglicht flexible Erweiterungen ohne Schema-Änderungen.
### 2. Array-Felder
- `statusProzess`: Ein Projekt kann mehrere Status gleichzeitig haben
- `tags`: Dokumente können mehrere Tags haben
- `parzellenNummern`: Parzellen können mehrere offizielle Nummern haben
### 3. Selbstreferenzierende Beziehungen
Parzellen referenzieren sich gegenseitig als Nachbarn (n:m Beziehung).
### 4. Versionierung
Dokumente haben eine `versionsbezeichnung` für manuelle Versionskontrolle.
### 5. Drei-wertiger Zustand
`JaNein` Enum erlaubt "", "Ja", "Nein" für unbekannte Zustände.
## 📋 Anwendungsfälle
### UC1: Neues Projekt erstellen
```python
# Python Beispiel
projekt = Projekt(
label="Neubau Mehrfamilienhaus",
status_prozess=[StatusProzess.EINGANG]
)
projekt.perimeter.append(parzelle_1)
session.add(projekt)
session.commit()
```
### UC2: Bebaubarkeit prüfen
```sql
-- SQL Beispiel
SELECT
p.label,
p.bauzone,
p.az,
p.bz,
p.vollgeschoss_zahl,
p.gebaeudehoehe_max,
ST_Area(p.geo_umfang) as flaeche_m2
FROM v_parzelle_vollstaendig p
WHERE p.id = 'UUID';
```
### UC3: Nachbaranalyse
```typescript
// TypeScript/Prisma Beispiel
const parzelle = await prisma.parzelle.findUnique({
where: { id: parzelleId },
include: {
nachbarEigentuemer_von: {
include: { nachbar: true }
}
}
});
```
## 🎯 Best Practices
### Geometrie-Handling
```python
# PostGIS: Parzelle mit Polygon erstellen
from geoalchemy2.shape import from_shape
from shapely.geometry import Polygon
polygon = Polygon([
(2683140, 1247850),
(2683200, 1247850),
(2683200, 1247900),
(2683140, 1247900),
(2683140, 1247850)
])
parzelle.geo_umfang = from_shape(polygon, srid=2056)
```
### Kontext-Informationen
```python
# Flexibles Hinzufügen von Kontextinformationen
kontext = Kontext(
thema="Dienstbarkeiten",
inhalt="Wegrecht zugunsten Parzelle 1235 entlang Ostgrenze, eingetragen am 15.03.2020",
parzelle_id=parzelle.id
)
```
### Dokumenten-Management
```python
# Dokument mit Tags
dokument = Dokument(
label="Zonenplan Gemeinde Zürich",
versionsbezeichnung="2024-v1",
typ=DokumentTyp.DATEI,
format="PDF",
dokument_referenz="/storage/docs/zonenplan-zh-2024.pdf",
tags=[TagTyp.ZONENPLAN, TagTyp.BZO]
)
```
## 🔐 Sicherheit
### Zu beachten:
- Eigentümerdaten sind personenbezogene Daten (DSGVO/DSG)
- Dokumente benötigen Zugriffskontrolle
- Audit-Logging für Änderungen empfohlen
- Geometriedaten sollten validiert werden
### Empfohlene Maßnahmen:
```sql
-- Audit-Log Tabelle hinzufügen
CREATE TABLE audit_log (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
table_name VARCHAR(100) NOT NULL,
record_id UUID NOT NULL,
action VARCHAR(20) NOT NULL,
changed_by UUID NOT NULL,
changed_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
old_values JSONB,
new_values JSONB
);
```
## 📈 Performance-Optimierung
### Indices sind bereits erstellt für:
- Geografische Suchen (GIST Index auf Geometrien)
- Array-Suchen (GIN Index auf Arrays)
- Foreign Key Joins
### Zusätzliche Optimierungen:
```sql
-- Materialized View für häufige Reports
CREATE MATERIALIZED VIEW mv_projekt_statistik AS
SELECT
k.label as kanton,
COUNT(DISTINCT pr.id) as anzahl_projekte,
COUNT(DISTINCT pa.id) as anzahl_parzellen,
SUM(ST_Area(pa.geo_umfang)) as gesamtflaeche_m2
FROM projekt pr
JOIN projekt_parzelle pp ON pr.id = pp.projekt_id
JOIN parzelle pa ON pp.parzelle_id = pa.id
JOIN kanton k ON pa.kanton_id = k.id
GROUP BY k.label;
-- Refresh periodisch
REFRESH MATERIALIZED VIEW mv_projekt_statistik;
```
## 🧪 Testing
### Unit Tests
```python
# pytest Beispiel
def test_parzelle_creation():
parzelle = Parzelle(
label="Test Parzelle",
parzellen_nummern=["1234"],
az=1.5,
bz=0.4
)
assert parzelle.label == "Test Parzelle"
assert parzelle.az == 1.5
```
### Integration Tests
```typescript
// Jest Beispiel
describe('Projekt API', () => {
it('should create projekt with parzellen', async () => {
const projekt = await createProjekt({
label: 'Test Projekt',
perimeter: [parzelle1.id, parzelle2.id]
});
expect(projekt.perimeter).toHaveLength(2);
});
});
```
## 📚 Weitere Ressourcen
- [PostGIS Dokumentation](https://postgis.net/docs/)
- [SQLAlchemy ORM](https://docs.sqlalchemy.org/)
- [Prisma Dokumentation](https://www.prisma.io/docs/)
- [Swisstopo - Schweizer Koordinatensysteme](https://www.swisstopo.admin.ch/de/wissen-fakten/geodaesie-vermessung/bezugsrahmen/lokal/lv95.html)
## 🤝 Nächste Schritte
1. **Validierung**: Review mit Architekten und Fachexperten
2. **API-Design**: RESTful oder GraphQL API implementieren
3. **Frontend-Prototyp**: Kartenansicht mit Leaflet/MapLibre
4. **GIS-Integration**: Anbindung an Swisstopo-APIs
5. **Workflow-Engine**: Statusübergänge und Genehmigungen
6. **Benutzer-Management**: Rollen und Berechtigungen
## 📞 Support
Bei Fragen zum Datenmodell:
- Öffne ein Issue im Repository
- Konsultiere die `DATENMODELL_DOKUMENTATION.md`
- Prüfe die Beispiel-Implementierungen in `models.py` oder `schema.prisma`
## 📝 License
[Lizenz hier einfügen]
---
**Version:** 1.0
**Letzte Aktualisierung:** 2025-10-24
**Koordinatensystem:** LV95 (EPSG:2056)
**Datenbank:** PostgreSQL 15+ mit PostGIS 3.4+