mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 10:09:35 +01:00
refactor: restructure repository with separate backend and frontend directories
- Move Java backend to backend/ directory - Create frontend/ directory for TypeScript TUI and future WebUI - Update .gitignore for Node.js and worktrees - Update README.md with new repository structure - Copy documentation to backend/
This commit is contained in:
parent
ec9114aa0a
commit
c2c48a03e8
141 changed files with 734 additions and 9 deletions
13
.gitignore
vendored
13
.gitignore
vendored
|
|
@ -50,3 +50,16 @@ coverage/
|
||||||
.mvn/
|
.mvn/
|
||||||
mvnw
|
mvnw
|
||||||
mvnw.cmd
|
mvnw.cmd
|
||||||
|
|
||||||
|
# Node.js / Frontend
|
||||||
|
frontend/node_modules/
|
||||||
|
frontend/dist/
|
||||||
|
frontend/build/
|
||||||
|
frontend/.pnpm-store/
|
||||||
|
frontend/**/.turbo/
|
||||||
|
frontend/**/.next/
|
||||||
|
*.tsbuildinfo
|
||||||
|
.pnpm-debug.log
|
||||||
|
|
||||||
|
# Git worktrees
|
||||||
|
.worktrees/
|
||||||
|
|
|
||||||
94
README.md
94
README.md
|
|
@ -2,6 +2,20 @@
|
||||||
|
|
||||||
ERP-System für Fleischereien mit HACCP-Compliance, GoBD-konform, Mehrfilialen-Support.
|
ERP-System für Fleischereien mit HACCP-Compliance, GoBD-konform, Mehrfilialen-Support.
|
||||||
|
|
||||||
|
## Repository Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
effigenix/
|
||||||
|
├── backend/ # Java Spring Boot Backend
|
||||||
|
│ ├── src/ # Java source code (DDD + Clean Architecture)
|
||||||
|
│ ├── docs/ # Backend documentation
|
||||||
|
│ └── pom.xml # Maven configuration
|
||||||
|
│
|
||||||
|
└── frontend/ # TypeScript Frontend (TUI & WebUI)
|
||||||
|
├── apps/ # Applications (CLI TUI, WebUI)
|
||||||
|
└── packages/ # Shared packages (api-client, types, validation)
|
||||||
|
```
|
||||||
|
|
||||||
## Architektur
|
## Architektur
|
||||||
|
|
||||||
**Domain-Driven Design + Clean Architecture + Java 21 + Spring Boot**
|
**Domain-Driven Design + Clean Architecture + Java 21 + Spring Boot**
|
||||||
|
|
@ -58,14 +72,15 @@ ERP-System für Fleischereien mit HACCP-Compliance, GoBD-konform, Mehrfilialen-S
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
### Prerequisites
|
### Backend (Java Spring Boot)
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
- Java 21+
|
- Java 21+
|
||||||
- Maven 3.9+
|
- Maven 3.9+
|
||||||
- PostgreSQL 15+
|
- PostgreSQL 15+
|
||||||
- Docker (optional, für PostgreSQL)
|
- Docker (optional, für PostgreSQL)
|
||||||
|
|
||||||
### Database Setup
|
**Database Setup:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# PostgreSQL mit Docker
|
# PostgreSQL mit Docker
|
||||||
|
|
@ -77,9 +92,11 @@ docker run --name effigenix-postgres \
|
||||||
-d postgres:15
|
-d postgres:15
|
||||||
```
|
```
|
||||||
|
|
||||||
### Build & Run
|
**Build & Run:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
cd backend
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
mvn clean install
|
mvn clean install
|
||||||
|
|
||||||
|
|
@ -90,16 +107,41 @@ mvn spring-boot:run
|
||||||
mvn spring-boot:run -Dspring-boot.run.profiles=dev
|
mvn spring-boot:run -Dspring-boot.run.profiles=dev
|
||||||
```
|
```
|
||||||
|
|
||||||
### API Documentation
|
**API Documentation:**
|
||||||
|
|
||||||
Nach dem Start verfügbar unter:
|
Nach dem Start verfügbar unter:
|
||||||
- Swagger UI: http://localhost:8080/swagger-ui.html
|
- Swagger UI: http://localhost:8080/swagger-ui.html
|
||||||
- OpenAPI Spec: http://localhost:8080/api-docs
|
- OpenAPI Spec: http://localhost:8080/api-docs
|
||||||
|
|
||||||
|
### Frontend (TypeScript TUI)
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
- Node.js 20+
|
||||||
|
- pnpm 9+
|
||||||
|
|
||||||
|
**Setup & Run:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# Run TUI in dev mode
|
||||||
|
pnpm run dev
|
||||||
|
|
||||||
|
# Build for production
|
||||||
|
pnpm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Detailed frontend documentation: [frontend/README.md](frontend/README.md)
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
|
**Backend (Java):**
|
||||||
|
|
||||||
```
|
```
|
||||||
src/main/java/com/effigenix/
|
backend/src/main/java/de/effigenix/
|
||||||
├── domain/ # Domain Layer (keine Framework-Dependencies!)
|
├── domain/ # Domain Layer (keine Framework-Dependencies!)
|
||||||
│ └── usermanagement/
|
│ └── usermanagement/
|
||||||
│ ├── User.java
|
│ ├── User.java
|
||||||
|
|
@ -120,8 +162,27 @@ src/main/java/com/effigenix/
|
||||||
├── security/ # AuthorizationPort, Action
|
├── security/ # AuthorizationPort, Action
|
||||||
└── common/ # Result, ApplicationError
|
└── common/ # Result, ApplicationError
|
||||||
|
|
||||||
src/main/resources/
|
backend/src/main/resources/
|
||||||
└── db/migration/ # Flyway Migrations
|
└── db/migration/ # Liquibase Migrations
|
||||||
|
```
|
||||||
|
|
||||||
|
**Frontend (TypeScript):**
|
||||||
|
|
||||||
|
```
|
||||||
|
frontend/
|
||||||
|
├── apps/
|
||||||
|
│ └── cli/ # Terminal UI (Ink)
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── components/ # UI components
|
||||||
|
│ │ ├── hooks/ # React hooks
|
||||||
|
│ │ └── state/ # State management
|
||||||
|
│ └── package.json
|
||||||
|
│
|
||||||
|
└── packages/ # Shared packages (reusable for WebUI)
|
||||||
|
├── api-client/ # HTTP client für Backend API
|
||||||
|
├── types/ # TypeScript types (generated from OpenAPI)
|
||||||
|
├── validation/ # Zod schemas
|
||||||
|
└── config/ # Shared configuration
|
||||||
```
|
```
|
||||||
|
|
||||||
## User Management (Generic Subdomain)
|
## User Management (Generic Subdomain)
|
||||||
|
|
@ -166,7 +227,11 @@ public class CreateRecipe {
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
|
**Backend:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
cd backend
|
||||||
|
|
||||||
# Unit Tests
|
# Unit Tests
|
||||||
mvn test
|
mvn test
|
||||||
|
|
||||||
|
|
@ -177,6 +242,21 @@ mvn verify
|
||||||
mvn clean verify jacoco:report
|
mvn clean verify jacoco:report
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Frontend:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
|
||||||
|
# Unit Tests
|
||||||
|
pnpm test
|
||||||
|
|
||||||
|
# Test Coverage
|
||||||
|
pnpm run test:coverage
|
||||||
|
|
||||||
|
# Type Check
|
||||||
|
pnpm run typecheck
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Proprietary - Effigenix GmbH
|
Proprietary - Effigenix GmbH
|
||||||
|
|
|
||||||
4
TODO.md
4
TODO.md
|
|
@ -7,7 +7,7 @@
|
||||||
3. ✅ Inventory BC implementieren (Basis: 8.1-8.3)
|
3. ✅ Inventory BC implementieren (Basis: 8.1-8.3)
|
||||||
4. ✅ Document Archive BC (Basis: 12.1-12.2) - parallel zu Inventory
|
4. ✅ Document Archive BC (Basis: 12.1-12.2) - parallel zu Inventory
|
||||||
|
|
||||||
- [ ] Liquibase statt Flyway
|
- [x] Liquibase statt Flyway
|
||||||
- [ ] Package Struktur gemäß DDD-model skill, ddd-implementer fragen wegen refactor?
|
- [x] Package Struktur gemäß DDD-model skill, ddd-implementer fragen wegen refactor?
|
||||||
- [ ] ActionToPermissionMapper, warum unterschiedliches Vorgehen if/else vs. switch/case
|
- [ ] ActionToPermissionMapper, warum unterschiedliches Vorgehen if/else vs. switch/case
|
||||||
- [ ] Nix Shell für manuelles Testing mit Postgres sowie für Migrationstests
|
- [ ] Nix Shell für manuelles Testing mit Postgres sowie für Migrationstests
|
||||||
|
|
|
||||||
92
backend/.factorypath
Normal file
92
backend/.factorypath
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
<factorypath>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-web/3.2.2/spring-boot-starter-web-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter/3.2.2/spring-boot-starter-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot/3.2.2/spring-boot-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-autoconfigure/3.2.2/spring-boot-autoconfigure-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-logging/3.2.2/spring-boot-starter-logging-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-classic/1.4.14/logback-classic-1.4.14.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-core/1.4.14/logback-core-1.4.14.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-to-slf4j/2.21.1/log4j-to-slf4j-2.21.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-api/2.21.1/log4j-api-2.21.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/jul-to-slf4j/2.0.11/jul-to-slf4j-2.0.11.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/annotation/jakarta.annotation-api/2.1.1/jakarta.annotation-api-2.1.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-json/3.2.2/spring-boot-starter-json-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.15.3/jackson-datatype-jdk8-2.15.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.15.3/jackson-datatype-jsr310-2.15.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/module/jackson-module-parameter-names/2.15.3/jackson-module-parameter-names-2.15.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-tomcat/3.2.2/spring-boot-starter-tomcat-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-core/10.1.18/tomcat-embed-core-10.1.18.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-websocket/10.1.18/tomcat-embed-websocket-10.1.18.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-web/6.1.3/spring-web-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-beans/6.1.3/spring-beans-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/io/micrometer/micrometer-observation/1.12.2/micrometer-observation-1.12.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/io/micrometer/micrometer-commons/1.12.2/micrometer-commons-1.12.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-webmvc/6.1.3/spring-webmvc-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context/6.1.3/spring-context-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-expression/6.1.3/spring-expression-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-data-jpa/3.2.2/spring-boot-starter-data-jpa-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-aop/3.2.2/spring-boot-starter-aop-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/aspectj/aspectjweaver/1.9.21/aspectjweaver-1.9.21.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-jdbc/3.2.2/spring-boot-starter-jdbc-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/com/zaxxer/HikariCP/5.0.1/HikariCP-5.0.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jdbc/6.1.3/spring-jdbc-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/hibernate/orm/hibernate-core/6.4.1.Final/hibernate-core-6.4.1.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/persistence/jakarta.persistence-api/3.1.0/jakarta.persistence-api-3.1.0.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/transaction/jakarta.transaction-api/2.0.1/jakarta.transaction-api-2.0.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/jboss/logging/jboss-logging/3.5.3.Final/jboss-logging-3.5.3.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/hibernate/common/hibernate-commons-annotations/6.0.6.Final/hibernate-commons-annotations-6.0.6.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/io/smallrye/jandex/3.1.2/jandex-3.1.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/classmate/1.6.0/classmate-1.6.0.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/net/bytebuddy/byte-buddy/1.14.11/byte-buddy-1.14.11.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/glassfish/jaxb/jaxb-runtime/4.0.4/jaxb-runtime-4.0.4.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/glassfish/jaxb/jaxb-core/4.0.4/jaxb-core-4.0.4.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/eclipse/angus/angus-activation/2.0.1/angus-activation-2.0.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/glassfish/jaxb/txw2/4.0.4/txw2-4.0.4.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/com/sun/istack/istack-commons-runtime/4.1.2/istack-commons-runtime-4.1.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/antlr/antlr4-runtime/4.13.0/antlr4-runtime-4.13.0.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-jpa/3.2.2/spring-data-jpa-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-commons/3.2.2/spring-data-commons-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-orm/6.1.3/spring-orm-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-tx/6.1.3/spring-tx-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/slf4j-api/2.0.11/slf4j-api-2.0.11.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-aspects/6.1.3/spring-aspects-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-validation/3.2.2/spring-boot-starter-validation-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-el/10.1.18/tomcat-embed-el-10.1.18.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/hibernate/validator/hibernate-validator/8.0.1.Final/hibernate-validator-8.0.1.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/validation/jakarta.validation-api/3.0.2/jakarta.validation-api-3.0.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-security/3.2.2/spring-boot-starter-security-3.2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-aop/6.1.3/spring-aop-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-config/6.2.1/spring-security-config-6.2.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-web/6.2.1/spring-security-web-6.2.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/io/jsonwebtoken/jjwt-api/0.12.5/jjwt-api-0.12.5.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/io/jsonwebtoken/jjwt-impl/0.12.5/jjwt-impl-0.12.5.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/io/jsonwebtoken/jjwt-jackson/0.12.5/jjwt-jackson-0.12.5.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-databind/2.15.3/jackson-databind-2.15.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-annotations/2.15.3/jackson-annotations-2.15.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.15.3/jackson-core-2.15.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/postgresql/postgresql/42.6.0/postgresql-42.6.0.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/checkerframework/checker-qual/3.31.0/checker-qual-3.31.0.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/liquibase/liquibase-core/4.24.0/liquibase-core-4.24.0.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/com/opencsv/opencsv/5.8/opencsv-5.8.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-lang3/3.13.0/commons-lang3-3.13.0.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-text/1.10.0/commons-text-1.10.0.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-collections4/4.4/commons-collections4-4.4.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/yaml/snakeyaml/2.2/snakeyaml-2.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springdoc/springdoc-openapi-starter-webmvc-ui/2.3.0/springdoc-openapi-starter-webmvc-ui-2.3.0.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springdoc/springdoc-openapi-starter-webmvc-api/2.3.0/springdoc-openapi-starter-webmvc-api-2.3.0.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springdoc/springdoc-openapi-starter-common/2.3.0/springdoc-openapi-starter-common-2.3.0.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/core/v3/swagger-core-jakarta/2.2.19/swagger-core-jakarta-2.2.19.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/core/v3/swagger-annotations-jakarta/2.2.19/swagger-annotations-jakarta-2.2.19.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/core/v3/swagger-models-jakarta/2.2.19/swagger-models-jakarta-2.2.19.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/dataformat/jackson-dataformat-yaml/2.15.3/jackson-dataformat-yaml-2.15.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/webjars/swagger-ui/5.10.3/swagger-ui-5.10.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/projectlombok/lombok/1.18.30/lombok-1.18.30.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/xml/bind/jakarta.xml.bind-api/4.0.1/jakarta.xml.bind-api-4.0.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/jakarta/activation/jakarta.activation-api/2.1.2/jakarta.activation-api-2.1.2.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-core/6.1.3/spring-core-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jcl/6.1.3/spring-jcl-6.1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-core/6.2.1/spring-security-core-6.2.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-crypto/6.2.1/spring-security-crypto-6.2.1.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
</factorypath>
|
||||||
47
backend/CLAUDE.md
Normal file
47
backend/CLAUDE.md
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Effigenix ERP – Agent Guide
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
Java 21, Spring Boot 3.2, PostgreSQL, Liquibase, JWT (JJWT), Maven
|
||||||
|
|
||||||
|
## Architektur
|
||||||
|
DDD + Clean Architecture. Einweg-Abhängigkeit: `domain → application → infrastructure`.
|
||||||
|
|
||||||
|
```
|
||||||
|
de.effigenix.
|
||||||
|
├── domain.{bc}/ # Reine Geschäftslogik, KEINE Framework-Deps
|
||||||
|
├── application.{bc}/ # Use Cases, Commands, DTOs
|
||||||
|
├── infrastructure.{bc}/ # JPA, REST, Security, Audit
|
||||||
|
└── shared/ # Shared Kernel (Result<E,T>, AuthorizationPort, Action)
|
||||||
|
```
|
||||||
|
|
||||||
|
Bounded Contexts: `usermanagement` (implementiert), `production`, `quality`, `inventory`, `procurement`, `sales`, `labeling`, `filiales` (Platzhalter).
|
||||||
|
|
||||||
|
## Namenskonventionen
|
||||||
|
| Artefakt | Muster | Beispiel |
|
||||||
|
|---|---|---|
|
||||||
|
| Use Case | `{Verb}{Noun}` | `CreateUser`, `AuthenticateUser` |
|
||||||
|
| Command | `{Verb}{Noun}Command` | `CreateUserCommand` |
|
||||||
|
| Domain Entity | `{Noun}` | `User`, `Role` |
|
||||||
|
| Value Object | `{Noun}` | `UserId`, `PasswordHash`, `RoleName` |
|
||||||
|
| Domain Error | `{Noun}Error` (sealed interface) | `UserError.UsernameAlreadyExists` |
|
||||||
|
| JPA Entity | `{Noun}Entity` | `UserEntity` |
|
||||||
|
| Mapper | `{Noun}Mapper` | `UserMapper` (Domain↔JPA) |
|
||||||
|
| Repository (Domain) | `{Noun}Repository` | `UserRepository` (Interface) |
|
||||||
|
| Repository (Impl) | `Jpa{Noun}Repository` | `JpaUserRepository` |
|
||||||
|
| Controller | `{Noun}Controller` | `UserController` |
|
||||||
|
| Web DTO | `{Verb}{Noun}Request` | `CreateUserRequest` |
|
||||||
|
| Action Enum | `{Noun}Action implements Action` | `ProductionAction` |
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
Funktional via `Result<E, T>` (`shared.common.Result`). Domain-Fehler sind sealed interfaces mit Records. Keine Exceptions im Domain/Application Layer.
|
||||||
|
|
||||||
|
## Commits
|
||||||
|
Conventional Commits. Kein `Co-Authored-By` Header – niemals.
|
||||||
|
|
||||||
|
## DDD Skill
|
||||||
|
Für neue Bounded Contexts: `/ddd-implement` Skill verwenden. Dokumentation unter `.claude/skills/ddd-implement/SKILL.md`.
|
||||||
|
|
||||||
|
## Doku
|
||||||
|
- `docs/QUICK_START.md` – Lokale Entwicklung, Docker, Seed-Daten
|
||||||
|
- `docs/USER_MANAGEMENT.md` – Referenz-BC mit AuthorizationPort, JWT, Audit
|
||||||
|
- `TODO.md` – Offene Aufgaben und Waves
|
||||||
188
backend/README.md
Normal file
188
backend/README.md
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
# Effigenix Fleischerei ERP
|
||||||
|
|
||||||
|
ERP-System für Fleischereien mit HACCP-Compliance, GoBD-konform, Mehrfilialen-Support.
|
||||||
|
|
||||||
|
## Architektur
|
||||||
|
|
||||||
|
**Domain-Driven Design + Clean Architecture + Java 21 + Spring Boot**
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Presentation (REST Controllers) │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Application Layer (Use Cases) │
|
||||||
|
│ - Transaction Script for Generic Subdomains │
|
||||||
|
│ - Rich Domain Model for Core Domains │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Domain Layer (DDD Tactical Patterns) │
|
||||||
|
│ - Aggregates, Entities, Value Objects │
|
||||||
|
│ - Domain Events, Repositories │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Infrastructure Layer │
|
||||||
|
│ - Spring, JPA, PostgreSQL, JWT, REST │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bounded Contexts (11)
|
||||||
|
|
||||||
|
### Core Domains (7)
|
||||||
|
- **Production Management** - Rezeptverwaltung, Chargenproduktion
|
||||||
|
- **Quality Management** - HACCP-Compliance, Temperaturüberwachung
|
||||||
|
- **Inventory Management** - Bestandsführung, Lagerverwaltung
|
||||||
|
- **Procurement** - Einkauf, Wareneingang, Lieferanten
|
||||||
|
- **Sales** - Auftragserfassung, Rechnungsstellung, Kunden
|
||||||
|
|
||||||
|
### Supporting Domains (3)
|
||||||
|
- **Labeling** - Etikettendruck mit HACCP-Daten
|
||||||
|
- **Filiales** - Mehrfilialen-Verwaltung
|
||||||
|
|
||||||
|
### Generic Subdomains (3)
|
||||||
|
- **User Management** - Authentifizierung, Autorisierung, Rollen
|
||||||
|
- **Reporting** - Standard-Reports
|
||||||
|
- **Notifications** - E-Mail/SMS-Benachrichtigungen
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
- **Java 21** (Records, Sealed Interfaces, Pattern Matching)
|
||||||
|
- **Spring Boot 3.2** (Spring Security 6, Spring Data JPA 3)
|
||||||
|
- **PostgreSQL 15+** (Produktiv-DB)
|
||||||
|
- **JWT** (Stateless Authentication)
|
||||||
|
- **Flyway** (Schema Migrations)
|
||||||
|
- **Maven** (Build Tool)
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Java 21+
|
||||||
|
- Maven 3.9+
|
||||||
|
- PostgreSQL 15+
|
||||||
|
- Docker (optional, für PostgreSQL)
|
||||||
|
|
||||||
|
### Database Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# PostgreSQL mit Docker
|
||||||
|
docker run --name effigenix-postgres \
|
||||||
|
-e POSTGRES_DB=effigenix \
|
||||||
|
-e POSTGRES_USER=effigenix \
|
||||||
|
-e POSTGRES_PASSWORD=effigenix \
|
||||||
|
-p 5432:5432 \
|
||||||
|
-d postgres:15
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build & Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build
|
||||||
|
mvn clean install
|
||||||
|
|
||||||
|
# Run
|
||||||
|
mvn spring-boot:run
|
||||||
|
|
||||||
|
# Run with specific profile
|
||||||
|
mvn spring-boot:run -Dspring-boot.run.profiles=dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Documentation
|
||||||
|
|
||||||
|
Nach dem Start verfügbar unter:
|
||||||
|
- Swagger UI: http://localhost:8080/swagger-ui.html
|
||||||
|
- OpenAPI Spec: http://localhost:8080/api-docs
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/main/java/com/effigenix/
|
||||||
|
├── domain/ # Domain Layer (keine Framework-Dependencies!)
|
||||||
|
│ └── usermanagement/
|
||||||
|
│ ├── User.java
|
||||||
|
│ ├── Role.java
|
||||||
|
│ ├── UserId.java
|
||||||
|
│ └── UserRepository.java
|
||||||
|
├── application/ # Application Layer (Use Cases)
|
||||||
|
│ └── usermanagement/
|
||||||
|
│ ├── CreateUser.java
|
||||||
|
│ ├── AuthenticateUser.java
|
||||||
|
│ └── dto/
|
||||||
|
├── infrastructure/ # Infrastructure Layer
|
||||||
|
│ ├── persistence/
|
||||||
|
│ ├── security/
|
||||||
|
│ ├── web/
|
||||||
|
│ └── audit/
|
||||||
|
└── shared/ # Shared Kernel
|
||||||
|
├── security/ # AuthorizationPort, Action
|
||||||
|
└── common/ # Result, ApplicationError
|
||||||
|
|
||||||
|
src/main/resources/
|
||||||
|
└── db/migration/ # Flyway Migrations
|
||||||
|
```
|
||||||
|
|
||||||
|
## User Management (Generic Subdomain)
|
||||||
|
|
||||||
|
### Vordefinierte Rollen
|
||||||
|
|
||||||
|
| Rolle | Permissions | Zielgruppe |
|
||||||
|
|-------|-------------|------------|
|
||||||
|
| **ADMIN** | Alle | Systemadministrator |
|
||||||
|
| **PRODUCTION_MANAGER** | RECIPE_*, BATCH_*, PRODUCTION_ORDER_* | Leiter Produktion |
|
||||||
|
| **PRODUCTION_WORKER** | RECIPE_READ, BATCH_* | Produktionsmitarbeiter |
|
||||||
|
| **QUALITY_MANAGER** | HACCP_*, TEMPERATURE_LOG_* | Qualitätsbeauftragter |
|
||||||
|
| **QUALITY_INSPECTOR** | TEMPERATURE_LOG_*, GOODS_INSPECTION_* | QM-Mitarbeiter |
|
||||||
|
| **PROCUREMENT_MANAGER** | PURCHASE_ORDER_*, SUPPLIER_* | Einkaufsleiter |
|
||||||
|
| **WAREHOUSE_WORKER** | STOCK_*, INVENTORY_COUNT_* | Lagermitarbeiter |
|
||||||
|
| **SALES_MANAGER** | ORDER_*, INVOICE_*, CUSTOMER_* | Verkaufsleiter |
|
||||||
|
| **SALES_STAFF** | ORDER_READ/WRITE, CUSTOMER_READ | Verkaufsmitarbeiter |
|
||||||
|
|
||||||
|
### AuthorizationPort (für andere BCs)
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Typsichere, fachliche Authorization - kein direkter Zugriff auf User/Roles!
|
||||||
|
public interface AuthorizationPort {
|
||||||
|
boolean can(Action action);
|
||||||
|
void assertCan(Action action);
|
||||||
|
boolean can(Action action, ResourceId resource);
|
||||||
|
void assertCan(Action action, ResourceId resource);
|
||||||
|
ActorId currentActor();
|
||||||
|
Optional<BranchId> currentBranch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Beispiel: Production BC
|
||||||
|
public class CreateRecipe {
|
||||||
|
private final AuthorizationPort authPort;
|
||||||
|
|
||||||
|
public Result<ApplicationError, RecipeDTO> execute(CreateRecipeCommand cmd) {
|
||||||
|
authPort.assertCan(ProductionAction.RECIPE_WRITE);
|
||||||
|
// Business logic...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Unit Tests
|
||||||
|
mvn test
|
||||||
|
|
||||||
|
# Integration Tests
|
||||||
|
mvn verify
|
||||||
|
|
||||||
|
# Test Coverage
|
||||||
|
mvn clean verify jacoco:report
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Proprietary - Effigenix GmbH
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
- Project Lead: sebi@effigenix.com
|
||||||
|
- Architecture: DDD + Clean Architecture
|
||||||
|
- Documentation: /docs/mvp/
|
||||||
13
backend/TODO.md
Normal file
13
backend/TODO.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
Welle 1 (sofort starten):
|
||||||
|
1. ✅ User Management BC implementieren
|
||||||
|
2. ✅ Master Data BC implementieren (Artikel, Lieferanten, Kunden)
|
||||||
|
|
||||||
|
Welle 2 (parallel):
|
||||||
|
3. ✅ Inventory BC implementieren (Basis: 8.1-8.3)
|
||||||
|
4. ✅ Document Archive BC (Basis: 12.1-12.2) - parallel zu Inventory
|
||||||
|
|
||||||
|
- [x] Liquibase statt Flyway
|
||||||
|
- [x] Package Struktur gemäß DDD-model skill, ddd-implementer fragen wegen refactor?
|
||||||
|
- [ ] ActionToPermissionMapper, warum unterschiedliches Vorgehen if/else vs. switch/case
|
||||||
|
- [ ] Nix Shell für manuelles Testing mit Postgres sowie für Migrationstests
|
||||||
292
backend/docs/tickets/001-code-review-usermanagement.md
Normal file
292
backend/docs/tickets/001-code-review-usermanagement.md
Normal file
|
|
@ -0,0 +1,292 @@
|
||||||
|
# Ticket 001 – Code Review: User Management Bounded Context
|
||||||
|
|
||||||
|
**Datum:** 2026-02-17
|
||||||
|
**Commit:** `ec9114a` – feat: add Spring Boot ERP application with user management domain
|
||||||
|
**Status:** Offen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kritisch
|
||||||
|
|
||||||
|
### K1 – Aggregates sind nicht immutable
|
||||||
|
|
||||||
|
**Betroffene Dateien:**
|
||||||
|
- `domain/usermanagement/User.java` (Zeilen 22–29)
|
||||||
|
- `domain/usermanagement/Role.java` (Zeilen 18–21)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Felder in `User` und `Role` sind nicht `final`. Business-Methoden wie `lock()`, `unlock()`, `updateEmail()`, `changePassword()` mutieren den internen State direkt statt neue Instanzen oder `Result` zurückzugeben. Die interne `roles`-Collection in `User` (Zeile 25) ist mutable und wird per `assignRole()`/`removeRole()` in-place modifiziert.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Alle Felder `final` machen
|
||||||
|
- Business-Methoden geben `Result<UserError, User>` zurück (Copy-on-Write)
|
||||||
|
- Interne Collections nur über defensive Kopien exponieren
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### K2 – Fehlende Command-Validierung im Application Layer
|
||||||
|
|
||||||
|
**Betroffene Dateien:**
|
||||||
|
- `application/usermanagement/command/CreateUserCommand.java`
|
||||||
|
- `application/usermanagement/command/UpdateUserCommand.java`
|
||||||
|
- `application/usermanagement/command/AuthenticateCommand.java`
|
||||||
|
- `application/usermanagement/command/ChangePasswordCommand.java`
|
||||||
|
- `application/usermanagement/command/AssignRoleCommand.java`
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Alle Commands sind reine Records ohne jegliche Validierung. Wird der Application Layer außerhalb des HTTP-Kontexts aufgerufen (z.B. Scheduled Jobs, Message Queues), gibt es keine Input-Checks. Die Validierung liegt ausschließlich in den Web-DTOs (`@Valid`-Annotationen).
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Self-validating Commands mit Factory-Methoden, die `Result` zurückgeben
|
||||||
|
- Oder Validierung als ersten Schritt im Use Case
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### K3 – Authorization fehlt im Application Layer
|
||||||
|
|
||||||
|
**Betroffene Dateien:**
|
||||||
|
- `application/usermanagement/CreateUser.java`
|
||||||
|
- `application/usermanagement/LockUser.java`
|
||||||
|
- `application/usermanagement/AssignRole.java`
|
||||||
|
- (alle Use Cases betroffen)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
`AuthorizationPort` wird im Application Layer nicht verwendet. Autorisierung liegt ausschließlich auf `@PreAuthorize` im Controller. Bei Non-HTTP-Aufrufen wird die Authorization komplett umgangen.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- `AuthorizationPort.can(actorId, action)` als ersten Check in jedem Use Case aufrufen
|
||||||
|
- Bei fehlender Berechtigung `Result.failure(UserError.Unauthorized(...))` zurückgeben
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### K4 – Token-Blacklist Memory Leak
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `infrastructure/security/JwtSessionManager.java` (Zeilen 35–36, 164–168)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Die Token-Blacklist nutzt `ConcurrentHashMap.newKeySet()` ohne TTL oder Cleanup. Bei laufendem Server wächst die Blacklist unbegrenzt. Nach Server-Restart ist die Blacklist leer – ausgeloggte Tokens werden wieder gültig. Funktioniert nicht in Cluster-Deployments.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Redis-basierte Blacklist mit TTL = Token-Expiration
|
||||||
|
- Oder In-Memory mit Scheduled Cleanup abgelaufener Tokens
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### K5 – Audit-Bug in ChangePassword
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `application/usermanagement/ChangePassword.java` (Zeile 52)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Im Fehlerfall wird `AuditEvent.PASSWORD_CHANGED` geloggt statt `PASSWORD_CHANGE_FAILED`. Falsche Audit-Events verfälschen das Security-Monitoring.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Korrektes Event `PASSWORD_CHANGE_FAILED` im Fehlerfall loggen
|
||||||
|
- Audit-Events auf Success/Failure-Pfade prüfen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mittel
|
||||||
|
|
||||||
|
### M1 – Fehlende Status-Transitionen-Validierung
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `domain/usermanagement/User.java` (Zeilen 118–132)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
`lock()`, `unlock()`, `deactivate()`, `activate()` validieren den aktuellen Status nicht. Invalide Übergänge sind möglich (z.B. einen inaktiven User sperren). Keine Idempotenz-Checks.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- State-Machine oder explizite Guards für erlaubte Übergänge
|
||||||
|
- `Result<UserError, User>` als Rückgabetyp
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M2 – Inkonsistente Result-Nutzung in Domain-Methoden
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `domain/usermanagement/User.java` (Zeilen 106–159)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Manche Business-Methoden geben `Result` zurück (`assignRole()`, `changePassword()`), andere `void` (`removeRole()`, `lock()`, `unlock()`, `updateBranch()`, `updateLastLogin()`). Inkonsistentes API-Design.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Alle Business-Methoden einheitlich auf `Result<UserError, User>` umstellen (siehe K1)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M3 – RemoveRole bricht Command-Pattern
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `application/usermanagement/RemoveRole.java` (Zeile 44)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Einziger Use Case, der Raw-Parameter statt eines Command-Objekts akzeptiert:
|
||||||
|
```java
|
||||||
|
public Result<UserError, UserDTO> execute(String userId, RoleName roleName, ActorId performedBy)
|
||||||
|
```
|
||||||
|
Alle anderen Use Cases nutzen Command-Records.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- `RemoveRoleCommand` Record erstellen und in `execute()` verwenden
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M4 – Refresh Token nicht implementiert
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `infrastructure/security/JwtSessionManager.java` (Zeilen 141–144)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
`refreshSession()` wirft `UnsupportedOperationException`. User-Status (gesperrt/gelöscht) wird beim Token-Refresh nicht geprüft.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Implementierung: User laden, Status verifizieren, neues Token-Paar ausstellen
|
||||||
|
- Altes Refresh-Token auf Blacklist setzen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M5 – Kein Rate Limiting auf Login-Endpoint
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `infrastructure/usermanagement/web/controller/AuthController.java` (Zeile 90)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
`/api/auth/login` hat keinen Brute-Force-Schutz. Angreifer können Credentials unbegrenzt durchprobieren.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Rate Limiting per IP/Username (z.B. Bucket4j, Spring Cloud Gateway)
|
||||||
|
- Account-Lockout nach N fehlgeschlagenen Versuchen
|
||||||
|
- Exponentielles Backoff
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M6 – CORS deaktiviert statt konfiguriert
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `infrastructure/security/SecurityConfig.java` (Zeile 69)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
CORS ist komplett deaktiviert (`AbstractHttpConfigurer::disable`). TODO-Kommentar im Code, aber keine Konfiguration. Browser-basierte Clients können keine Cross-Origin-Requests senden.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- CORS mit expliziten Allowed Origins, Methods und Headers konfigurieren
|
||||||
|
- Environment-abhängig (Dev: localhost, Prod: spezifische Domain)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M7 – Error Response Information Disclosure
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `infrastructure/security/SecurityConfig.java` (Zeilen 100–113)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Custom Exception Handler gibt `authException.getMessage()` direkt im JSON-Response zurück. Kann interne Implementierungsdetails exponieren.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Generische Fehlermeldungen für Authentication/Authorization Failures
|
||||||
|
- Details nur ins Server-Log schreiben
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Niedrig
|
||||||
|
|
||||||
|
### N1 – Swagger UI öffentlich zugänglich
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `infrastructure/security/SecurityConfig.java` (Zeile 83)
|
||||||
|
- `application.yml` (Zeile 56)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Swagger UI ist standardmäßig ohne Authentifizierung erreichbar. Exponiert alle API-Endpoints mit Parametern.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- In Production deaktivieren oder hinter Authentication legen
|
||||||
|
- Spring-Profile nutzen: `springdoc.swagger-ui.enabled=${SWAGGER_ENABLED:false}`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### N2 – Default-Admin Credentials in Seed-Daten
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `db/changelog/changes/004-seed-admin-user.sql` (Zeilen 2–3, 26)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Admin-Credentials `admin/admin123` stehen als Kommentar in der SQL-Datei und als Table-Comment in der Datenbank. Bei versehentlicher DB-Exposure sind Credentials sofort sichtbar.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Seed-Daten nur in Dev-Profil ausführen
|
||||||
|
- Table-Comment mit Credentials entfernen
|
||||||
|
- Production: Admin über Environment-Variablen oder Init-Script bootstrappen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### N3 – CreateUser erlaubt User ohne Rollen
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `application/usermanagement/CreateUser.java` (Zeile 76)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Wenn `cmd.roleNames()` leer ist, wird ein User ohne Rollen erstellt. Kein Check, ob mindestens eine Rolle zugewiesen wird.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Validierung in Command oder Use Case: mindestens eine Rolle erforderlich
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### N4 – Falscher Error-Typ in Role.addPermission()
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `domain/usermanagement/Role.java` (Zeile 70)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Bei `null`-Permission wird `UserError.NullRole()` verwendet – semantisch falsch.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Eigenen Error-Typ verwenden oder `IllegalArgumentException` (da interner Programmierfehler)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### N5 – Fehlende Uniqueness-Validierung im Domain Layer
|
||||||
|
|
||||||
|
**Betroffene Datei:**
|
||||||
|
- `domain/usermanagement/User.java` (Zeilen 64–69)
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Username- und Email-Uniqueness wird nur im Application Layer geprüft (Repository-Abfrage). Der Domain Layer dokumentiert diese Invariante nicht explizit.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Domain Service für Uniqueness-Checks oder explizite Dokumentation als Application-Layer-Invariante
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### N6 – Repetitiver Switch-Pattern in Use Cases
|
||||||
|
|
||||||
|
**Betroffene Dateien:**
|
||||||
|
- Alle Use Cases im Application Layer
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
Das Pattern `switch (repository.method()) { case Failure f -> ...; case Success s -> ...; }` wiederholt sich ~30+ Mal. Hoher Boilerplate-Anteil.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- `Result.flatMap()` und `Result.mapError()` konsequent nutzen
|
||||||
|
- Helper-Methode für Repository-Error → UserError Mapping
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checkliste Production-Readiness
|
||||||
|
|
||||||
|
- [ ] K1: Aggregates immutable machen
|
||||||
|
- [ ] K2: Command-Validierung im Application Layer
|
||||||
|
- [ ] K3: AuthorizationPort in Use Cases integrieren
|
||||||
|
- [ ] K4: Token-Blacklist mit TTL/Redis ersetzen
|
||||||
|
- [ ] K5: Audit-Bug in ChangePassword fixen
|
||||||
|
- [ ] M4: Refresh Token implementieren
|
||||||
|
- [ ] M5: Rate Limiting einrichten
|
||||||
|
- [ ] M6: CORS konfigurieren
|
||||||
|
- [ ] N1: Swagger in Production absichern
|
||||||
|
- [ ] N2: Seed-Daten Production-safe machen
|
||||||
|
- [ ] JWT_SECRET, DB_PASSWORD als Environment-Variablen setzen
|
||||||
|
- [ ] HTTPS/TLS konfigurieren
|
||||||
|
- [ ] Log-Aggregation und Security-Monitoring aufsetzen
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue