Architektur
Überblick über Aufbau und technische Realisierung des Portals. Ziel ist, dass neue Teammitglieder schnell verstehen, wo welche Logik liegt und wie Daten von der Datenbank bis in die Charts gelangen.
Systemübersicht
PharMaAnalyst besteht aus einem Next.js Frontend (Pages Router) mit API-Routen für Datenzugriffe. In Produktion läuft die App hinter einem Nginx Reverse Proxy; im Backend wird eine PostgreSQL Datenbank verwendet.
High-level Architektur
flowchart LR U[User / Browser] -->|HTTPS| N[Nginx Reverse Proxy] N -->|HTTP| A["Next.js (Frontend + API Routes)"] A -->|SQL| DB[(PostgreSQL)] A -->|JSON| U
Analyse-Requests laufen primär über frontend/pages/api/data.js (Medikament/Wirkstoff) sowie separate Endpunkte für Ranking/Hersteller. Die API liefert neben den eigentlichen Daten auch die effektiv verwendeten Parameter zurück.
Defaults (Auto-Load)Beim Klick auf eine Analyse-Seite wird automatisch ein interessanter Default-Datensatz geladen. Best Practice: Die Default-Policy ist zentral und serverseitig, damit Frontend und Export konsistent bleiben.
- Default-Policy/Queries: frontend/lib/defaults.js
- Default-Resolution + Kennzeichnung: frontend/pages/api/data.js
- Frontend Auto-Load Trigger: frontend/pages/index.js
Default-Load Datenfluss
sequenceDiagram autonumber participant UI as UI (Next.js) participant API as /api/data participant Defaults as defaults.js participant DB as PostgreSQL UI->>API: GET /api/data?mode=medikament (ohne Auswahl/Zeitraum) API->>Defaults: last full year + default selection Defaults->>DB: Aggregation queries (Top 10 / ATC L1 Top) DB-->>Defaults: Default IDs / ATC + Zeitraum Defaults-->>API: resolved defaults API->>DB: Data queries (summary/timeseries/...) DB-->>API: result rows API-->>UI: JSON + resolvedParams + defaultsApplied
Die Produktions-Instanz wird als Next.js Standalone über PM2 gestartet. Der Wrapper stellt sicher, dass Umgebungsvariablen geladen werden und statische Assets verfügbar bleiben:
frontend/scripts/pm2-standalone.js
Details zu Default-Datensätzen und Zeiträumen sind zusätzlich im Repository dokumentiert: README (Defaults)
Hinweis zu Mermaid: Die Diagramme werden client-seitig gerendert und nur auf dieser Seite lazy-loaded. Das ist in der Regel nicht ressourcenlastig, solange die Diagramme klein bleiben.
ER-Diagramm (Datenbank)Snapshot des aktuellen Schemas (Stand: 2025-12-13, DB: pad). PK/FK markieren Primary Key und Foreign Key (NOT NULL wird hier nicht extra annotiert).
ER-Diagramm (PostgreSQL)
erDiagram
age_groups {
int age_group_id PK
text age_group_description
}
aggregated_data {
int data_id PK
int medication_id FK
int year
int month
int region_id FK
int age_group_id FK
int gender_id FK
numeric verordnungen
numeric nettokosten
numeric tagesdosen_ddd
numeric kosten_je_verordnung
numeric kosten_je_tagesdosis
timestamp loaded_at
}
atc_codes {
text atc_code PK
text description
int level
text parent_atc_code FK
}
genders {
int gender_id PK
text gender_description
}
market_totals {
int id PK
int year
int month
text market_segment
text atc_level1
numeric total_verordnungen
numeric total_nettokosten
numeric total_ddd
int medication_count
timestamp created_at
}
medications {
int medication_id PK
text pzn
text product_name
text manufacturer
text atc_code FK
boolean is_generic
text market_type
}
physician_groups {
int physician_group_id PK
text group_code
text group_name
text category
int sort_order
}
physician_prescriptions {
int id PK
int medication_id FK
int physician_group_id FK
int year
int month
numeric verordnungen
numeric nettokosten
numeric tagesdosen_ddd
timestamp created_at
}
regions {
int region_id PK
text region_code
text region_name
}
age_groups ||--o{ aggregated_data : "age_group_id"
genders ||--o{ aggregated_data : "gender_id"
medications ||--o{ aggregated_data : "medication_id"
regions ||--o{ aggregated_data : "region_id"
atc_codes ||--o{ atc_codes : "parent_atc_code"
atc_codes ||--o{ medications : "atc_code"
medications ||--o{ physician_prescriptions : "medication_id"
physician_groups ||--o{ physician_prescriptions : "physician_group_id"