Klinischer Stationsarbeitsplatz (CPOE) für das fiktive Musterklinikum Nord: Laborauftrag, Befundrücklauf, AMTS, HL7 v2 und FHIR R4. Nur synthetische Demodaten.
Spring-Boot-Backend und React/TypeScript-UI in einem Repository; der UI-Build liegt im JAR unter /.
- Dr. med. Lena Weber (Passwort
demo). - Elena Krüger — Bett 12, Allergie Penicillin, Verdacht auf ambulant erworbene Pneumonie.
- Laborauftrag Blutbild + CRP →
ORM^O01plus ACK. - Rolle Labor: annehmen, Befund freigeben →
ORU^R01. - CRP pathologisch. Amoxicillin → AMTS-Sperre (ATC-Hierarchie J01C).
- Cefuroxim → Hinweis Kreuzallergie β-Laktam. Ansicht HL7 / FHIR.
Optional zweiter Fall: Karl-Heinz Vogt, Herzinsuffizienz NYHA III. Ibuprofen → AMTS-Hinweis NSAR; Niere über CKD-EPI, sobald Kreatinin befundet ist.
Kennungen: weber Ärztin, hoffmann MTA, schmidt Pflege — Passwort demo. RBAC: Pflege ohne CPOE.
Synthetische Demo, aufgenommen lokal.
| Login | Station Innere 3 |
|---|---|
![]() |
![]() |
| AMTS-Sperre Elena / Penicillin | Interop HL7 v2 + FHIR R4 (Spalte MSH) |
![]() |
![]() |
Akte, Labor-Worklist und Pflege-RBAC: docs/screenshots/03-akte-elena.png, docs/screenshots/04-labor.png, docs/screenshots/07-pflege-rbac.png.
Öffentliche Demo auf Render Free: https://careflow-1-s5lg.onrender.com
Render Free schläft nach Idle. Der erste Request danach kann 30–60 Sekunden dauern (Cold Start) — das ist kein Fehler. Danach Login-Seite Musterklinikum Nord. H2 startet leer; der Seeder legt sechs Fälle an.
Kennungen (Passwort überall demo): weber Ärztin, hoffmann MTA, schmidt Pflege. RBAC: Pflege ohne CPOE.
5-Minuten-Pfad auf der Live-Instanz: Dr. med. Lena Weber → Elena Krüger (Bett 12) → Laborauftrag Blutbild + CRP → Rolle Labor, Befund freigeben → Amoxicillin (AMTS-Sperre) → Cefuroxim → Ansicht HL7 / FHIR.
| Einstieg | URL |
|---|---|
| Stationsarbeitsplatz | https://careflow-1-s5lg.onrender.com/ |
| FHIR Patient | https://careflow-1-s5lg.onrender.com/fhir/Patient?_format=json |
| FHIR Patient (MRN Elena) | https://careflow-1-s5lg.onrender.com/fhir/Patient?identifier=MKN-10021&_format=json |
| FHIR Observation | https://careflow-1-s5lg.onrender.com/fhir/Observation?patient={id}&_format=json |
| FHIR metadata | https://careflow-1-s5lg.onrender.com/fhir/metadata?_format=json |
| OpenAPI | https://careflow-1-s5lg.onrender.com/swagger-ui.html |
| Health | https://careflow-1-s5lg.onrender.com/actuator/health |
Akte zeigt Kreatinin/eGFR; Interop das Audit-Protokoll.
Backend
- Java 21, Spring Boot 3.4 (Web, WebSocket, Security/RBAC, Data JPA, Validation, Actuator)
- Flyway, Hibernate (
ddl-auto: validate), Optimistic Locking (@Versionauf Aufträgen) - H2 im Demo-Betrieb, PostgreSQL 16 lokal per Docker Compose
- springdoc-openapi
Interop
- HAPI HL7 v2.5:
ORM^O01(ORC NW / SC / CA),ORU^R01(ORC CM),ACK(PipeParser, ohne Validating) - Ausgang ORM NW/CA von CAREFLOW, Eingang SC/ORU von LABSYS, ACK jeweils vom Empfänger. FHIR-Suche gebatched.
- HAPI FHIR 7.6 R4 RestfulServer als Lese-Projektion (Search/Read/metadata), kein Create: Patient (auch
identifier/MRN), Encounter, AllergyIntolerance, ServiceRequest, Observation, DiagnosticReport, MedicationRequest - FHIR Collection-Bundle je Akte unter
/api/patients/{id}/fhir
Fachlogik
- Auftrags-Zustandsmaschine (LAB: PLACED → IN_LAB → RESULTED; MED: ACTIVE / BLOCKED)
- Storno: LAB in PLACED/IN_LAB und MED in ACTIVE → CANCELLED
- Optimistic Locking sichtbar als HTTP 409
OPTIMISTIC_LOCK - AMTS-Regelengine: ATC-Hierarchie (Allergie-Prefix, chemische 5-Stellen-Gruppe), Kreuzallergie J01C/J01D, NSAR bei Herzinsuffizienz
- AMTS-Sperre (HTTP 409
CDS_BLOCK) kann die Ärztin dokumentiert überschreiben (override): Auftrag bleibtBLOCKED, Audit/Alertoverridden. Der 5-Minuten-Pfad nimmt Cefuroxim, nicht den Override. - Niere: CKD-EPI Kreatinin 2021 (ohne Ethnizitätskorrektur), NSAR-Block bei eGFR unter 30, Warnung unter 60
- Befundflags HL7 0078 (N/L/H/LL/HH) inkl. LOINC-Panic-Grenzen
- Offene Doppel-Laboraufträge und überlappende Panels (BBCRP ⊃ BB/CRP): HTTP 409
- Stationsboard, Akte und Labor-Worklist in einem Roundtrip (kein N+1)
Frontend
- React 19, TypeScript, Vite 6
- Views: Login, Stationsboard, Akte, Labor-Worklist, Interop
- RBAC in der Oberfläche: Pflege ohne CPOE- und Labor-Aktionen (nicht nur API 403); Labor ohne Anordnung
- Stationsboard und Interop-Log tastaturbedienbar (
aria-current, Fokusring) - Interop: eigene Spalte MSH (CAREFLOW ↔ LABSYS) plus ORC-Chips (NW/SC/CA/CM; ACK ohne ORC)
- Produktionsbuild im selben JAR (
npm run build:spring) - WebSocket-Ereignisse in der UI nur mit Session; HL7-Storno als ORM CA
Qualität / Betrieb
- Bean Validation: ungültiger Request → HTTP 400
VALIDATION - CSRF absichtlich aus (Cookie-SPA + Vite-Proxy); Session HttpOnly + SameSite=Lax;
Secureim Docker/Render-Betrieb, lokal HTTP ohne Secure - WebSocket-Origins: localhost und
https://*.onrender.com(kein*) - Demo-Session: 8 Stunden
- JUnit 5: ATC, CKD-EPI, Referenzbereich, Zustandsmaschine/Storno, HL7-Roundtrip, API (AMTS-Sperre, Override
BLOCKED/overridden, VALIDATION 400, RBAC Pflege ohne CPOE/Laborannahme/Freigabe, Overlap 409, SameSite-Cookie, Kreatinin/eGFR, Audit-DTO, FHIR Search inkl. Patientidentifier/MRN, Read ohne Create, CapabilityStatement) - GitHub Actions (CI grün): Temurin 21, Node 22, Free Runner
- Docker Multi-Stage; Render Free, ein Dienst, H2 im Speicher; Tomcat auf
0.0.0.0/ IPv4 (Health-Check findet den Port) - Live-Demo: https://careflow-1-s5lg.onrender.com (Cold Start nach Idle 30–60 s)
- SPA indexierbar (
robots.txtAllow/); Open Graph für geteilte Links
Kein Kafka, kein STOMP, kein Keycloak, keine bezahlte Arzneimittel-DB.
flowchart LR
Arzt[Arzt UI] --> API[Careflow API]
Labor[Labor UI] --> API
API --> SM[Zustandsmaschine]
SM --> HL7[HL7 v2 ORM/ORU]
SM --> CDS[AMTS ATC / CKD-EPI]
SM --> FHIR[FHIR R4 Projektion]
Eine Akte, drei Sichten: Station, Labor, Schnittstelle. FHIR ist Lese-Projektion (Search/Read/metadata), nicht zweites Primärsystem.
./mvnw spring-boot:run
cd frontend && npm install && npm run devUI: http://localhost:5173 — API: http://localhost:8080/swagger-ui.html
UI im JAR:
cd frontend && npm run build:spring
./mvnw spring-boot:run./mvnw -B test
cd frontend && npm run buildREADME-Screenshots (optional; Playwright nur lokal, keine Projekt-Dependency):
cd frontend && npm install --no-save playwright && npx playwright install chromium
cd ..
# UI im JAR, z. B. PORT=8090 ./mvnw spring-boot:run
node scripts/capture-screenshots.mjsCAREFLOW_URL default http://127.0.0.1:8080.



