Files
aza/AzA march 2026 - Kopie (4)/workforce_planner/ARCHITECTURE.md
2026-03-30 07:59:11 +02:00

298 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Workforce Planner Zielarchitektur
## 1. Architekturdiagramm
```
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTS │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Desktop App │ │ Web App │ │ MedWork Plugin │ │
│ │ (tkinter) │ │(React/Vue/…) │ │ (Integration) │ │
│ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │
│ │ │ │ │
│ └──────────────────┼─────────────────────┘ │
│ │ │
│ HTTPS / REST API │
└────────────────────────────┼────────────────────────────────────┘
┌────────────────────────────┼────────────────────────────────────┐
│ API GATEWAY │
│ │
│ ┌─────────────────────────┴────────────────────────────────┐ │
│ │ FastAPI Server │ │
│ │ │ │
│ │ ┌─────────┐ ┌────────────┐ ┌──────────┐ ┌────────┐ │ │
│ │ │ Auth │ │ CORS │ │ Audit │ │ Rate │ │ │
│ │ │Middleware│ │ Middleware │ │Middleware │ │Limiter │ │ │
│ │ └─────────┘ └────────────┘ └──────────┘ └────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ API Routes │ │ │
│ │ │ /employees /absences /balance /practices │ │ │
│ │ │ /auth /reports /health /audit │ │ │
│ │ └────────────────────────┬───────────────────────────┘ │ │
│ └───────────────────────────┼───────────────────────────────┘ │
└──────────────────────────────┼──────────────────────────────────┘
┌──────────────────────────────┼──────────────────────────────────┐
│ SERVICE LAYER │
│ (Businesslogik) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Employee │ │ Absence │ │ Approval │ │
│ │ Service │ │ Service │ │ Workflow │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
│ │ │ │ │
│ │ ┌────────────┴──────────┐ │ │
│ │ │ Business Rules │ │ │
│ │ │ • Überschneidung │ │ │
│ │ │ • Kontingentprüfung │ │ │
│ │ │ • Mindestbesetzung │ │ │
│ │ └───────────────────────┘ │ │
│ │ │ │
└─────────┼────────────────────────────────────────┼──────────────┘
│ │
┌─────────┼────────────────────────────────────────┼──────────────┐
│ │ REPOSITORY LAYER │ │
│ │ (Datenzugriff) │ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Employee │ │ Absence │ │ Balance │ │
│ │ Repository │ │ Repository │ │ Repository │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
└─────────┼─────────────────┼─────────────────────┼──────────────┘
│ │ │
└─────────────────┼──────────────────────┘
┌──────┴──────┐
│ SQLAlchemy │
│ ORM │
└──────┬──────┘
┌────────────┴────────────┐
│ │
┌──────┴──────┐ ┌───────┴──────┐
│ SQLite │ │ PostgreSQL │
│ (Dev) │ │ (Produktion)│
└─────────────┘ └──────────────┘
```
## 2. Komponentenbeschreibung
### Client Layer
| Komponente | Beschreibung |
|---|---|
| **Desktop App** | Bestehende tkinter-App. Wird umgebaut: statt lokaler JSON-Dateien ruft sie die REST API auf. Gleiche Benutzeranmeldung wie Web. |
| **Web App** | Modernes Web-Frontend (React/Vue/Svelte). Nutzt dieselbe API. Responsive für Tablet/Desktop. |
| **MedWork Plugin** | Adapter der MedWork-Daten (Patienten, Termine) mit dem Arbeitsplan verknüpft. Eigener API-Endpunkt. |
### API Layer
| Komponente | Beschreibung |
|---|---|
| **FastAPI Server** | Zentraler Einstiegspunkt. Stellt REST-Endpunkte bereit. Auto-generierte OpenAPI-Doku. |
| **Auth Middleware** | JWT-basierte Authentifizierung. Gleiche Tokens für Desktop + Web. |
| **Audit Middleware** | Protokolliert jede schreibende Aktion (wer, was, wann, alte/neue Werte). |
| **CORS Middleware** | Erlaubt Cross-Origin-Zugriffe für Web-Client. |
### Service Layer (Businesslogik)
| Komponente | Beschreibung |
|---|---|
| **EmployeeService** | Mitarbeiter anlegen/bearbeiten/deaktivieren. E-Mail-Duplikat-Check. |
| **AbsenceService** | Abwesenheiten erstellen mit Regelprüfung. Kontingent berechnen. |
| **Business Rules** | `check_no_overlap` keine überlappenden Einträge. `check_balance` genug Ferientage? `check_min_staffing` Mindestbesetzung gewährleistet? |
| **Approval Workflow** | Pending → Approved/Rejected Workflow mit Genehmiger-Zuweisung. |
### Repository Layer
| Komponente | Beschreibung |
|---|---|
| **EmployeeRepository** | CRUD für Mitarbeiter. Einziger Code der direkt SQL ausführt. |
| **AbsenceRepository** | CRUD + Spezialabfragen (Überschneidungen, Abwesende pro Tag). |
| **BalanceRepository** | Ferientage-Kontingent pro Mitarbeiter/Jahr. |
### Data Layer
| Komponente | Beschreibung |
|---|---|
| **SQLAlchemy ORM** | Abstrahiert Datenbank. Identischer Code für SQLite und PostgreSQL. |
| **Practice (Mandant)** | Multi-Tenant: jede Praxis hat eigene Mitarbeiter/Abwesenheiten. |
| **AuditLog** | Unveränderliches Änderungsprotokoll mit JSON-Diff. |
## 3. Technologie-Stack
| Schicht | Technologie | Begründung |
|---|---|---|
| **Backend** | Python 3.11+ | Bestehende Codebasis, grosses Ökosystem |
| **API Framework** | FastAPI | Async, Auto-Docs, Pydantic-Integration, modern |
| **ORM** | SQLAlchemy 2.0 | Industriestandard, DB-agnostisch |
| **Validierung** | Pydantic v2 | Schemas für API + Clients, schnell |
| **Auth** | JWT (python-jose) | Stateless, Desktop + Web kompatibel |
| **Passwörter** | passlib + bcrypt | Industriestandard |
| **DB (Dev)** | SQLite | Kein Setup nötig, lokales Testen |
| **DB (Prod)** | PostgreSQL 16 | ACID, JSON-Support, skalierbar |
| **Desktop** | tkinter (→ später Qt) | Bestehend, HTTP-Client wird ergänzt |
| **Web** | React oder Vue.js | SPA, nutzt dieselbe API |
| **Deployment** | Docker + Docker Compose | Einfach, reproduzierbar |
| **Reverse Proxy** | nginx / Caddy | HTTPS, Load Balancing |
## 4. Datenfluss
### Abwesenheit eintragen (von jedem Client)
```
Client (Desktop/Web)
POST /api/v1/absences
{ employee_id, category, start_date, end_date, reason }
Auth Middleware ──── JWT prüfen ──── 401 wenn ungültig
AbsenceService.create_absence()
├─→ EmployeeRepository.get_by_id() → Mitarbeiter existiert?
├─→ rules.check_no_overlap() → Überschneidung?
├─→ rules.check_balance() → Genug Ferientage?
├─→ rules.check_min_staffing() → Mindestbesetzung OK?
├─→ AbsenceRepository.create() → DB INSERT
├─→ AuditLog.log_action() → Protokollierung
├─→ db.commit()
Response: 201 Created
{ id, employee_id, category, status, business_days, ... }
Client aktualisiert Kalenderansicht
```
### Login-Flow (Desktop + Web identisch)
```
Client
POST /api/v1/auth/login
{ email, password }
verify_password(plain, hashed)
├─→ Falsch: 401 Unauthorized
├─→ Richtig: create_access_token(employee_id, role)
Response: { access_token, token_type, employee }
Client speichert Token → sendet bei jedem Request als
Authorization: Bearer <token>
```
### Multi-Tenant Datenfluss
```
Praxis A (ID: praxis_a) Praxis B (ID: praxis_b)
│ │
▼ ▼
Mitarbeiter mit Mitarbeiter mit
practice_id = praxis_a practice_id = praxis_b
│ │
▼ ▼
Abwesenheiten nur Abwesenheiten nur
für Praxis A sichtbar für Praxis B sichtbar
Admin-Benutzer mit role=ADMIN kann praxisübergreifend zugreifen.
```
## 5. Deployment-Konzept
### Entwicklung (lokal)
```
┌─────────────────────────────────────────┐
│ Entwickler-PC │
│ │
│ uvicorn workforce_planner.api.app:app │
│ │ │
│ └──→ SQLite (workforce_planner.db)│
│ │
│ Desktop App (tkinter) → localhost:8000 │
└─────────────────────────────────────────┘
```
### Produktion (Docker)
```
┌──────────────────────────────────────────────────────┐
│ Server / VPS / Cloud │
│ │
│ ┌────────────┐ │
│ │ nginx │ ◄── HTTPS (Let's Encrypt) │
│ │ :443/:80 │ │
│ └─────┬──────┘ │
│ │ │
│ ┌─────┴──────┐ ┌──────────────────────────────┐ │
│ │ FastAPI │ │ PostgreSQL │ │
│ │ :8000 ├──►│ :5432 │ │
│ │ (2 Worker) │ │ Volume: /data/postgres │ │
│ └────────────┘ └──────────────────────────────┘ │
│ │
│ docker-compose.yml orchestriert alles │
└──────────────────────────────────────────────────────┘
Desktop Clients ──────► server.praxis.ch:443/api/v1/
Web Clients ──────► server.praxis.ch:443/
```
### docker-compose.yml (Ziel)
```yaml
services:
api:
build: .
ports: ["8000:8000"]
environment:
WP_DATABASE_URL: postgresql://wp:secret@db:5432/workforce
WP_SECRET_KEY: ${SECRET_KEY}
depends_on: [db]
db:
image: postgres:16-alpine
volumes: [pgdata:/var/lib/postgresql/data]
environment:
POSTGRES_DB: workforce
POSTGRES_USER: wp
POSTGRES_PASSWORD: secret
nginx:
image: nginx:alpine
ports: ["443:443", "80:80"]
volumes: [./nginx.conf:/etc/nginx/conf.d/default.conf]
depends_on: [api]
volumes:
pgdata:
```
## Aktueller Stand
| Komponente | Status |
|---|---|
| Datenmodelle (Employee, Absence, BalanceAccount, Practice, AuditLog) | ✅ Fertig |
| Enums (AbsenceCategory, EmployeeRole, AbsenceStatus) | ✅ Fertig |
| Pydantic Schemas (Create/Update/Read) | ✅ Fertig |
| Database Setup (SQLite/PostgreSQL) | ✅ Fertig |
| Repository Layer (Employee, Absence, Balance) | ✅ Fertig |
| Service Layer + Business Rules | ✅ Fertig |
| FastAPI Routen (Employees, Absences, Balance) | ✅ Fertig |
| JWT Auth + Rollen | ✅ Fertig |
| Audit Logging | ✅ Fertig |
| Multi-Tenant (Practice) | ✅ Modell fertig |
| Desktop-Client Umstellung auf API | ⬜ Nächster Schritt |
| Web-Frontend | ⬜ Geplant |
| MedWork-Integration | ⬜ Geplant |
| Docker Deployment | ⬜ Geplant |