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

16 KiB
Raw Blame History

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)

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