1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 14:09:34 +01:00
effigenix/backend/docs/mvp/ddd/07-bestandsfuehrungs-kontext.md
Sebastian Frick c2c48a03e8 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/
2026-02-17 22:08:51 +01:00

163 lines
5.3 KiB
Markdown

# Bestandsführungs-Kontext - Detailliertes Domain Model
**Bounded Context:** Bestandsführung
**Domain-Typ:** KERN
**Verantwortung:** Chargen-basierte Bestandsführung, Rückverfolgbarkeit, MHD-Tracking
---
## Aggregate
### 1. Bestand (Aggregate Root)
**Struktur:**
```
Bestand (Aggregate Root)
├── BestandId (Wertobjekt)
├── ArtikelId (Wertobjekt) - Referenz zu Stammdaten
├── LagerortId (Wertobjekt)
├── FilialId (Wertobjekt)
├── Bestandsmenge (Wertobjekt: Menge) - Aktueller Gesamtbestand
├── Chargen[] (Entität) - Chargen-spezifischer Bestand (KRITISCH!)
│ ├── ChargenId (Wertobjekt) - ProduktionsChargenId ODER LieferantenChargenId
│ ├── ChargenTyp (Wertobjekt: PRODUZIERT | EINGEKAUFT)
│ ├── Menge (Wertobjekt)
│ ├── Verfallsdatum (Wertobjekt: MHD)
│ ├── EingegangenenAm (Wertobjekt)
│ └── Status (Wertobjekt: VERFUEGBAR | RESERVIERT | ABGELAUFEN | VERKAUFT)
├── MinimalBestandsmenge (Wertobjekt) - Für Warnungen bei niedrigem Bestand
└── Nachbestellpunkt (Wertobjekt) - Auslöser für Beschaffung
Invarianten:
- Bestandsmenge = SUMME(Chargen.Menge wo Status = VERFUEGBAR)
- Kann nicht mehr abheben als verfügbar
- FEFO durchgesetzt: Ältestes Verfallsdatum zuerst
- Negativer Bestand nicht erlaubt
- Chargen mit Verfallsdatum < HEUTE müssen Status = ABGELAUFEN haben
```
**Geschäftsmethoden:**
```java
// Bestand hinzufügen
public Result<BestandFehler, Void> hinzufuegen(
ChargenId chargenId,
ChargenTyp chargenTyp,
Menge menge,
LocalDate verfallsdatum
);
// Bestand abheben (mit FEFO)
public Result<BestandFehler, List<ChargenAbhebung>> abheben(Menge menge);
// Charge reservieren
public Result<BestandFehler, Void> reservieren(ChargenId chargenId, Menge menge);
// Abgelaufene Chargen markieren
public Result<BestandFehler, List<ChargenId>> markiereAbgelaufeneChargen();
// Abfragemethoden
public Menge verfuegbareMenge();
public List<Charge> findeChargenNachFEFO(); // Sortiert nach Verfallsdatum
public boolean istUnterMinimalBestand();
```
**Domänen-Events:**
```java
BestandUnterMinimum(ArtikelId, FilialId, Menge)
ChargeLaeuftDemnaechstAb(ChargenId, ArtikelId, Verfallsdatum)
```
---
### 2. Bestandsbewegung (Aggregate Root)
**Event Sourcing Kandidat!**
**Struktur:**
```
Bestandsbewegung (Aggregate Root)
├── BestandsbewegungId (Wertobjekt)
├── ArtikelId (Wertobjekt)
├── ChargenId (Wertobjekt) - KRITISCH für Rückverfolgbarkeit
├── BewegungsTyp (Wertobjekt: WARENEINGANG | PRODUKTIONSAUSGANG | VERKAUF |
│ INTERFILIAL_TRANSFER | AUSSCHUSS | INVENTUR_KORREKTUR)
├── VonLagerort (Wertobjekt: LagerortId) - Null bei WARENEINGANG
├── ZuLagerort (Wertobjekt: LagerortId) - Null bei VERKAUF/AUSSCHUSS
├── VonFiliale (Wertobjekt: FilialId)
├── ZuFiliale (Wertobjekt: FilialId) - Für Interfilial-Transfers
├── Menge (Wertobjekt)
├── Bewegungsdatum (Wertobjekt: Zeitstempel)
├── DurchgefuehrtVon (Wertobjekt: BenutzerId)
├── Grund (Wertobjekt: für AUSSCHUSS/KORREKTUR)
├── Referenzdokument (Wertobjekt) - WareneingangId, ProduktionsauftragId, RechnungId
└── Rueckverfolgbarkeitskette (Wertobjekt) - Link zu vorgelagerten/nachgelagerten Bewegungen
Invarianten:
- Menge muss positiv sein
- WARENEINGANG: VonLagerort muss null sein
- VERKAUF oder AUSSCHUSS: ZuLagerort muss null sein
- INTERFILIAL_TRANSFER: VonFiliale != ZuFiliale
- AUSSCHUSS benötigt Grund
- Alle Bewegungen müssen eine Charge referenzieren
```
**Domänen-Events:**
```java
BestandsbewegungErfasst(BestandsbewegungId, ChargenId, BewegungsTyp)
```
---
## Repository-Schnittstellen
```java
public interface BestandRepository {
Result<RepositoryFehler, Void> speichern(Bestand bestand);
Result<RepositoryFehler, Bestand> findeNachArtikelUndLagerort(
ArtikelId artikelId,
LagerortId lagerortId,
FilialId filialId
);
Result<RepositoryFehler, List<Bestand>> findeUnterMinimalBestand();
}
public interface BestandsbewegungRepository {
Result<RepositoryFehler, Void> speichern(Bestandsbewegung bewegung);
Result<RepositoryFehler, List<Bestandsbewegung>> findeNachCharge(ChargenId chargenId);
Result<RepositoryFehler, List<Bestandsbewegung>> findeNachZeitraum(
LocalDate von, LocalDate bis
);
}
```
---
## Rückverfolgbarkeits-Beispiel
```java
// Beispiel: Rückruf wegen kontaminierter Rohstoff-Charge
// 1. Finde alle Bestandsbewegungen für Lieferanten-Charge
List<Bestandsbewegung> wareneingang = repository.findeNachCharge(
ChargenId.von("SUPPLIER-BATCH-12345")
);
// 2. Finde Produktionschargen, die diesen Rohstoff verwendet haben
List<ChargenId> produktionschargen = wareneingang.stream()
.filter(b -> b.bewegungsTyp() == BewegungsTyp.PRODUKTIONSAUSGANG)
.map(b -> b.referenzdokument().produktionsChargenId())
.toList();
// 3. Finde alle Verkäufe dieser Produktionschargen
List<Bestandsbewegung> verkaeufe = produktionschargen.stream()
.flatMap(chargeId -> repository.findeNachCharge(chargeId).stream())
.filter(b -> b.bewegungsTyp() == BewegungsTyp.VERKAUF)
.toList();
// 4. Extrahiere betroffene Kunden/Rechnungen
List<RechnungId> betroffeneRechnungen = verkaeufe.stream()
.map(b -> b.referenzdokument().rechnungId())
.toList();
// → Rückruf kann durchgeführt werden!
```