mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 15:29:34 +01:00
169 lines
6 KiB
Markdown
169 lines
6 KiB
Markdown
# 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` |
|
||
| 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<SupplierError, Supplier> create(SupplierDraft draft) { ... }
|
||
public Result<SupplierError, Void> 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<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`.
|
||
|
||
## 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 <command>
|
||
|
||
# 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 <ITEM_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
|