mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 12:29:36 +01:00
init
This commit is contained in:
commit
4e448afa57
19 changed files with 4391 additions and 0 deletions
163
docs/mvp/ddd/07-bestandsfuehrungs-kontext.md
Normal file
163
docs/mvp/ddd/07-bestandsfuehrungs-kontext.md
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
# 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!
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue