# 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, 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` | | Create-Draft | `{Noun}Draft` | `SupplierDraft` | | Update-Draft | `{Noun}UpdateDraft` | `SupplierUpdateDraft` | | 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` | ## EntityDraft-Pattern Für Aggregate mit komplexer VO-Konstruktion (Address, ContactInfo, PaymentTerms u.ä.) gilt: Der Application Layer baut **keine** VOs – er erzeugt einen **Draft-Record** mit rohen Strings und übergibt ihn ans Aggregate. Das Aggregate orchestriert Validierung und VO-Konstruktion intern. ```java // Application Layer – nur Daten weitergeben, kein VO-Wissen var draft = new SupplierDraft(cmd.name(), cmd.phone(), ...); switch (Supplier.create(draft)) { ... } // Domain Layer – validiert intern, gibt Result zurück public static Result create(SupplierDraft draft) { ... } public Result update(SupplierUpdateDraft draft) { ... } ``` **Regeln:** - Pflichtfelder: non-null im Draft-Record - Optionale VOs (z.B. Address, PaymentTerms): `null`-Felder → VO wird nicht konstruiert - Primitive `int` → `Integer` wenn das Feld optional/nullable sein muss - Einzelne `updateXxx(VO)`-Methoden entfallen → ersetzt durch ein `update({Noun}UpdateDraft)` - Uniqueness-Check bleibt im Application Layer (Repository-Concern), nach `Aggregate.create()` - Invarianten-Kommentar im Aggregat aktuell halten ## Error Handling Funktional via `Result` (`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`. ## GitHub CLI (`gh`) > **WICHTIG: `gh`-Befehle NIEMALS ohne ausdrückliche Zustimmung des Users ausführen!** > Issues anlegen, PRs erstellen, Labels/Milestones verwalten – all das verändert den Remote-Zustand und ist schwer rückgängig zu machen. IMMER vorher fragen. `gh` ist nicht direkt installiert, sondern wird über Nix bereitgestellt: ```bash # Alle gh-Befehle mit diesem Prefix ausführen: nix shell nixpkgs#gh -c gh # Auth prüfen nix shell nixpkgs#gh -c gh auth status # Repo-Info nix shell nixpkgs#gh -c gh repo view --json name,owner ``` ### Milestones (via API, kein `gh milestone`-Subcommand) ```bash # Auflisten nix shell nixpkgs#gh -c gh api repos/{owner}/{repo}/milestones # Erstellen nix shell nixpkgs#gh -c gh api repos/{owner}/{repo}/milestones \ -f title="Name" -f state=open -f description="Beschreibung" ``` ### Labels ```bash # Auflisten nix shell nixpkgs#gh -c gh label list --limit 100 # Erstellen nix shell nixpkgs#gh -c gh label create "label-name" --color "0E8A16" --description "Beschreibung" ``` ### Issues ```bash # Erstellen (Body via HEREDOC für Multiline) nix shell nixpkgs#gh -c gh issue create \ --title "Titel" \ --milestone "Milestone Name" \ --label "label1,label2" \ --body "$(cat <<'EOF' Issue-Body mit Markdown... EOF )" # Auflisten (nach Milestone, State, Label) nix shell nixpkgs#gh -c gh issue list --milestone "Name" --limit 50 nix shell nixpkgs#gh -c gh issue list --milestone "Name" --state open --json number,title,labels \ --jq '.[] | "\(.number)\t\(.title)"' | sort -n # Details anzeigen nix shell nixpkgs#gh -c gh issue view 3 --json title,body,projectItems # Kommentieren nix shell nixpkgs#gh -c gh issue comment 3 --body "Kommentar-Text" # Schließen (mit optionalem Kommentar) nix shell nixpkgs#gh -c gh issue close 3 --comment "$(cat <<'EOF' Abschluss-Kommentar mit Markdown... EOF )" ``` ### GitHub Projects (Projektstatus ändern) ```bash # Projekt-IDs: # Project: PVT_kwHOAvvITc4BPlrt # Status-Field: PVTSSF_lAHOAvvITc4BPlrtzg99FPE # Status-Optionen: # Todo: f75ad846 # In Progress: 47fc9ee4 # Done: 98236657 # Test: d755b468 # Completed: b7405d2f # Item-ID eines Issues im Projekt ermitteln (z.B. Issue #3) nix shell nixpkgs#gh -c gh api graphql -f query='{ node(id: "PVT_kwHOAvvITc4BPlrt") { ... on ProjectV2 { items(first: 50) { nodes { id content { ... on Issue { number title } } } } } } }' \ --jq '.data.node.items.nodes[] | select(.content.number == 3) | .id' # Status ändern (Item-ID einsetzen) nix shell nixpkgs#gh -c gh project item-edit \ --project-id PVT_kwHOAvvITc4BPlrt \ --id \ --field-id PVTSSF_lAHOAvvITc4BPlrtzg99FPE \ --single-select-option-id 47fc9ee4 \ --format json ``` ## 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