Files
aza/AzA march 2026 - Kopie (4)/workforce_planner/ARCHITECTURE.md

298 lines
16 KiB
Markdown
Raw Normal View History

2026-03-25 13:42:48 +01:00
# 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 |