1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 15:29:34 +01:00
This commit is contained in:
Sebastian Frick 2026-02-17 08:10:34 +01:00
commit 4e448afa57
19 changed files with 4391 additions and 0 deletions

View file

@ -0,0 +1,83 @@
# DDD Domain Model - Effigenix Fleischerei-ERP
**Erstellt:** 2026-02-17
**Technologie:** Java 21+
**Architektur:** Domain-Driven Design + Clean Architecture
## Überblick
Dieses Dokument beschreibt das Domain-Driven Design (DDD) Modell für das Effigenix Fleischerei-ERP System.
## Projektziele
1. **ERP-Grundlagen** für Stammdatenverwaltung, Auftragsabwicklung und Fakturierung
2. **HACCP-Compliance** - Vollständiges QM-System als Kernmehrwert für kleine Betriebe
3. **Rezeptur-Management** - Mehrstufige Rezepturen mit Chargen-Tracking und Rückverfolgbarkeit
4. **Produktionsplanung** - Verkaufstrends-Analyse zur Vermeidung von Überproduktion
5. **Mehrfilialen-Unterstützung** - Zentrale Produktion mit Belieferung mehrerer Filialen
## DDD-Phasen
### Phase 0: Technologie-Auswahl ✅
- **Sprache:** Java 21+
- **Patterns:** Result Types, Sealed Interfaces, Pattern Matching
- **Architektur:** Clean Architecture (Domain → Application → Infrastructure)
### Phase 1: Domain Discovery ✅
- **Subdomain-Klassifizierung:** Core, Supporting, Generic
- **DDD-Investment:** Volles DDD für 7 Core Domains
### Phase 2: Bounded Contexts ✅
- **11 Bounded Contexts** identifiziert
- **Context Map** mit Beziehungen erstellt
- **Ubiquitous Language** für jeden BC definiert
### Phase 3: Tactical Modeling ✅
- **Aggregates** identifiziert für alle Core BCs
- **Entities** und **Value Objects** definiert
- **Invarianten** dokumentiert
### Phase 4: Invarianten (TODO)
- Detaillierte Invarianten-Dokumentation
- Enforcement Points definieren
### Phase 5: Code-Generierung (TODO)
- Java-Code aus Aggregates generieren
- Repository Interfaces
- Use Cases
### Phase 6: Validation (TODO)
- DDD-Rules-Checklist durchgehen
- Clean Architecture Compliance prüfen
## Dokumentationsstruktur
```
docs/mvp/ddd/
├── 00-overview.md (dieses Dokument)
├── 01-domain-classification.md
├── 02-bounded-contexts.md
├── 03-ubiquitous-language.md
├── 04-production-bc.md
├── 05-quality-bc.md
├── 06-labeling-bc.md
├── 07-inventory-bc.md
├── 08-procurement-bc.md
├── 09-filiales-bc.md
└── 10-supporting-bcs.md
```
## Nächste Schritte
1. ✅ Bounded Contexts dokumentieren
2. ✅ Aggregates für Core BCs modellieren
3. ⏳ Invarianten detailliert ausarbeiten
4. ⏳ Java-Code generieren
5. ⏳ Validierung durchführen
## Referenzen
- Feature-Definition: `docs/mvp/2026-02-15-fleischerei-erp-feature-definition.md`
- Abhängigkeitsanalyse: `docs/mvp/2026-02-16-abhangigkeitsanalyse-fleischerei-erp.md`
- Java Style Guide: `.claude/skills/ddd-model/languages/java/style-guide.md`
- DDD Rules: `.claude/skills/ddd-model/rules/ddd-rules.md`

View file

@ -0,0 +1,171 @@
# Domain-Klassifizierung
**Datum:** 2026-02-17
## Subdomain-Typen und DDD-Investment
Die Klassifizierung bestimmt den DDD-Aufwand:
| Subdomain Type | DDD Investment | Patterns |
|----------------|----------------|----------|
| **Core** | Voll | Aggregates, Domain Events, Domain Services, CQRS |
| **Supporting** | Vereinfacht | Aggregates, Value Objects, einfache Services |
| **Generic** | Minimal | CRUD, Transaction Script |
---
## CORE DOMAIN (7 Bereiche)
### 1. HACCP/QM & Compliance
**Warum Core:**
- ✅ **Kernmehrwert** für kleine Betriebe - Wettbewerbsvorteil
- ✅ **Komplexe Invarianten** - Kritische Kontrollpunkte, Grenzwerte
- ✅ **Compliance-kritisch** - Gesetzliche Pflicht, Audit-Vorbereitung
- ✅ **Geschäftskritisch** - Ohne HACCP keine Betriebserlaubnis
**Epics:** 3 (QM & Compliance)
---
### 2. Rezeptur-Management
**Warum Core:**
- ✅ **Mehrstufige Strukturen** - Rezepte können Zwischenprodukte enthalten
- ✅ **Chargen-Tracking** - Lückenlose Rückverfolgbarkeit (gesetzlich!)
- ✅ **Komplexe Berechnungen** - Ausbeute, Nährwerte, Kosten
- ✅ **Geschäftskritisch** - Basis für Produktion, Deklaration, Kalkulation
**Epics:** 4 (Rezeptur- & Stammdatenmanagement)
---
### 3. Produktionsplanung
**Warum Core:**
- ✅ **Verkaufstrends-Analyse** - Datenbasierte Planung (Kernmehrwert!)
- ✅ **Vermeidung von Überproduktion** - Echtes Geschäftsproblem lösen
- ✅ **Komplexe Bedarfsberechnung** - Rezeptur + Ausbeute + Lagerbestand
- ✅ **Wettbewerbsvorteil** - Viele Betriebe planen nach "Bauchgefühl"
**Epics:** 7 (Produktionsplanung & -steuerung)
---
### 4. Deklaration & Nährwerte
**Warum Core:**
- ✅ **Automatische Berechnung** aus Rezepturen - Komplexe Logik
- ✅ **Rechtskonforme Etiketten** - Compliance-kritisch
- ✅ **Allergenkennzeichnung** - Fehler können lebensbedrohlich sein
- ✅ **Qualitätssiegel-Management** - Bio/Regional-Nachweis
**Epics:** 6 (Kundenanforderungen & Deklaration)
---
### 5. Inventory (Bestandsführung)
**Warum Core:**
- ✅ **Chargen-Tracking** - Gesetzlich vorgeschrieben für Rückverfolgbarkeit
- ✅ **Lückenlose Traceability** - Rohstoff-Charge → Produktion → Verkauf
- ✅ **MHD-Tracking mit FEFO** - Komplexe Logik, nicht Standard-WaWi
- ✅ **Basis für HACCP** - Ohne korrekte Chargen-Führung keine Compliance
- ✅ **Existenziell bei Rückrufen** - Welche Produkte betroffen?
**Epics:** 8 (Bestandsführung & Inventur)
---
### 6. Procurement (Beschaffung)
**Warum Core:**
- ✅ **Bedarfsplanung** aus Produktionsplan - Business-Logik, kein CRUD
- ✅ **Wareneingangskontrolle** - Integraler Teil von HACCP
- ✅ **Lieferanten-Qualitätsmanagement** - Fließt in Compliance ein
- ✅ **Chargen-Zuordnung** - Start der Rückverfolgbarkeitskette
- ✅ **Compliance-kritisch** - Fehler gefährden HACCP
**Epics:** 5 (Beschaffung & Rohstoffmanagement)
---
### 7. Filiales (Mehrfilialen)
**Warum Core:**
- ✅ **Zentrale Produktion mit Belieferung** - Komplexe Koordination
- ✅ **Interfilial-Transfers** - Chargen-Tracking über Standorte
- ✅ **Filial-übergreifende Produktionsplanung** - Bedarf aggregieren
- ✅ **Langfristige Strategie** - Nach MVP wird Mehrfilialen wichtig
- ✅ **Wettbewerbsvorteil** - Skalierungsfähigkeit
**Epics:** 11 (Mehrfilialen-Management)
---
## SUPPORTING DOMAIN (3 Bereiche)
### Master Data (Stammdaten)
**Warum Supporting:**
- Wichtig, aber **keine komplexe Geschäftslogik**
- Artikel, Lieferanten, Kunden = Standard-CRUD
- **Unterstützt** Core Domains (Production, Procurement, Sales)
**Epics:** 1.1-1.3 (Artikelstamm, Lieferantenstamm, Kundenstamm)
---
### Sales (Verkauf)
**Warum Supporting:**
- Auftragserfassung, Rechnungen, Lieferscheine = **Standard-ERP**
- Keine komplexe Geschäftslogik (im Vergleich zu Production/HACCP)
- **Unterstützt** Geschäftsprozess, aber kein Wettbewerbsvorteil
**Epics:** 1.4-1.5 (Auftragserfassung, Lieferschein & Rechnung)
---
### Scale Integration (Waagen/Kassen)
**Warum Supporting:**
- **Technische Integration**, keine Geschäftslogik
- Wichtig für Workflow, aber **kein Kernmehrwert**
- **Unterstützt** Verkauf und Etikettierung
**Epics:** 2 (Waagen/Kassen-Anbindung)
---
## GENERIC SUBDOMAINS (3 Bereiche)
### Reporting
**Warum Generic:**
- Standardfunktionalität - **jedes ERP braucht Reports**
- Keine Differenzierung zum Wettbewerb
- CRUD über aggregierte Daten
**Epics:** 9 (Reporting & Auswertungen)
---
### Document Archive (GoBD)
**Warum Generic:**
- **Gesetzliche Pflicht**, aber Standardfunktionalität
- Commodity - viele Standardlösungen verfügbar
- Keine Geschäftslogik, nur Speicherung + Retrieval
**Epics:** 12 (Dokumentenarchivierung & GoBD)
---
### User Management
**Warum Generic:**
- **Commodity** - jede Software braucht Benutzerverwaltung
- Vordefinierte Rollen, keine komplexe Logik
- Standardlösung (Spring Security, Keycloak, etc.)
**Epics:** 10 (Benutzerverwaltung & Rollen)
---
## Zusammenfassung
| Kategorie | Anzahl | DDD-Aufwand | Begründung |
|-----------|--------|-------------|------------|
| **Core** | 7 | Hoch | Wettbewerbsvorteil, komplexe Logik, Compliance-kritisch |
| **Supporting** | 3 | Mittel | Wichtig, aber Standard-ERP-Funktionalität |
| **Generic** | 3 | Niedrig | Commodity, CRUD, keine Differenzierung |
**Gesamt:** 13 Bereiche aus Feature-Definition abgedeckt

View file

@ -0,0 +1,203 @@
# Bounded Contexts (Kontextgrenzen) & Context Map
**Datum:** 2026-02-17
## Context Map (Kontextkarte)
```mermaid
graph TB
subgraph CORE["⚡ KERN-DOMÄNE"]
Produktion["<b>Produktion</b><br/>Rezept, Charge,<br/>Ausbeute, Produktionsauftrag"]
Qualitaet["<b>Qualität</b><br/>(HACCP/QM)<br/>Temperaturprotokolle,<br/>Reinigung, Schulung,<br/>Wartung"]
Deklaration["<b>Deklaration</b><br/>Allergene, Nährwerte,<br/>Etiketten, Qualitätssiegel"]
Bestand["<b>Bestandsführung</b><br/>Lagerbestände, Chargen-Tracking,<br/>Rückverfolgbarkeit, MHD, FEFO,<br/>Lagerorte"]
Beschaffung["<b>Beschaffung</b><br/>Bedarfsplanung,<br/>Bestellungen,<br/>Wareneingang,<br/>Qualitätsprüfung"]
Filialen["<b>Filialen</b><br/>Standorte, Interfilial-Transfer,<br/>Zentrale Produktion,<br/>Verteilung"]
Produktion -->|Rezeptdaten| Deklaration
Produktion -->|Verbraucht/Produziert| Bestand
Qualitaet -->|Prüft| Bestand
Bestand -->|Beliefert| Beschaffung
Bestand -->|Transfers| Filialen
end
subgraph SUPPORTING["🔧 UNTERSTÜTZENDE DOMÄNE"]
Stammdaten["<b>Stammdaten</b><br/>Artikel, Kunden,<br/>Lieferanten"]
Verkauf["<b>Verkauf</b><br/>Aufträge, Rechnungen,<br/>Lieferscheine"]
Waagen["<b>Waagen-Integration</b><br/>Synchronisation,<br/>Bondaten, Etikettendruck"]
Stammdaten -->|Stellt Artikel bereit| Verkauf
Waagen -->|Bondaten| Verkauf
end
subgraph GENERIC["📊 GENERISCHE SUBDOMÄNEN"]
Reporting["<b>Reporting</b><br/>Analysen, Statistiken,<br/>Dashboards"]
Dokumentenarchiv["<b>Dokumentenarchiv</b><br/>(GoBD)<br/>Audit Trail,<br/>Revisionssicher"]
Benutzerverwaltung["<b>Benutzerverwaltung</b><br/>Rollen, Berechtigungen"]
end
Stammdaten -.->|Stellt Artikel bereit| CORE
Filialen -.->|Koordiniert Mehrfilialen| SUPPORTING
CORE -.->|Konsumiert Daten| GENERIC
SUPPORTING -.->|Konsumiert Daten| GENERIC
style CORE fill:#e1f5ff,stroke:#0066cc,stroke-width:3px
style SUPPORTING fill:#fff4e6,stroke:#ff9800,stroke-width:2px
style GENERIC fill:#f5f5f5,stroke:#666,stroke-width:1px
```
---
## Bounded Context Details (Kontextdetails)
### Kern-Domäne (7 BCs)
#### 1. Produktions-Kontext
- **Verantwortung:** Rezepturverwaltung, Produktionsplanung, Chargen-Erzeugung
- **Aggregate:** Rezept, Charge, Produktionsauftrag
- **Upstream:** Stammdaten (Artikelkatalog)
- **Downstream:** Deklaration (Rezeptdaten), Bestandsführung (Bestandsbewegungen)
#### 2. Qualitäts-Kontext (HACCP/QM)
- **Verantwortung:** HACCP-Compliance, Qualitätsmanagement, Audit-Vorbereitung
- **Aggregate:** Temperaturprotokoll, Reinigungsnachweis, Wareneingangsprüfung, Schulungsnachweis, Wartungsprotokoll
- **Upstream:** Beschaffung (Wareneingangsprüfung), Stammdaten (Lieferanten, Mitarbeiter)
- **Downstream:** Reporting (HACCP-Berichte), Dokumentenarchiv (Audit-Nachweise)
#### 3. Deklarations-Kontext
- **Verantwortung:** Automatische Berechnung von Nährwerten und Allergenen, Etikettengenerierung
- **Aggregate:** Produktetikett, Allergene-Matrix
- **Upstream:** Produktion (Rezeptdaten für Berechnungen)
- **Downstream:** Waagen-Integration (Etikettendruck)
#### 4. Bestandsführungs-Kontext
- **Verantwortung:** Chargen-basierte Bestandsführung, Rückverfolgbarkeit, MHD-Tracking
- **Aggregate:** Bestand, Bestandsbewegung
- **Upstream:** Produktion (Produktionsausstoß), Beschaffung (Wareneingang), Verkauf (verkaufte Artikel)
- **Downstream:** Beschaffung (Lagerbestände für Bedarfsplanung), Reporting (Bestandsberichte)
#### 5. Beschaffungs-Kontext
- **Verantwortung:** Bedarfsplanung, Bestellwesen, Wareneingangskontrolle, Lieferanten-QM
- **Aggregate:** Bestellung, Wareneingang, Bedarfsplan
- **Upstream:** Bestandsführung (aktueller Bestand), Produktion (Produktionsaufträge), Stammdaten (Lieferanten, Artikel)
- **Downstream:** Qualität (Wareneingangsprüfung), Bestandsführung (Bestandszugang)
#### 6. Filialen-Kontext
- **Verantwortung:** Mehrfilialen-Management, Interfilial-Transfers, zentrale Produktion
- **Aggregate:** Filiale, Interfilial-Transfer, Verteilungsplan
- **Upstream:** Alle Kern-BCs (koordiniert Mehrfilialen-Operationen)
- **Downstream:** Bestandsführung (Interfilial-Bestandsbewegungen), Produktion (filialspezifische Produktion)
---
### Supporting Domain (3 BCs)
#### 8. Master Data BC
- **Verantwortung:** Stammdatenverwaltung für Artikel, Lieferanten, Kunden
- **Aggregates:** Article, Supplier, Customer
- **Downstream:** Alle BCs nutzen Master Data als Referenz
#### 9. Sales BC
- **Verantwortung:** Auftragserfassung, Rechnungsstellung, Lieferscheine
- **Aggregates:** Order, Invoice, DeliveryNote
- **Upstream:** Master Data (customers, articles), Inventory (stock availability)
- **Downstream:** Inventory (sales deduct stock), Reporting (sales statistics)
#### 10. Scale Integration BC
- **Verantwortung:** Waagen/Kassen-Anbindung, Bondaten-Import, Etikettendruck
- **Aggregates:** ScaleSyncJob, BondDataImport
- **Upstream:** Master Data (article sync), Labeling (label templates)
- **Downstream:** Inventory (bond data → stock movements), Sales (sales data)
---
### Generic Subdomains (3 BCs)
#### 11. Reporting BC
- **Verantwortung:** Auswertungen, Statistiken, Dashboards
- **Upstream:** Alle BCs liefern Daten für Reports
- **Keine Aggregates** (Read-only, CQRS Query Side)
#### 12. Document Archive BC (GoBD)
- **Verantwortung:** Revisionssichere Dokumentenarchivierung
- **Aggregates:** Document, AuditLog
- **Upstream:** Quality (HACCP documents), Sales (invoices), Procurement (delivery notes)
#### 13. User Management BC
- **Verantwortung:** Benutzer, Rollen, Permissions
- **Aggregates:** User, Role
- **Downstream:** Alle BCs nutzen User Management für Autorisierung
---
## Context Relationships
### Partnership
- **Production ↔ Inventory:** Enge Zusammenarbeit bei Chargen-Tracking
- **Procurement ↔ Quality:** Wareneingangskontrolle gemeinsam
### Customer-Supplier
- **Production → Labeling:** Production liefert Rezeptdaten
- **Master Data → alle BCs:** Master Data liefert Stammdaten
### Conformist
- **Labeling → Production:** Labeling übernimmt Recipe-Struktur 1:1
- **Reporting → alle BCs:** Reporting passt sich an alle Datenmodelle an
### Anti-Corruption Layer
- **Scale Integration → Inventory:** ACL übersetzt Bondaten in StockMovements
- **FIBU Integration → Sales:** ACL übersetzt zu DATEV-Format
---
## Integration Patterns
### Event-Driven
- **Production.BatchCompleted** → Inventory (stock in)
- **Procurement.GoodsReceived** → Inventory (stock in)
- **Sales.ItemSold** → Inventory (stock out)
### Request-Response
- **Labeling → Production:** GET /recipes/{id} für Nährwertberechnung
- **Procurement → Inventory:** GET /stock/{articleId} für Bedarfsplanung
### Shared Database (Anti-Pattern, vermeiden!)
- ❌ Nicht verwenden - jeder BC hat eigene Datenbank
---
## Deployment Strategy
### Monolith First (MVP)
Alle BCs in einer Anwendung:
```
effigenix-erp/
├── domain/
│ ├── production/
│ ├── quality/
│ ├── labeling/
│ ├── inventory/
│ ├── procurement/
│ └── filiales/
├── application/
└── infrastructure/
```
### Microservices Later (nach MVP)
Extraktion von BCs in Services:
```
production-service
quality-service
inventory-service
procurement-service
filiales-service
master-data-service
sales-service
scale-integration-service
```
**Empfohlene Reihenfolge:**
1. Scale Integration (eigener Prozess wegen Hardware-Anbindung)
2. Reporting (Read-only, CQRS)
3. Inventory (hohe Last)
4. Production (geschäftskritisch, isolieren)

View file

@ -0,0 +1,233 @@
# Ubiquitous Language - Glossar
**Datum:** 2026-02-17
Diese Datei definiert die **Ubiquitous Language** für jeden Bounded Context. Diese Begriffe müssen im Code (Klassennamen, Methoden, Variablen) exakt so verwendet werden.
---
## Production BC
| Begriff (DE) | Begriff (EN) | Typ | Definition |
|--------------|--------------|-----|------------|
| Rezept | Recipe | Aggregate | Mehrstufige Anleitung zur Herstellung eines Produkts aus Rohstoffen und Zwischenprodukten |
| Zutat | Ingredient | Entity | Rohstoff oder Zwischenprodukt, das in einem Rezept verwendet wird |
| Ausbeute | Yield | Value Object | Verhältnis zwischen Input (Rohmaterial) und Output (Endprodukt) nach Verarbeitungsverlusten, in Prozent |
| Charge | Batch | Aggregate | Eindeutig identifizierte Produktionseinheit mit Datum, Rezept, Menge und verwendeten Rohstoffchargen |
| Produktionsauftrag | Production Order | Aggregate | Auftrag zur Herstellung einer bestimmten Menge eines Produkts nach Rezept |
| Rückverfolgbarkeit | Traceability | Concept | Lückenlose Dokumentation von Rohstoff-Charge → Produktions-Charge → Verkauf |
| Zwischenprodukt | Intermediate Product | Concept | Produkt, das in Rezept A hergestellt wird und als Zutat in Rezept B verwendet wird |
| Ausschuss | Waste | Value Object | Menge an Material, die während Produktion verloren geht oder unbrauchbar ist |
| Rezeptur-Version | Recipe Version | Value Object | Versionsnummer eines Rezepts, um Änderungen nachvollziehbar zu machen |
---
## Quality BC (HACCP/QM)
| Begriff (DE) | Begriff (EN) | Typ | Definition |
|--------------|--------------|-----|------------|
| HACCP | HACCP | Concept | Hazard Analysis Critical Control Points - systematische Präventivmaßnahmen für Lebensmittelsicherheit |
| Kritischer Kontrollpunkt | Critical Control Point (CCP) | Concept | Stelle im Prozess, an der Kontrolle notwendig ist, um Gefahren zu vermeiden |
| Temperaturprotokoll | Temperature Log | Aggregate | Dokumentierte Temperaturmessung an kritischem Punkt (Kühlraum, Theke) mit Grenzwerten |
| Reinigungsnachweis | Cleaning Record | Aggregate | Dokumentation einer durchgeführten Reinigung mit Datum, Person, Checkliste |
| Reinigungsplan | Cleaning Plan | Aggregate | Vordefinierter Plan mit Intervallen und Checklisten für Reinigungsaufgaben |
| Wareneingangskontrolle | Goods Receipt Inspection | Aggregate | Prüfung von Temperatur, MHD, Sichtkontrolle, Dokumenten bei Warenanlieferung |
| Schulungsnachweis | Training Record | Aggregate | Zertifikat oder Nachweis einer absolvierten Schulung (HACCP, Hygiene) mit Gültigkeitsdatum |
| Wartungsprotokoll | Maintenance Record | Aggregate | Dokumentation von Gerätewartungen (planmäßig oder Störung) mit Befund |
| Messwert | Measurement | Value Object | Gemessener Wert mit Einheit (z.B. Temperatur in °C) |
| Grenzwert | Critical Limit | Value Object | Minimal-/Maximalwert für CCP (z.B. Kühlraum: 2-7°C) |
| Abweichung | Deviation | Concept | Überschreitung eines Grenzwerts oder Nichteinhaltung eines Verfahrens |
| Korrekturmaßnahme | Corrective Action | Entity | Maßnahme zur Behebung einer Abweichung |
---
## Labeling BC
| Begriff (DE) | Begriff (EN) | Typ | Definition |
|--------------|--------------|-----|------------|
| Allergen | Allergen | Value Object | Einer der 14 EU-Hauptallergene, die kennzeichnungspflichtig sind |
| Spurenkennzeichnung | Trace Declaration | Value Object | "Kann Spuren von X enthalten" bei gemeinsamer Verarbeitung |
| Nährwerttabelle | Nutrition Facts Table | Entity | Rechtlich vorgeschriebene Angabe von Kalorien, Fett, Eiweiß, etc. pro 100g |
| Qualitätssiegel | Quality Label | Value Object | Bio, Regional, Tierwohl-Zertifizierung |
| Herkunftskennzeichnung | Origin Labeling | Value Object | Angabe des Herkunftslandes/-region für Rohstoffe |
| Etikett | Label | Aggregate | Gedrucktes Etikett mit allen Pflichtangaben |
| Pflichtangabe | Mandatory Declaration | Concept | Rechtlich vorgeschriebene Information auf Etikett (Name, Zutaten, Allergene, MHD, Hersteller) |
| Allergenkennzeichnung | Allergen Declaration | Concept | Hervorhebung von Allergenen in Zutatenliste (z.B. **Milch**, **Gluten**) |
| Allergene Matrix | Allergen Matrix | Aggregate | Übersichtstabelle: welches Produkt enthält welche Allergene (für Aushang) |
| Zertifizierungsnummer | Certification Number | Value Object | Nummer des Bio-/Regional-/Tierwohl-Zertifikats |
---
## Inventory BC
| Begriff (DE) | Begriff (EN) | Typ | Definition |
|--------------|--------------|-----|------------|
| Bestand | Stock | Aggregate | Aktueller Bestand eines Artikels in einem Lagerort, chargengenau |
| Lagerort | Storage Location | Value Object | Physischer Ort (Kühlraum, Tiefkühler, Trockenlager, Theke) |
| Bestandsbewegung | Stock Movement | Aggregate | Veränderung des Bestands (Wareneingang, Produktion, Verkauf, Umbuchung) |
| MHD | Best-Before Date | Value Object | Mindesthaltbarkeitsdatum, chargenspezifisch |
| FEFO | First-Expired-First-Out | Concept | Verkaufspriorisierung nach MHD (ältestes MHD zuerst) |
| Charge | Batch | Concept | Eindeutig identifizierte Menge eines Artikels mit gleicher Herkunft/Produktion |
| Chargennummer | Batch Number | Value Object | Eindeutige Identifikation einer Charge (ProductionBatchId oder SupplierBatchId) |
| Verfügbarer Bestand | Available Stock | Value Object | Bestand abzüglich Reservierungen |
| Reservierung | Reservation | Entity | Vorgemerkter Bestand für Produktionsauftrag oder Kundenauftrag |
| Schwund | Shrinkage | Concept | Bestandsverlust durch Verderb, Bruch, Diebstahl |
| Inventur | Inventory Count | Aggregate | Physische Zählung des Bestands mit Soll-Ist-Abgleich |
---
## Procurement BC
| Begriff (DE) | Begriff (EN) | Typ | Definition |
|--------------|--------------|-----|------------|
| Bestellung | Purchase Order | Aggregate | Bestellung an Lieferant mit Artikeln, Mengen, Liefertermin |
| Wareneingang | Goods Receipt | Aggregate | Annahme und Erfassung gelieferter Ware mit Qualitätsprüfung |
| Lieferanten-Chargennummer | Supplier Batch Number | Value Object | Chargen-Nummer des Lieferanten für Rückverfolgbarkeit |
| Bedarfsplanung | Demand Planning | Aggregate | Berechnung des Einkaufsbedarfs aus Produktionsplan und Lagerbestand |
| Lieferschein | Delivery Note | Value Object | Vom Lieferanten mitgeliefertes Dokument |
| Veterinärbescheinigung | Veterinary Certificate | Value Object | Gesetzlich vorgeschriebenes Dokument für Fleischwaren |
| Qualitätszertifikat | Quality Certificate | Value Object | Bio-, Regional-, Tierwohl-Zertifikat des Lieferanten |
| Bestellmenge | Order Quantity | Value Object | Bestellte Menge (kann von empfangener Menge abweichen) |
| Mindestbestellmenge | Minimum Order Quantity | Value Object | Vom Lieferanten vorgegebene Mindestmenge |
| Zahlungsziel | Payment Terms | Value Object | Vereinbarte Zahlungsfrist (z.B. "Netto 30 Tage") |
| Lieferantenbewertung | Supplier Rating | Entity | Bewertung nach Qualität, Pünktlichkeit, Preis-Leistung |
---
## Filiales BC
| Begriff (DE) | Begriff (EN) | Typ | Definition |
|--------------|--------------|-----|------------|
| Filiale | Branch | Aggregate | Standort mit eigenem Bestand, ggf. eigener Produktion/Verkauf |
| Filialtyp | Branch Type | Value Object | PRODUCTION_AND_SALES, SALES_ONLY, PRODUCTION_ONLY |
| Interfilial-Transfer | Inter-Branch Transfer | Aggregate | Warenversand von Filiale A nach Filiale B mit Chargen-Tracking |
| Zentrale Produktion | Central Production | Concept | Produktion an einem Standort für Belieferung mehrerer Filialen |
| Verteilungsplan | Distribution Plan | Aggregate | Plan zur Verteilung eines Produktionsbatches an mehrere Filialen |
| Liefertermin | Delivery Date | Value Object | Geplanter oder tatsächlicher Liefertermin für Interfilial-Transfer |
| Transportdokument | Transport Document | Value Object | Lieferschein für interne Lieferung zwischen Filialen |
| Filiallager | Branch Warehouse | Concept | Lagerbestand einer spezifischen Filiale |
---
## Master Data BC
| Begriff (DE) | Begriff (EN) | Typ | Definition |
|--------------|--------------|-----|------------|
| Artikel | Article | Aggregate | Produkt oder Rohstoff mit Stammdaten |
| Verkaufseinheit | Sales Unit | Entity | Einheit, in der Artikel verkauft wird (Stück, kg, 100g) |
| Preismodell | Price Model | Value Object | FIXED (fester Preis) oder WEIGHT_BASED (Preis × Gewicht) |
| Lieferant | Supplier | Aggregate | Geschäftspartner, von dem Waren bezogen werden |
| Kunde | Customer | Aggregate | Geschäftspartner, an den Waren verkauft werden |
| Rahmenvertrag | Frame Contract | Entity | Langfristige Vereinbarung mit Kunde über Preise/Mengen |
| Artikelnummer | Article Number (SKU) | Value Object | Eindeutige Identifikation eines Artikels |
| Produktgruppe | Product Category | Value Object | Kategorisierung von Artikeln (z.B. "Wurst", "Aufschnitt", "Frischfleisch") |
| Kundenpräferenz | Customer Preference | Value Object | Präferenz für Bio, Regional, Tierwohl, etc. |
---
## Sales BC
| Begriff (DE) | Begriff (EN) | Typ | Definition |
|--------------|--------------|-----|------------|
| Auftrag | Order | Aggregate | Kundenauftrag (Vorbestellung oder B2B-Auftrag) |
| Auftragsart | Order Type | Value Object | PRE_ORDER (Vorbestellung), B2B_ORDER, WALK_IN (Ladenverkauf) |
| Rechnung | Invoice | Aggregate | Rechnungsdokument mit Positionen und Betrag |
| Lieferschein | Delivery Note | Aggregate | Dokument für Warenauslieferung (mit oder ohne Rechnungsfunktion) |
| Gutschrift | Credit Note | Aggregate | Stornierung oder Teilstornierung einer Rechnung |
| Zahlungsstatus | Payment Status | Value Object | OPEN, PAID, OVERDUE, CANCELLED |
| Sammelrechnung | Collective Invoice | Concept | Monatliche Zusammenfassung mehrerer Lieferungen (B2B) |
---
## Scale Integration BC
| Begriff (DE) | Begriff (EN) | Typ | Definition |
|--------------|--------------|-----|------------|
| Waagensynchronisation | Scale Sync | Aggregate | Übertragung von Artikelstammdaten an Waage |
| Bondaten | Bond Data | Aggregate | Verkaufsdaten aus Kassensystem |
| Etikettendruck | Label Print | Concept | Druck von Etiketten an Waage mit aktuellem Gewicht |
| TSE | Technical Security Equipment | Concept | Technische Sicherheitseinrichtung für Kassensysteme (Deutschland) |
---
## Shared Concepts (BC-übergreifend)
| Begriff (DE) | Begriff (EN) | Typ | Definition |
|--------------|--------------|-----|------------|
| Menge | Quantity | Value Object | Numerischer Wert mit Einheit (kg, g, Stück, Liter) |
| Geld | Money | Value Object | Betrag mit Währung (z.B. 100 EUR) |
| Zeitstempel | Timestamp | Value Object | Datum + Uhrzeit |
| Benutzer-ID | User ID | Value Object | Eindeutige Identifikation eines Benutzers |
| Status | Status | Value Object (Enum) | Zustand eines Objekts (z.B. ACTIVE, INACTIVE, PENDING) |
---
## Naming Conventions (Java)
### Packages
```java
com.effigenix.domain.production // Production BC
com.effigenix.domain.quality // Quality BC
com.effigenix.domain.labeling // Labeling BC
com.effigenix.domain.inventory // Inventory BC
com.effigenix.domain.procurement // Procurement BC
com.effigenix.domain.filiales // Filiales BC (Plural!)
com.effigenix.domain.masterdata // Master Data BC
```
### Classes (Englisch!)
```java
// Aggregates: Singular noun
Recipe, Batch, ProductionOrder
TemperatureLog, CleaningRecord
ProductLabel, AllergenMatrix
Stock, StockMovement
PurchaseOrder, GoodsReceipt
Branch, InterBranchTransfer
// Value Objects: Descriptive noun
RecipeId, BatchId, YieldPercentage
Temperature, CriticalLimit
AllergenType, NutritionFacts
Quantity, Money, Timestamp
```
### Methods (Englisch!)
```java
// Domain behavior (verbs)
recipe.addIngredient(...)
batch.complete(...)
stock.withdraw(...)
purchaseOrder.confirm(...)
// Factory methods
Recipe.create(...)
Batch.of(...)
// Getters (no "get" prefix)
recipe.id()
batch.status()
stock.availableQuantity()
```
---
## Anti-Patterns (NICHT verwenden!)
❌ **Technische Begriffe im Domain:**
- ~~DTO~~, ~~Entity~~ (nur als Typ-Suffix in Infrastructure)
- ~~Save~~, ~~Update~~, ~~Delete~~ (stattdessen: Domain-Verben)
❌ **Abkürzungen ohne Definition:**
- ~~WE~~ → Wareneingang / Goods Receipt
- ~~BS~~ → Bestand / Stock
❌ **Deutsche Begriffe im Code:**
- ~~Rezept.java~~ → Recipe.java
- ~~addZutat()~~ → addIngredient()
✅ **Deutsche Begriffe nur in:**
- Dokumentation
- UI/UX
- User Stories
- Glossar (wie dieses Dokument)

View file

@ -0,0 +1,518 @@
# Production BC - Detailliertes Domain Model
**Bounded Context:** Production
**Domain Type:** CORE
**Verantwortung:** Rezepturverwaltung, Produktionsplanung, Chargen-Erzeugung
---
## Aggregates
### 1. Recipe (Aggregate Root)
**Verantwortung:** Verwaltet mehrstufige Rezepturen mit Zutaten und Produktionsschritten.
```
Recipe (Aggregate Root)
├── RecipeId (VO)
├── Name (VO)
├── Version (VO)
├── RecipeType (VO: RAW_MATERIAL | INTERMEDIATE | FINISHED_PRODUCT)
├── YieldPercentage (VO) - Ausbeute in %
├── Ingredients[] (Entity)
│ ├── Position (VO) - Reihenfolge im Rezept
│ ├── ArticleId (VO) - Reference to Master Data
│ ├── Quantity (VO)
│ ├── RecipeId (VO) - For intermediate products (nested recipes)
│ └── IsSubstitutable (VO) - Kann durch Alternativzutat ersetzt werden?
├── ProductionSteps[] (Entity)
│ ├── StepNumber (VO)
│ ├── Description (VO)
│ ├── Duration (VO) - Geschätzte Dauer
│ └── Temperature (VO) - Optional, für Räuchern/Kochen
└── Status (VO: DRAFT | ACTIVE | ARCHIVED)
```
**Invarianten:**
```java
/**
* Recipe aggregate root.
*
* Invariants:
* - Recipe must have at least one ingredient
* - Yield percentage must be between 1-100%
* - Ingredient quantities must be positive
* - Nested recipes cannot create circular dependencies (A → B → A)
* - Sum of ingredient quantities should roughly match expected input
* - Position numbers in Ingredients must be unique
* - StepNumber in ProductionSteps must be sequential (1, 2, 3, ...)
*/
```
**Business Methods:**
```java
public Result<RecipeError, Void> addIngredient(
ArticleId articleId,
Quantity quantity,
Optional<RecipeId> nestedRecipeId
);
public Result<RecipeError, Void> removeIngredient(ArticleId articleId);
public Result<RecipeError, Void> updateYield(YieldPercentage newYield);
public Result<RecipeError, Void> addProductionStep(
String description,
Optional<Duration> duration
);
public Result<RecipeError, Void> activate();
public Result<RecipeError, Void> archive();
// Query methods
public Quantity calculateRequiredInput(Quantity desiredOutput);
public List<ArticleId> getAllIngredients(); // Flattened, including nested
public boolean containsNestedRecipe();
```
**Domain Events:**
```java
RecipeCreated
RecipeActivated
RecipeArchived
IngredientAdded
IngredientRemoved
YieldPercentageChanged
```
---
### 2. Batch (Aggregate Root)
**Verantwortung:** Repräsentiert eine Produktions-Charge mit lückenloser Rückverfolgbarkeit.
```
Batch (Aggregate Root)
├── BatchId (VO) - Eindeutige Chargennummer (z.B. "BATCH-2026-02-17-001")
├── RecipeId (VO) - Reference to Recipe
├── ProductionDate (VO)
├── PlannedQuantity (VO)
├── ActualQuantity (VO) - Nach Produktionsrückmeldung
├── Status (VO: PLANNED | IN_PRODUCTION | COMPLETED | CANCELLED)
├── ProducedBy (VO: UserId)
├── ProducedAt (VO: BranchId) - Für Mehrfilialen
├── ExpiryDate (VO) - MHD des Endprodukts
├── UsedIngredientBatches[] (Entity) - CRITICAL for traceability!
│ ├── RawMaterialBatchId (VO) - Batch of used raw material
│ ├── ArticleId (VO)
│ ├── SupplierBatchNumber (VO) - Von Wareneingang
│ ├── QuantityUsed (VO)
│ └── ExpiryDate (VO) - MHD des Rohstoffs
├── Waste (VO) - Ausschuss/Schwund
└── Remarks (VO) - Optional, für Abweichungen
Invariants:
- Batch must reference a valid Recipe
- ActualQuantity must be <= (PlannedQuantity / Recipe.YieldPercentage)
- Status can only transition: PLANNED → IN_PRODUCTION → COMPLETED (or CANCELLED)
- Cannot complete without recording used ingredient batches
- ExpiryDate must be in the future at production time
- Waste must be >= 0
- All UsedIngredientBatches must match Recipe.Ingredients
```
**Business Methods:**
```java
public static Result<BatchError, Batch> plan(
RecipeId recipeId,
Quantity plannedQuantity,
LocalDate productionDate,
LocalDate expiryDate,
UserId producedBy,
BranchId branchId
);
public Result<BatchError, Void> startProduction();
public Result<BatchError, Void> recordIngredientUsage(
ArticleId articleId,
BatchId rawMaterialBatchId,
SupplierBatchNumber supplierBatchNumber,
Quantity quantityUsed,
LocalDate expiryDate
);
public Result<BatchError, Void> complete(
Quantity actualQuantity,
Quantity waste,
Optional<String> remarks
);
public Result<BatchError, Void> cancel(String reason);
// Query methods
public boolean isTraceable(); // All ingredients have batch numbers
public List<BatchId> getUpstreamBatches(); // All used raw material batches
public Quantity getYieldEfficiency(); // ActualQuantity / (PlannedQuantity / YieldPercentage)
```
**Domain Events:**
```java
BatchPlanned
BatchStarted
IngredientUsageRecorded
BatchCompleted(BatchId, ArticleId, Quantity, ExpiryDate) → triggers Inventory stock in
BatchCancelled
```
---
### 3. ProductionOrder (Aggregate Root)
**Verantwortung:** Plant eine zukünftige Produktion.
```
ProductionOrder (Aggregate Root)
├── ProductionOrderId (VO)
├── RecipeId (VO)
├── PlannedQuantity (VO)
├── PlannedDate (VO)
├── Priority (VO: LOW | NORMAL | HIGH | URGENT)
├── Status (VO: PLANNED | RELEASED | IN_PRODUCTION | COMPLETED | CANCELLED)
├── CreatedBy (VO: UserId)
├── CreatedAt (VO: Timestamp)
├── TargetBranch (VO: BranchId) - For multi-branch production
├── GeneratedBatchId (VO) - Link to actual Batch when production starts
└── Remarks (VO)
Invariants:
- Planned quantity must be positive
- Planned date cannot be in the past
- Can only release if materials available (checked in Application layer!)
- Cannot complete without generating a Batch
- Status transitions: PLANNED → RELEASED → IN_PRODUCTION → COMPLETED
```
**Business Methods:**
```java
public static Result<ProductionOrderError, ProductionOrder> create(
RecipeId recipeId,
Quantity plannedQuantity,
LocalDate plannedDate,
Priority priority,
BranchId targetBranch,
UserId createdBy
);
public Result<ProductionOrderError, Void> release();
public Result<ProductionOrderError, Void> startProduction(BatchId batchId);
public Result<ProductionOrderError, Void> complete();
public Result<ProductionOrderError, Void> cancel(String reason);
public Result<ProductionOrderError, Void> reschedule(LocalDate newDate);
```
**Domain Events:**
```java
ProductionOrderCreated
ProductionOrderReleased → triggers Demand Planning update
ProductionOrderStarted
ProductionOrderCompleted
ProductionOrderCancelled
ProductionOrderRescheduled
```
---
## Value Objects
### RecipeId
```java
public record RecipeId(String value) {
public RecipeId {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("RecipeId cannot be empty");
}
}
}
```
### BatchId
```java
public record BatchId(String value) {
// Format: "BATCH-YYYY-MM-DD-XXX"
public static BatchId generate(LocalDate productionDate, int sequenceNumber) {
String value = String.format("BATCH-%s-%03d",
productionDate, sequenceNumber);
return new BatchId(value);
}
}
```
### YieldPercentage
```java
public record YieldPercentage(int value) {
public YieldPercentage {
if (value < 1 || value > 100) {
throw new IllegalArgumentException(
"Yield percentage must be between 1-100, got: " + value
);
}
}
public Quantity calculateRequiredInput(Quantity desiredOutput) {
// If yield is 80%, and we want 100kg output, we need 125kg input
return desiredOutput.multiply(100.0 / value);
}
}
```
### RecipeType
```java
public enum RecipeType {
RAW_MATERIAL, // Rohstoff, kein Rezept
INTERMEDIATE, // Zwischenprodukt (z.B. Gewürzmischung)
FINISHED_PRODUCT // Endprodukt
}
```
### ProductionOrderPriority
```java
public enum Priority {
LOW,
NORMAL,
HIGH,
URGENT
}
```
---
## Domain Services
### RecipeValidator
```java
public class RecipeValidator {
/**
* Validates that recipe does not create circular dependencies.
*/
public Result<RecipeError, Void> validateNoCyclicDependency(
Recipe recipe,
RecipeRepository recipeRepository
);
}
```
### BatchTraceabilityService
```java
public class BatchTraceabilityService {
/**
* Finds all upstream batches (raw materials) used in a batch.
*/
public List<BatchId> findUpstreamBatches(BatchId batchId);
/**
* Finds all downstream batches (finished products) that used a raw material batch.
* CRITICAL for recalls!
*/
public List<BatchId> findDownstreamBatches(BatchId rawMaterialBatchId);
}
```
---
## Repository Interfaces
```java
package com.effigenix.domain.production;
import com.effigenix.shared.result.Result;
public interface RecipeRepository {
Result<RepositoryError, Void> save(Recipe recipe);
Result<RepositoryError, Recipe> findById(RecipeId id);
Result<RepositoryError, List<Recipe>> findActive();
Result<RepositoryError, List<Recipe>> findByArticleId(ArticleId articleId);
}
public interface BatchRepository {
Result<RepositoryError, Void> save(Batch batch);
Result<RepositoryError, Batch> findById(BatchId id);
Result<RepositoryError, List<Batch>> findByProductionDate(LocalDate date);
Result<RepositoryError, List<Batch>> findByStatus(BatchStatus status);
// For traceability
Result<RepositoryError, List<Batch>> findByUpstreamBatch(BatchId upstreamBatchId);
}
public interface ProductionOrderRepository {
Result<RepositoryError, Void> save(ProductionOrder order);
Result<RepositoryError, ProductionOrder> findById(ProductionOrderId id);
Result<RepositoryError, List<ProductionOrder>> findByPlannedDate(LocalDate date);
Result<RepositoryError, List<ProductionOrder>> findByStatus(ProductionOrderStatus status);
}
```
---
## Domain Errors
```java
public sealed interface RecipeError permits
RecipeError.InvalidYieldPercentage,
RecipeError.CyclicDependencyDetected,
RecipeError.NoIngredientsError,
RecipeError.RecipeNotFound {
String message();
}
public sealed interface BatchError permits
BatchError.InvalidQuantity,
BatchError.InvalidStatusTransition,
BatchError.MissingIngredientBatches,
BatchError.ExpiryDateInPast,
BatchError.RecipeNotFound {
String message();
}
public sealed interface ProductionOrderError permits
ProductionOrderError.PlannedDateInPast,
ProductionOrderError.InvalidQuantity,
ProductionOrderError.InvalidStatusTransition,
ProductionOrderError.RecipeNotFound {
String message();
}
```
---
## Integration with other BCs
### Upstream Dependencies
- **Master Data BC:** Recipe references ArticleId
- **User Management BC:** Batch/ProductionOrder reference UserId
- **Filiales BC:** ProductionOrder references BranchId
### Downstream Integrations
- **Inventory BC:** `BatchCompleted` event triggers stock in
- **Labeling BC:** Labeling reads Recipe data for nutrition calculation
- **Procurement BC:** ProductionOrder triggers demand planning
---
## Use Cases (Application Layer)
```java
// application/production/CreateRecipe.java
public class CreateRecipe {
public Result<ApplicationError, RecipeDTO> execute(CreateRecipeCommand cmd);
}
// application/production/PlanProduction.java
public class PlanProduction {
public Result<ApplicationError, ProductionOrderDTO> execute(PlanProductionCommand cmd);
}
// application/production/StartProductionBatch.java
public class StartProductionBatch {
public Result<ApplicationError, BatchDTO> execute(StartBatchCommand cmd);
}
// application/production/CompleteProductionBatch.java
public class CompleteProductionBatch {
public Result<ApplicationError, BatchDTO> execute(CompleteBatchCommand cmd);
// Triggers BatchCompleted event → Inventory stock in
}
```
---
## Example: Batch Creation Flow
```java
// 1. Plan Production Order
ProductionOrder order = ProductionOrder.create(
recipeId,
Quantity.of(100, "kg"),
LocalDate.now().plusDays(1),
Priority.NORMAL,
branchId,
userId
);
// 2. Release Order (checks material availability in Application layer)
order.release();
// 3. Start Production → Create Batch
Batch batch = Batch.plan(
order.recipeId(),
order.plannedQuantity(),
LocalDate.now(),
LocalDate.now().plusDays(30), // MHD
userId,
branchId
);
batch.startProduction();
order.startProduction(batch.id());
// 4. Record ingredient usage
batch.recordIngredientUsage(
ArticleId.of("ART-001"),
BatchId.of("BATCH-2026-02-15-042"), // Supplier batch
SupplierBatchNumber.of("SUPPLIER-12345"),
Quantity.of(50, "kg"),
LocalDate.now().plusDays(20) // MHD of raw material
);
// 5. Complete Batch
batch.complete(
Quantity.of(80, "kg"), // Actual output
Quantity.of(5, "kg"), // Waste
Optional.of("Leichter Schwund beim Räuchern")
);
order.complete();
// 6. BatchCompleted event → Inventory creates Stock entry
```
---
## Testing Strategy
### Unit Tests (Domain Layer)
```java
@Test
void createBatch_withValidData_succeeds() {
var result = Batch.plan(recipeId, quantity, date, expiryDate, userId, branchId);
assertThat(result.isSuccess()).isTrue();
}
@Test
void completeBatch_withoutIngredients_fails() {
var batch = Batch.plan(...).unsafeGetValue();
batch.startProduction();
var result = batch.complete(quantity, waste, Optional.empty());
assertThat(result.isFailure()).isTrue();
// Should fail with MissingIngredientBatches error
}
```
### Integration Tests (Application Layer)
```java
@Test
void completeProductionBatch_updatesInventory() {
// Given: Production order and started batch
// When: Complete batch
var result = completeProductionBatch.execute(command);
// Then: Inventory stock should increase
var stock = inventoryRepository.findByArticle(articleId);
assertThat(stock.quantity()).isEqualTo(expectedQuantity);
}
```

View file

@ -0,0 +1,490 @@
# Produktions-Kontext - Detailliertes Domain Model
**Bounded Context:** Produktion
**Domain-Typ:** KERN
**Verantwortung:** Rezepturverwaltung, Produktionsplanung, Chargen-Erzeugung
---
## Aggregate
### 1. Rezept (Aggregate Root)
**Verantwortung:** Verwaltet mehrstufige Rezepturen mit Zutaten und Produktionsschritten.
**Struktur:**
```
Rezept (Aggregate Root)
├── RezeptId (Wertobjekt)
├── Name (Wertobjekt)
├── Version (Wertobjekt)
├── RezeptTyp (Wertobjekt: ROHSTOFF | ZWISCHENPRODUKT | ENDPRODUKT)
├── AusbeuteInProzent (Wertobjekt) - Ausbeute in %
├── Zutaten[] (Entität)
│ ├── Position (Wertobjekt) - Reihenfolge im Rezept
│ ├── ArtikelId (Wertobjekt) - Referenz zu Stammdaten
│ ├── Menge (Wertobjekt)
│ ├── RezeptId (Wertobjekt) - Für Zwischenprodukte (verschachtelte Rezepte)
│ └── IstErsetzbar (Wertobjekt) - Kann durch Alternativzutat ersetzt werden?
├── Produktionsschritte[] (Entität)
│ ├── SchrittNummer (Wertobjekt)
│ ├── Beschreibung (Wertobjekt)
│ ├── Dauer (Wertobjekt) - Geschätzte Dauer
│ └── Temperatur (Wertobjekt) - Optional, für Räuchern/Kochen
└── Status (Wertobjekt: ENTWURF | AKTIV | ARCHIVIERT)
```
**Invarianten:**
```java
/**
* Rezept Aggregate Root.
*
* Invarianten:
* - Rezept muss mindestens eine Zutat haben
* - Ausbeute muss zwischen 1-100% liegen
* - Zutatmengen müssen positiv sein
* - Verschachtelte Rezepte dürfen keine Zyklen erzeugen (A → B → A)
* - Summe der Zutatmengen sollte ungefähr dem erwarteten Input entsprechen
* - Positionsnummern bei Zutaten müssen eindeutig sein
* - SchrittNummer bei Produktionsschritten muss sequenziell sein (1, 2, 3, ...)
*/
```
**Geschäftsmethoden:**
```java
// Zutat hinzufügen
public Result<RezeptFehler, Void> zutatHinzufuegen(
ArtikelId artikelId,
Menge menge,
Optional<RezeptId> verschachteltesRezeptId
);
// Zutat entfernen
public Result<RezeptFehler, Void> zutatEntfernen(ArtikelId artikelId);
// Ausbeute aktualisieren
public Result<RezeptFehler, Void> ausbeuteAktualisieren(AusbeuteInProzent neueAusbeute);
// Produktionsschritt hinzufügen
public Result<RezeptFehler, Void> produktionsschrittHinzufuegen(
String beschreibung,
Optional<Duration> dauer
);
// Rezept aktivieren
public Result<RezeptFehler, Void> aktivieren();
// Rezept archivieren
public Result<RezeptFehler, Void> archivieren();
// Abfragemethoden
public Menge berechneBenoeteigtenInput(Menge gewuenschterOutput);
public List<ArtikelId> holeAlleZutaten(); // Flach, inkl. verschachtelte
public boolean enthaeltVerschachtelteRezepte();
```
**Domänen-Events:**
```java
RezeptErstellt
RezeptAktiviert
RezeptArchiviert
ZutatHinzugefuegt
ZutatEntfernt
AusbeuteGeaendert
```
---
### 2. Charge (Aggregate Root)
**Verantwortung:** Repräsentiert eine Produktions-Charge mit lückenloser Rückverfolgbarkeit.
**Struktur:**
```
Charge (Aggregate Root)
├── ChargenId (Wertobjekt) - Eindeutige Chargennummer (z.B. "CHARGE-2026-02-17-001")
├── RezeptId (Wertobjekt) - Referenz zu Rezept
├── Produktionsdatum (Wertobjekt)
├── GeplantemMenge (Wertobjekt)
├── TatsaechlicheMenge (Wertobjekt) - Nach Produktionsrückmeldung
├── Status (Wertobjekt: GEPLANT | IN_PRODUKTION | ABGESCHLOSSEN | STORNIERT)
├── ProduziertVon (Wertobjekt: BenutzerId)
├── ProduziertIn (Wertobjekt: FilialId) - Für Mehrfilialen
├── Verfallsdatum (Wertobjekt) - MHD des Endprodukts
├── VerwendeteZutatenchargen[] (Entität) - KRITISCH für Rückverfolgbarkeit!
│ ├── RohstoffChargenId (Wertobjekt) - Charge des verwendeten Rohstoffs
│ ├── ArtikelId (Wertobjekt)
│ ├── LieferantenChargennummer (Wertobjekt) - Vom Wareneingang
│ ├── VerwendeteMenge (Wertobjekt)
│ └── Verfallsdatum (Wertobjekt) - MHD des Rohstoffs
├── Ausschuss (Wertobjekt) - Ausschuss/Schwund
└── Bemerkungen (Wertobjekt) - Optional, für Abweichungen
Invarianten:
- Charge muss ein gültiges Rezept referenzieren
- TatsaechlicheMenge muss <= (GeplantemMenge / Rezept.AusbeuteInProzent) sein
- Status kann nur übergehen: GEPLANT → IN_PRODUKTION → ABGESCHLOSSEN (oder STORNIERT)
- Kann nicht abgeschlossen werden ohne verwendete Zutatenchargen zu erfassen
- Verfallsdatum muss in der Zukunft liegen zum Produktionszeitpunkt
- Ausschuss muss >= 0 sein
- Alle VerwendeteZutatenchargen müssen zu Rezept.Zutaten passen
```
**Geschäftsmethoden:**
```java
// Charge planen
public static Result<ChargenFehler, Charge> planen(
RezeptId rezeptId,
Menge geplantemMenge,
LocalDate produktionsdatum,
LocalDate verfallsdatum,
BenutzerId produziertVon,
FilialId filialId
);
// Produktion starten
public Result<ChargenFehler, Void> produktionStarten();
// Zutatverbrauch erfassen
public Result<ChargenFehler, Void> zutatverbrauchErfassen(
ArtikelId artikelId,
ChargenId rohstoffChargenId,
LieferantenChargennummer lieferantenChargennummer,
Menge verwendeteMenge,
LocalDate verfallsdatum
);
// Charge abschließen
public Result<ChargenFehler, Void> abschliessen(
Menge tatsaechlicheMenge,
Menge ausschuss,
Optional<String> bemerkungen
);
// Charge stornieren
public Result<ChargenFehler, Void> stornieren(String grund);
// Abfragemethoden
public boolean istRueckverfolgbar(); // Alle Zutaten haben Chargennummern
public List<ChargenId> holeVorgelageteChargen(); // Alle verwendeten Rohstoffchargen
public Menge berechneAusbeuteEffizienz(); // TatsaechlicheMenge / (GeplantemMenge / AusbeuteInProzent)
```
**Domänen-Events:**
```java
ChargeGeplant
ChargeGestartet
ZutatverbrauchErfasst
ChargeAbgeschlossen(ChargenId, ArtikelId, Menge, Verfallsdatum) → löst Bestandszugang aus
ChargeStorniert
```
---
### 3. Produktionsauftrag (Aggregate Root)
**Verantwortung:** Plant eine zukünftige Produktion.
**Struktur:**
```
Produktionsauftrag (Aggregate Root)
├── ProduktionsauftragId (Wertobjekt)
├── RezeptId (Wertobjekt)
├── GeplantemMenge (Wertobjekt)
├── GeplantesDatum (Wertobjekt)
├── Prioritaet (Wertobjekt: NIEDRIG | NORMAL | HOCH | DRINGEND)
├── Status (Wertobjekt: GEPLANT | FREIGEGEBEN | IN_PRODUKTION | ABGESCHLOSSEN | STORNIERT)
├── ErstelltVon (Wertobjekt: BenutzerId)
├── ErstelltAm (Wertobjekt: Zeitstempel)
├── Zielfiliale (Wertobjekt: FilialId) - Für Mehrfilialen-Produktion
├── ErzeugeChargenId (Wertobjekt) - Link zur tatsächlichen Charge bei Produktionsstart
└── Bemerkungen (Wertobjekt)
Invarianten:
- Geplante Menge muss positiv sein
- Geplantes Datum darf nicht in der Vergangenheit liegen
- Kann nur freigegeben werden, wenn Materialien verfügbar sind (geprüft in Anwendungsschicht!)
- Kann nicht abgeschlossen werden ohne eine Charge zu erzeugen
- Status-Übergänge: GEPLANT → FREIGEGEBEN → IN_PRODUKTION → ABGESCHLOSSEN
```
**Geschäftsmethoden:**
```java
// Produktionsauftrag erstellen
public static Result<ProduktionsauftragFehler, Produktionsauftrag> erstellen(
RezeptId rezeptId,
Menge geplantemMenge,
LocalDate geplantesDatum,
Prioritaet prioritaet,
FilialId zielfiliale,
BenutzerId erstelltVon
);
// Freigeben
public Result<ProduktionsauftragFehler, Void> freigeben();
// Produktion starten
public Result<ProduktionsauftragFehler, Void> produktionStarten(ChargenId chargenId);
// Abschließen
public Result<ProduktionsauftragFehler, Void> abschliessen();
// Stornieren
public Result<ProduktionsauftragFehler, Void> stornieren(String grund);
// Umplanen
public Result<ProduktionsauftragFehler, Void> umplanen(LocalDate neuesDatum);
```
**Domänen-Events:**
```java
ProduktionsauftragErstellt
ProduktionsauftragFreigegeben → löst Bedarfsplanung-Update aus
ProduktionsauftragGestartet
ProduktionsauftragAbgeschlossen
ProduktionsauftragStorniert
ProduktionsauftragUmgeplant
```
---
## Wertobjekte
### RezeptId
```java
public record RezeptId(String wert) {
public RezeptId {
if (wert == null || wert.isBlank()) {
throw new IllegalArgumentException("RezeptId darf nicht leer sein");
}
}
}
```
### ChargenId
```java
public record ChargenId(String wert) {
// Format: "CHARGE-YYYY-MM-DD-XXX"
public static ChargenId generieren(LocalDate produktionsdatum, int sequenzNummer) {
String wert = String.format("CHARGE-%s-%03d",
produktionsdatum, sequenzNummer);
return new ChargenId(wert);
}
}
```
### AusbeuteInProzent
```java
public record AusbeuteInProzent(int wert) {
public AusbeuteInProzent {
if (wert < 1 || wert > 100) {
throw new IllegalArgumentException(
"Ausbeute muss zwischen 1-100% liegen, ist: " + wert
);
}
}
public Menge berechneBenoeteigtenInput(Menge gewuenschterOutput) {
// Bei 80% Ausbeute und 100kg gewünschtem Output brauchen wir 125kg Input
return gewuenschterOutput.multiplizieren(100.0 / wert);
}
}
```
### RezeptTyp
```java
public enum RezeptTyp {
ROHSTOFF, // Rohstoff, kein Rezept
ZWISCHENPRODUKT, // Zwischenprodukt (z.B. Gewürzmischung)
ENDPRODUKT // Endprodukt
}
```
---
## Domänen-Services
### RezeptValidator
```java
public class RezeptValidator {
/**
* Validiert, dass Rezept keine zyklischen Abhängigkeiten erzeugt.
*/
public Result<RezeptFehler, Void> validiereKeineZyklischeAbhaengigkeit(
Rezept rezept,
RezeptRepository rezeptRepository
);
}
```
### ChargenRueckverfolgbarkeitService
```java
public class ChargenRueckverfolgbarkeitService {
/**
* Findet alle vorgelagerten Chargen (Rohstoffe), die in einer Charge verwendet wurden.
*/
public List<ChargenId> findeVorgelageteChargen(ChargenId chargenId);
/**
* Findet alle nachgelagerten Chargen (Endprodukte), die einen Rohstoff verwendet haben.
* KRITISCH für Rückrufe!
*/
public List<ChargenId> findeNachgelageteChargen(ChargenId rohstoffChargenId);
}
```
---
## Repository-Schnittstellen
```java
package com.effigenix.domain.produktion;
import com.effigenix.shared.result.Result;
public interface RezeptRepository {
Result<RepositoryFehler, Void> speichern(Rezept rezept);
Result<RepositoryFehler, Rezept> findeNachId(RezeptId id);
Result<RepositoryFehler, List<Rezept>> findeAktive();
Result<RepositoryFehler, List<Rezept>> findeNachArtikelId(ArtikelId artikelId);
}
public interface ChargenRepository {
Result<RepositoryFehler, Void> speichern(Charge charge);
Result<RepositoryFehler, Charge> findeNachId(ChargenId id);
Result<RepositoryFehler, List<Charge>> findeNachProduktionsdatum(LocalDate datum);
Result<RepositoryFehler, List<Charge>> findeNachStatus(ChargenStatus status);
// Für Rückverfolgbarkeit
Result<RepositoryFehler, List<Charge>> findeNachVorgelagerterCharge(ChargenId vorgelagertChargenId);
}
public interface ProduktionsauftragRepository {
Result<RepositoryFehler, Void> speichern(Produktionsauftrag auftrag);
Result<RepositoryFehler, Produktionsauftrag> findeNachId(ProduktionsauftragId id);
Result<RepositoryFehler, List<Produktionsauftrag>> findeNachGeplantemDatum(LocalDate datum);
Result<RepositoryFehler, List<Produktionsauftrag>> findeNachStatus(ProduktionsauftragStatus status);
}
```
---
## Domänen-Fehler
```java
public sealed interface RezeptFehler permits
RezeptFehler.UngueltigeAusbeute,
RezeptFehler.ZyklischeAbhaengigkeitEntdeckt,
RezeptFehler.KeineZutatenVorhanden,
RezeptFehler.RezeptNichtGefunden {
String nachricht();
}
public sealed interface ChargenFehler permits
ChargenFehler.UngueltigeMenge,
ChargenFehler.UngueltigerStatusUebergang,
ChargenFehler.FehlendeZutatenchargen,
ChargenFehler.VerfallsdatumInVergangenheit,
ChargenFehler.RezeptNichtGefunden {
String nachricht();
}
public sealed interface ProduktionsauftragFehler permits
ProduktionsauftragFehler.GeplantesDatumInVergangenheit,
ProduktionsauftragFehler.UngueltigeMenge,
ProduktionsauftragFehler.UngueltigerStatusUebergang,
ProduktionsauftragFehler.RezeptNichtGefunden {
String nachricht();
}
```
---
## Integration mit anderen Kontexten
### Upstream-Abhängigkeiten
- **Stammdaten-Kontext:** Rezept referenziert ArtikelId
- **Benutzerverwaltung:** Charge/Produktionsauftrag referenziert BenutzerId
- **Filialen-Kontext:** Produktionsauftrag referenziert FilialId
### Downstream-Integrationen
- **Bestandsführung:** `ChargeAbgeschlossen` Event löst Bestandszugang aus
- **Deklaration:** Deklaration liest Rezeptdaten für Nährwertberechnung
- **Beschaffung:** Produktionsauftrag löst Bedarfsplanung aus
---
## Anwendungsfälle (Anwendungsschicht)
```java
// application/produktion/RezeptErstellen.java
public class RezeptErstellen {
public Result<AnwendungsFehler, RezeptDTO> ausfuehren(RezeptErstellenKommando cmd);
}
// application/produktion/ProduktionPlanen.java
public class ProduktionPlanen {
public Result<AnwendungsFehler, ProduktionsauftragDTO> ausfuehren(ProduktionPlanenKommando cmd);
}
// application/produktion/ProduktionschargeStarten.java
public class ProduktionschargeStarten {
public Result<AnwendungsFehler, ChargenDTO> ausfuehren(ChargeStartenKommando cmd);
}
// application/produktion/ProduktionschargeAbschliessen.java
public class ProduktionschargeAbschliessen {
public Result<AnwendungsFehler, ChargenDTO> ausfuehren(ChargeAbschliessenKommando cmd);
// Löst ChargeAbgeschlossen Event aus → Bestandszugang
}
```
---
## Beispiel: Chargen-Erstellungs-Ablauf
```java
// 1. Produktionsauftrag planen
Produktionsauftrag auftrag = Produktionsauftrag.erstellen(
rezeptId,
Menge.von(100, "kg"),
LocalDate.now().plusDays(1),
Prioritaet.NORMAL,
filialId,
benutzerId
);
// 2. Auftrag freigeben (prüft Materialverfügbarkeit in Anwendungsschicht)
auftrag.freigeben();
// 3. Produktion starten → Charge erstellen
Charge charge = Charge.planen(
auftrag.rezeptId(),
auftrag.geplantemMenge(),
LocalDate.now(),
LocalDate.now().plusDays(30), // MHD
benutzerId,
filialId
);
charge.produktionStarten();
auftrag.produktionStarten(charge.id());
// 4. Zutatverbrauch erfassen
charge.zutatverbrauchErfassen(
ArtikelId.von("ART-001"),
ChargenId.von("CHARGE-2026-02-15-042"), // Lieferanten-Charge
LieferantenChargennummer.von("SUPPLIER-12345"),
Menge.von(50, "kg"),
LocalDate.now().plusDays(20) // MHD des Rohstoffs
);
// 5. Charge abschließen
charge.abschliessen(
Menge.von(80, "kg"), // Tatsächlicher Output
Menge.von(5, "kg"), // Ausschuss
Optional.of("Leichter Schwund beim Räuchern")
);
auftrag.abschliessen();
// 6. ChargeAbgeschlossen Event → Bestandsführung erzeugt Bestandseintrag
```

View file

@ -0,0 +1,192 @@
# Qualitäts-Kontext (HACCP/QM) - Detailliertes Domain Model
**Bounded Context:** Qualität
**Domain-Typ:** KERN
**Verantwortung:** HACCP-Compliance, Qualitätsmanagement, Audit-Vorbereitung
---
## Aggregate
### 1. Temperaturprotokoll (Aggregate Root)
**Struktur:**
```
Temperaturprotokoll
├── TemperaturprotokollId (Wertobjekt)
├── Messpunkt (Wertobjekt: KUEHLRAUM | TIEFKUEHLER | VERKAUFSTHEKE | PRODUKTIONSRAUM)
├── GeraetId (Wertobjekt) - Referenz zu Ausstattung
├── GemessenAm (Wertobjekt: Zeitstempel)
├── Temperatur (Wertobjekt: mit Einheit °C)
├── GemessenVon (Wertobjekt: BenutzerId)
├── KritischeGrenzwertMin (Wertobjekt)
├── KritischeGrenzwertMax (Wertobjekt)
└── Status (Wertobjekt: OK | WARNUNG | KRITISCH)
Invarianten:
- Temperatur muss im physikalisch möglichen Bereich liegen (-50°C bis +50°C)
- GemessenAm darf nicht in der Zukunft liegen
- Status = KRITISCH wenn Temperatur außerhalb kritischer Grenzwerte
- Status = WARNUNG wenn Temperatur nahe an Grenzwerten (innerhalb 10%)
- KritischeGrenzwertMin < KritischeGrenzwertMax
```
**Domänen-Events:**
```java
TemperaturKritischeGrenzwerteUeberschritten(TemperaturprotokollId, Messpunkt, Temperatur)
```
---
### 2. Reinigungsnachweis (Aggregate Root)
**Struktur:**
```
Reinigungsnachweis
├── ReinigungsnachweisId (Wertobjekt)
├── Bereich (Wertobjekt: PRODUKTIONSRAUM | KUEHLRAUM | VERKAUFSTHEKE | GERAET)
├── ReinigungsplanId (Wertobjekt) - Referenz zu Reinigungsplan
├── GeplantesFuer (Wertobjekt: Datum)
├── AbgeschlossenAm (Wertobjekt: Zeitstempel)
├── AbgeschlossenVon (Wertobjekt: BenutzerId)
├── Checklisten-Eintraege[] (Entität)
│ ├── Eintrag (Wertobjekt: "Boden gewischt", "Oberflächen desinfiziert")
│ ├── Erledigt (Wertobjekt: boolean)
│ └── Bemerkungen (Wertobjekt: optional)
└── GesamtBemerkungen (Wertobjekt)
Invarianten:
- Alle Checklisten-Einträge müssen erledigt sein zum Abschluss
- AbgeschlossenAm muss >= GeplantesFuer sein
- Kann nicht abgeschlossen werden ohne AbgeschlossenVon
- Kann nach Abschluss nicht mehr geändert werden
```
**Domänen-Events:**
```java
ReinigungUeberfaellig(ReinigungsplanId, Bereich, LocalDate geplantesFuer)
```
---
### 3. Wareneingangspruefung (Aggregate Root)
**Struktur:**
```
Wareneingangspruefung
├── PruefungId (Wertobjekt)
├── WareneingangId (Wertobjekt) - Referenz zu Beschaffungs-Kontext
├── GeprueftAm (Wertobjekt)
├── GeprueftVon (Wertobjekt: BenutzerId)
├── Temperaturpruefung (Entität)
│ ├── GemesseneTemperatur (Wertobjekt)
│ ├── ErwarteterBereich (Wertobjekt)
│ └── Status (Wertobjekt: BESTANDEN | DURCHGEFALLEN)
├── Sichtpruefung (Entität)
│ ├── VerpackungIntakt (Wertobjekt: boolean)
│ ├── Farbe (Wertobjekt: NORMAL | ABNORMAL)
│ ├── Geruchstest (Wertobjekt: NORMAL | ABNORMAL)
│ └── Bemerkungen (Wertobjekt)
├── MHD-Pruefung (Entität)
│ ├── Verfallsdatum (Wertobjekt)
│ ├── TageBeisVerfallsdatum (Wertobjekt)
│ ├── MinimalAkzeptableTage (Wertobjekt)
│ └── Status (Wertobjekt: BESTANDEN | DURCHGEFALLEN)
├── Dokumentenpruefung (Entität)
│ ├── LieferscheinErhalten (Wertobjekt: boolean)
│ ├── VeterinaerbescheinigungErhalten (Wertobjekt: boolean)
│ ├── Qualitaetszertifikate[] (Wertobjekt)
│ └── AlleDokumenteVollstaendig (Wertobjekt: boolean)
├── LieferantenChargennummer (Wertobjekt) - Für Rückverfolgbarkeit!
└── Endergebnis (Wertobjekt: ANGENOMMEN | ABGELEHNT | BEDINGT_ANGENOMMEN)
Invarianten:
- Alle Prüfungen müssen durchgeführt sein, bevor Endergebnis gesetzt werden kann
- Bei ABGELEHNT müssen GesamtBemerkungen angegeben werden
- Temperatur muss im akzeptablen Bereich liegen für ANGENOMMEN
- MHD muss Mindesttage haben für ANGENOMMEN
- Veterinärbescheinigung erforderlich für Fleischprodukte
```
**Domänen-Events:**
```java
WareneingangAbgelehnt(PruefungId, WareneingangId, String grund)
```
---
### 4. Schulungsnachweis (Aggregate Root)
**Struktur:**
```
Schulungsnachweis
├── SchulungsnachweisId (Wertobjekt)
├── MitarbeiterId (Wertobjekt: BenutzerId)
├── SchulungsTyp (Wertobjekt: HACCP | HYGIENE | LEBENSMITTELSICHERHEIT | GERAETEBEDIENUNG)
├── Schulungsdatum (Wertobjekt)
├── GueltigBis (Wertobjekt) - Auffrischung notwendig
├── Schulender (Wertobjekt) - Intern oder extern
├── Zertifikatsnummer (Wertobjekt)
├── ZertifikatsDokumentUrl (Wertobjekt)
└── Status (Wertobjekt: GUELTIG | ABGELAUFEN | WIDERRUFEN)
Invarianten:
- GueltigBis muss nach Schulungsdatum liegen
- Status = ABGELAUFEN wenn GueltigBis < HEUTE
- Kann nicht widerrufen werden ohne Grund
```
**Domänen-Events:**
```java
SchulungLaeuftDemnaechstAb(SchulungsnachweisId, BenutzerId, LocalDate ablaufdatum)
```
---
### 5. Wartungsprotokoll (Aggregate Root)
**Struktur:**
```
Wartungsprotokoll
├── WartungsprotokollId (Wertobjekt)
├── GeraetId (Wertobjekt)
├── WartungsTyp (Wertobjekt: GEPLANT | REPARATUR | INSPEKTION)
├── GeplantesFuer (Wertobjekt: Datum)
├── DurchgefuehrtAm (Wertobjekt: Zeitstempel)
├── DurchgefuehrtVon (Wertobjekt) - Internes Personal oder externe Firma
├── Befunde (Wertobjekt)
├── Massnahmen (Wertobjekt)
├── NaechsteWartungFaellig (Wertobjekt: Datum)
└── Status (Wertobjekt: ABGESCHLOSSEN | AUSSTEHEND | FEHLGESCHLAGEN)
Invarianten:
- DurchgefuehrtAm muss >= GeplantesFuer sein
- Bei FEHLGESCHLAGEN müssen Befunde und Massnahmen dokumentiert sein
- NaechsteWartungFaellig muss basierend auf Wartungsintervall berechnet werden
```
**Domänen-Events:**
```java
WartungUeberfaellig(GeraetId, LocalDate geplantesFuer)
```
---
## Repository-Schnittstellen
```java
public interface TemperaturprotokollRepository {
Result<RepositoryFehler, Void> speichern(Temperaturprotokoll protokoll);
Result<RepositoryFehler, List<Temperaturprotokoll>> findeNachZeitraum(
LocalDate von, LocalDate bis
);
Result<RepositoryFehler, List<Temperaturprotokoll>> findeKritische();
}
public interface WareneingangspruefungRepository {
Result<RepositoryFehler, Void> speichern(Wareneingangspruefung pruefung);
Result<RepositoryFehler, Wareneingangspruefung> findeNachWareneingangId(
WareneingangId id
);
}
```

View file

@ -0,0 +1,162 @@
# Quality BC (HACCP/QM) - Detailliertes Domain Model
**Bounded Context:** Quality
**Domain Type:** CORE
**Verantwortung:** HACCP-Compliance, Qualitätsmanagement, Audit-Vorbereitung
---
## Aggregates
### 1. TemperatureLog (Aggregate Root)
```
TemperatureLog
├── TemperatureLogId (VO)
├── MeasurementPoint (VO: COLD_ROOM | FREEZER | DISPLAY_COUNTER | PRODUCTION_ROOM)
├── DeviceId (VO) - Reference to Equipment
├── MeasuredAt (VO: Timestamp)
├── Temperature (VO: with unit °C)
├── MeasuredBy (VO: UserId)
├── CriticalLimitMin (VO)
├── CriticalLimitMax (VO)
└── Status (VO: OK | WARNING | CRITICAL)
Invariants:
- Temperature must be within physically possible range (-50°C to +50°C)
- MeasuredAt cannot be in the future
- Status = CRITICAL if temperature outside critical limits
- Status = WARNING if temperature close to limits (within 10%)
- CriticalLimitMin < CriticalLimitMax
```
### 2. CleaningRecord (Aggregate Root)
```
CleaningRecord
├── CleaningRecordId (VO)
├── Area (VO: PRODUCTION_ROOM | COLD_STORAGE | SALES_COUNTER | EQUIPMENT)
├── CleaningPlanId (VO) - Reference to CleaningPlan
├── ScheduledFor (VO: Date)
├── CompletedAt (VO: Timestamp)
├── CompletedBy (VO: UserId)
├── ChecklistItems[] (Entity)
│ ├── Item (VO: "Floor mopped", "Surfaces disinfected")
│ ├── Checked (VO: boolean)
│ └── Remarks (VO: optional)
└── OverallRemarks (VO)
Invariants:
- All checklist items must be checked to complete
- CompletedAt must be >= ScheduledFor
- Cannot complete without CompletedBy
- Cannot modify after completion
```
### 3. GoodsReceiptInspection (Aggregate Root)
```
GoodsReceiptInspection
├── InspectionId (VO)
├── GoodsReceiptId (VO) - Reference to Procurement BC
├── InspectedAt (VO)
├── InspectedBy (VO: UserId)
├── TemperatureCheck (Entity)
│ ├── MeasuredTemperature (VO)
│ ├── ExpectedRange (VO)
│ └── Status (VO: PASSED | FAILED)
├── VisualInspection (Entity)
│ ├── PackagingIntact (VO: boolean)
│ ├── ColorAppearance (VO: NORMAL | ABNORMAL)
│ ├── SmellTest (VO: NORMAL | ABNORMAL)
│ └── Remarks (VO)
├── MHDCheck (Entity)
│ ├── ExpiryDate (VO)
│ ├── DaysUntilExpiry (VO)
│ ├── MinimumAcceptableDays (VO)
│ └── Status (VO: PASSED | FAILED)
├── DocumentCheck (Entity)
│ ├── DeliveryNoteReceived (VO: boolean)
│ ├── VeterinaryCertificateReceived (VO: boolean)
│ ├── QualityCertificates[] (VO)
│ └── AllDocumentsComplete (VO: boolean)
├── SupplierBatchNumber (VO) - For traceability!
└── FinalResult (VO: ACCEPTED | REJECTED | CONDITIONALLY_ACCEPTED)
Invariants:
- All checks must be performed before FinalResult can be set
- If REJECTED, OverallRemarks must be provided
- Temperature must be within acceptable range for ACCEPTED
- MHD must have minimum days for ACCEPTED
- VeterinaryCertificate required for meat products
```
### 4. TrainingRecord (Aggregate Root)
```
TrainingRecord
├── TrainingRecordId (VO)
├── EmployeeId (VO: UserId)
├── TrainingType (VO: HACCP | HYGIENE | FOOD_SAFETY | EQUIPMENT_OPERATION)
├── TrainingDate (VO)
├── ValidUntil (VO) - Auffrischung notwendig
├── Trainer (VO) - Internal or external
├── CertificateNumber (VO)
├── CertificateDocumentUrl (VO)
└── Status (VO: VALID | EXPIRED | REVOKED)
Invariants:
- ValidUntil must be after TrainingDate
- Status = EXPIRED if ValidUntil < TODAY
- Cannot revoke without reason
```
### 5. MaintenanceRecord (Aggregate Root)
```
MaintenanceRecord
├── MaintenanceRecordId (VO)
├── EquipmentId (VO)
├── MaintenanceType (VO: SCHEDULED | REPAIR | INSPECTION)
├── ScheduledFor (VO: Date)
├── PerformedAt (VO: Timestamp)
├── PerformedBy (VO) - Internal staff or external company
├── Findings (VO)
├── Actions (VO)
├── NextMaintenanceDue (VO: Date)
└── Status (VO: COMPLETED | PENDING | FAILED)
Invariants:
- PerformedAt must be >= ScheduledFor
- If FAILED, Findings and Actions must be documented
- NextMaintenanceDue must be calculated based on maintenance interval
```
## Repository Interfaces
```java
public interface TemperatureLogRepository {
Result<RepositoryError, Void> save(TemperatureLog log);
Result<RepositoryError, List<TemperatureLog>> findByPeriod(
LocalDate from, LocalDate to
);
Result<RepositoryError, List<TemperatureLog>> findCritical();
}
public interface GoodsReceiptInspectionRepository {
Result<RepositoryError, Void> save(GoodsReceiptInspection inspection);
Result<RepositoryError, GoodsReceiptInspection> findByGoodsReceiptId(
GoodsReceiptId id
);
}
```
## Domain Events
```java
TemperatureCriticalLimitExceeded(TemperatureLogId, MeasurementPoint, Temperature)
CleaningOverdue(CleaningPlanId, Area, LocalDate scheduledFor)
GoodsReceiptRejected(InspectionId, GoodsReceiptId, String reason)
TrainingExpiringSoon(TrainingRecordId, UserId, LocalDate expiryDate)
MaintenanceOverdue(EquipmentId, LocalDate scheduledFor)
```

View file

@ -0,0 +1,42 @@
# Labeling BC - Detailliertes Domain Model
**Bounded Context:** Labeling
**Domain Type:** CORE
**Verantwortung:** Automatische Berechnung von Nährwerten/Allergenen, Etikettengenerierung
## Aggregates
### ProductLabel (Aggregate Root)
```
ProductLabel
├── ProductLabelId
├── ArticleId - Reference to Master Data
├── RecipeId - Reference to Production BC (for auto-calculation)
├── ProductName
├── Manufacturer
├── Ingredients[] (Entity) - From Recipe, sorted descending by weight
├── Allergens[] (VO) - Auto-calculated from ingredients
├── TraceDeclarations[] (VO) - "May contain traces of..."
├── NutritionFacts (Entity) - Auto-calculated from recipe
│ ├── EnergyKJ, EnergyKcal, Fat, SaturatedFat, Carbs, Sugars, Protein, Salt
│ └── CalculationBase (PER_100G | PER_PORTION)
├── QualityLabels[] (VO) - Bio, Regional, Animal Welfare
├── OriginLabeling (VO)
└── LabelVersion
Invariants:
- All EU allergens must be declared
- Ingredients sorted by quantity (highest first) - EU regulation
- Nutrition facts must sum correctly from recipe
- If BIO label, all ingredients must be BIO-certified
- Allergens automatically inherited from ingredients (no manual override!)
```
## Use Cases
```java
GenerateLabelFromRecipe - Calculates nutrition & allergens from recipe
PrintLabelAtScale - Sends label to scale for printing with current weight
UpdateAllergenMatrix - Regenerates allergen matrix for all products
```

View 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!
```

View file

@ -0,0 +1,68 @@
# Inventory BC - Detailliertes Domain Model
**Bounded Context:** Inventory
**Domain Type:** CORE
**Verantwortung:** Chargen-basierte Bestandsführung, Rückverfolgbarkeit, MHD-Tracking
## Aggregates
### Stock (Aggregate Root)
```
Stock
├── StockId
├── ArticleId - Reference to Master Data
├── StorageLocationId
├── BranchId
├── StockLevel (Quantity) - Current total
├── Batches[] (Entity) - Batch-level inventory (CRITICAL!)
│ ├── BatchId - ProductionBatchId OR SupplierBatchId
│ ├── BatchType (PRODUCED | PURCHASED)
│ ├── Quantity
│ ├── ExpiryDate (MHD)
│ ├── ReceivedAt
│ └── Status (AVAILABLE | RESERVED | EXPIRED | SOLD)
├── MinimumStockLevel
└── ReorderPoint
Invariants:
- StockLevel = SUM(Batches.Quantity where Status = AVAILABLE)
- Cannot withdraw more than available
- FEFO enforced: oldest expiry first
- Negative stock not allowed
- Expired batches must have Status = EXPIRED
```
### StockMovement (Aggregate Root) - Event Sourcing candidate!
```
StockMovement
├── StockMovementId
├── ArticleId
├── BatchId - CRITICAL for traceability
├── MovementType (GOODS_RECEIPT | PRODUCTION_OUTPUT | SALE |
│ INTER_BRANCH_TRANSFER | WASTE | ADJUSTMENT)
├── FromLocation, ToLocation
├── FromBranch, ToBranch
├── Quantity
├── MovementDate
├── PerformedBy (UserId)
├── Reason (for WASTE/ADJUSTMENT)
├── ReferenceDocument (GoodsReceiptId, ProductionOrderId, InvoiceId)
└── TraceabilityChain - Link to upstream/downstream
Invariants:
- Quantity must be positive
- GOODS_RECEIPT: FromLocation = null
- SALE/WASTE: ToLocation = null
- INTER_BRANCH_TRANSFER: FromBranch != ToBranch
- All movements must reference a Batch
```
## Domain Events
```java
StockLevelBelowMinimum(ArticleId, BranchId, Quantity)
BatchExpiringSoon(BatchId, ArticleId, ExpiryDate)
StockMovementRecorded(StockMovementId, BatchId, MovementType)
```

View file

@ -0,0 +1,83 @@
# Procurement BC - Detailliertes Domain Model
**Bounded Context:** Procurement
**Domain Type:** CORE
**Verantwortung:** Bedarfsplanung, Bestellwesen, Wareneingangskontrolle, Lieferanten-QM
## Aggregates
### PurchaseOrder (Aggregate Root)
```
PurchaseOrder
├── PurchaseOrderId
├── SupplierId
├── OrderDate
├── RequestedDeliveryDate
├── Status (DRAFT | ORDERED | CONFIRMED | PARTIALLY_RECEIVED | FULLY_RECEIVED | CANCELLED)
├── OrderedBy (UserId)
├── BranchId
├── Items[] (Entity)
│ ├── ArticleId
│ ├── OrderedQuantity
│ ├── ReceivedQuantity - Updated on goods receipt
│ └── UnitPrice
├── TotalAmount - Calculated
└── PaymentTerms
Invariants:
- Status cannot skip states
- TotalAmount = SUM(Items.Quantity * Items.UnitPrice)
- ReceivedQuantity <= OrderedQuantity per item
- FULLY_RECEIVED when all items received
```
### GoodsReceipt (Aggregate Root)
```
GoodsReceipt
├── GoodsReceiptId
├── PurchaseOrderId
├── SupplierId
├── SupplierDeliveryNote
├── ReceivedAt
├── ReceivedBy (UserId)
├── Items[] (Entity)
│ ├── ArticleId
│ ├── OrderedQuantity
│ ├── ReceivedQuantity
│ ├── SupplierBatchNumber - CRITICAL for traceability!
│ ├── ExpiryDate (MHD)
│ └── QualityStatus (ACCEPTED | REJECTED | PENDING_INSPECTION)
├── QualityInspectionId - Reference to Quality BC
├── Documents[] (Entity) - Photos/PDFs
└── Status (PENDING_INSPECTION | ACCEPTED | REJECTED)
Invariants:
- Cannot accept without completed quality inspection
- All items must have SupplierBatchNumber
- Veterinary certificate required for meat
- Triggers StockMovement when accepted
```
### DemandPlan (Aggregate Root)
```
DemandPlan
├── DemandPlanId
├── PlanningPeriod (Week or Month)
├── BranchId
├── GeneratedAt
├── Items[] (Entity)
│ ├── ArticleId
│ ├── CurrentStock - Snapshot from Inventory
│ ├── PlannedConsumption - From ProductionOrders
│ ├── AverageConsumption - Historical
│ ├── SuggestedOrderQuantity - Calculated
│ └── MinimumOrderQuantity - Supplier constraint
└── Status (DRAFT | APPROVED | ORDERED)
Invariants:
- SuggestedOrderQuantity = PlannedConsumption - CurrentStock + SafetyStock
- If < MinimumOrderQuantity, use MinimumOrderQuantity
```

View file

@ -0,0 +1,80 @@
# Filiales BC - Detailliertes Domain Model
**Bounded Context:** Filiales
**Domain Type:** CORE
**Verantwortung:** Mehrfilialen-Management, Interfilial-Transfers, zentrale Produktion
## Aggregates
### Branch (Aggregate Root)
```
Branch
├── BranchId
├── BranchName
├── BranchType (PRODUCTION_AND_SALES | SALES_ONLY | PRODUCTION_ONLY)
├── Address
├── OpeningHours
├── Status (ACTIVE | INACTIVE | CLOSED)
├── Manager (UserId)
├── ContactInfo
├── Capabilities
│ ├── CanProduce (boolean)
│ ├── CanSell (boolean)
│ └── CanReceiveGoods (boolean)
└── AssignedEmployees[] (UserId)
Invariants:
- BranchType must match Capabilities
- Manager must be in AssignedEmployees
- Cannot close with pending production orders
```
### InterBranchTransfer (Aggregate Root)
```
InterBranchTransfer
├── TransferId
├── FromBranch (BranchId)
├── ToBranch (BranchId)
├── TransferDate
├── RequestedBy (UserId)
├── Status (REQUESTED | APPROVED | IN_TRANSIT | RECEIVED | CANCELLED)
├── Items[] (Entity)
│ ├── ArticleId
│ ├── BatchId - CRITICAL for traceability!
│ ├── Quantity
│ ├── ExpiryDate
│ └── ReceivedQuantity
├── ShippedAt
├── ReceivedAt
└── TransportDocumentId
Invariants:
- FromBranch != ToBranch
- Status: REQUESTED → APPROVED → IN_TRANSIT → RECEIVED
- ShippedAt < ReceivedAt
- Triggers two StockMovements (out + in)
```
### DistributionPlan (Aggregate Root)
```
DistributionPlan
├── DistributionPlanId
├── ProductionBatchId - From Production BC
├── ProducingBranch (BranchId)
├── PlannedFor (Date)
├── Status (PLANNED | IN_DISTRIBUTION | COMPLETED)
├── Distributions[] (Entity)
│ ├── TargetBranch (BranchId)
│ ├── AllocatedQuantity
│ ├── TransferId - Link to InterBranchTransfer
│ └── DeliveryStatus (PENDING | SHIPPED | DELIVERED)
├── TotalQuantityProduced
└── RemainingAtCentral
Invariants:
- SUM(AllocatedQuantity) + RemainingAtCentral = TotalQuantityProduced
- ProducingBranch must be PRODUCTION_ONLY or PRODUCTION_AND_SALES
```

View file

@ -0,0 +1,111 @@
# Supporting BCs - Übersicht
**Domain Type:** SUPPORTING
**DDD-Aufwand:** Vereinfacht (weniger Komplexität als Core)
---
## Master Data BC
### Article (Aggregate Root)
```
Article
├── ArticleId
├── ArticleName
├── ArticleNumber (SKU)
├── Category (ProductCategory)
├── SalesUnits[] (Entity)
│ ├── Unit (PIECE_FIXED | KG | 100G | PIECE_VARIABLE)
│ ├── PriceModel (FIXED | WEIGHT_BASED)
│ └── Price (Money)
├── Status (ACTIVE | INACTIVE)
└── Supplier References[]
```
### Supplier (Aggregate Root)
```
Supplier
├── SupplierId
├── Name
├── Address
├── PaymentTerms
├── QualityCertificates[]
└── Rating (Quality, Delivery, Price)
```
### Customer (Aggregate Root)
```
Customer
├── CustomerId
├── CustomerType (B2C | B2B)
├── Name
├── DeliveryAddresses[]
├── FrameContract (optional)
└── Preferences (Bio, Regional, etc.)
```
---
## Sales BC
### Order (Aggregate Root)
```
Order
├── OrderId
├── OrderType (PRE_ORDER | B2B_ORDER | WALK_IN)
├── CustomerId
├── OrderDate
├── DeliveryDate
├── Items[]
└── Status (OPEN | CONFIRMED | FULFILLED)
```
### Invoice (Aggregate Root)
```
Invoice
├── InvoiceId
├── OrderId
├── InvoiceDate
├── Items[]
├── TotalAmount
└── PaymentStatus (OPEN | PAID | OVERDUE)
```
---
## Scale Integration BC
### ScaleSyncJob (Aggregate Root)
```
ScaleSyncJob
├── SyncJobId
├── ScaleId
├── SyncType (ARTICLES | PRICES | LABELS)
├── Status (PENDING | IN_PROGRESS | COMPLETED | FAILED)
└── SyncedAt
```
### BondDataImport (Aggregate Root)
```
BondDataImport
├── ImportId
├── ImportDate
├── BranchId
├── ScaleId
├── SalesData[] (Entity)
│ ├── ArticleId
│ ├── Quantity
│ ├── Price
│ ├── SoldAt
│ └── BatchId (if traceable)
└── Status (PENDING | IMPORTED | FAILED)
Triggers: StockMovement for each sale
```

180
docs/mvp/ddd/README.md Normal file
View file

@ -0,0 +1,180 @@
# DDD Domain Model - Effigenix Fleischerei-ERP
**Sprache:** Deutsch (für Domain-Experten)
**Code:** Englisch (siehe Java Style Guide)
**Erstellt:** 2026-02-17
**Technologie:** Java 21+ mit DDD + Clean Architecture
---
## 📚 Dokumentationsstruktur
### Übersichtsdokumente
| Dokument | Beschreibung |
|----------|--------------|
| `00-overview.md` | Gesamtüberblick, DDD-Phasen, Projektziele |
| `01-domain-classification.md` | Core/Supporting/Generic Klassifizierung mit Begründungen |
| `02-bounded-contexts.md` | **Context Map** (Mermaid) + Kontextbeziehungen |
| `03-ubiquitous-language.md` | Glossar DE/EN für alle Bounded Contexts |
### Kern-Domäne (Core) - 7 Kontexte
| Dokument | Bounded Context | Aggregates |
|----------|-----------------|------------|
| `04-produktions-kontext.md` | **Produktion** | Rezept, Charge, Produktionsauftrag |
| `05-qualitaets-kontext.md` | **Qualität (HACCP/QM)** | Temperaturprotokoll, Reinigungsnachweis, Wareneingangsprüfung, Schulungsnachweis, Wartungsprotokoll |
| `06-labeling-bc.md` | **Deklaration** | Produktetikett, Allergene-Matrix |
| `07-bestandsfuehrungs-kontext.md` | **Bestandsführung** | Bestand, Bestandsbewegung |
| `08-procurement-bc.md` | **Beschaffung** | Bestellung, Wareneingang, Bedarfsplan |
| `09-filiales-bc.md` | **Filialen** | Filiale, Interfilial-Transfer, Verteilungsplan |
### Unterstützende Domäne (Supporting) - 3 Kontexte
| Dokument | Bounded Context | Aggregates |
|----------|-----------------|------------|
| `10-supporting-bcs.md` | **Stammdaten** | Artikel, Lieferant, Kunde |
| `10-supporting-bcs.md` | **Verkauf** | Auftrag, Rechnung, Lieferschein |
| `10-supporting-bcs.md` | **Waagen-Integration** | Synchronisations-Job, Bondaten-Import |
---
## 🎯 Verwendung für Domain-Experten
### Für Besprechungen mit Fleischerei-Experten
**Diese Dokumente verwenden deutsche Begriffe**, um die Kommunikation zu erleichtern:
- ✅ **Rezept** statt Recipe
- ✅ **Charge** statt Batch
- ✅ **Wareneingang** statt Goods Receipt
- ✅ **Rückverfolgbarkeit** statt Traceability
**Im Code werden englische Begriffe verwendet** (siehe `03-ubiquitous-language.md`):
```java
// Dokumentation: Rezept
// Code: Recipe
public class Recipe { ... }
// Dokumentation: Charge
// Code: Batch
public class Batch { ... }
```
---
## 🔍 Wichtige Konzepte
### 1. Aggregate
Ein **Aggregat** ist eine Gruppe von Objekten, die immer konsistent zusammen geändert werden müssen.
**Beispiel: Rezept-Aggregat**
```
Rezept (Aggregate Root)
├── RezeptId
├── Name
├── Zutaten[] (Teil des Aggregats)
├── Produktionsschritte[] (Teil des Aggregats)
└── Status
```
- **Aggregate Root** = Einstiegspunkt (nur Rezept ist von außen zugänglich)
- **Invarianten** = Regeln, die IMMER gelten müssen (z.B. "Rezept muss mind. 1 Zutat haben")
- **Transaktionsgrenze** = Ein Rezept = eine Datenbanktransaktion
### 2. Wertobjekte (Value Objects)
**Unveränderliche Objekte**, die nur durch ihre Werte definiert sind.
**Beispiel:**
```java
// Zwei Geldbeträge mit gleichen Werten sind identisch
Money betrag1 = Money.of(100, "EUR");
Money betrag2 = Money.of(100, "EUR");
// betrag1.equals(betrag2) = true
// Im Gegensatz zu Entities (haben ID)
Charge charge1 = Charge.of(ChargenId.of("CHARGE-001"), ...);
Charge charge2 = Charge.of(ChargenId.of("CHARGE-002"), ...);
// charge1.equals(charge2) = false (verschiedene IDs)
```
### 3. Rückverfolgbarkeit (Traceability)
**KRITISCH für Fleischerei-Betriebe!**
```mermaid
graph LR
A[Lieferanten-Charge<br/>SUPPLIER-12345] -->|verwendet in| B[Produktions-Charge<br/>BATCH-2026-02-17-001]
B -->|verkauft als| C[Rechnung<br/>INV-2026-02-20-042]
C -->|an| D[Kunde<br/>CUST-123]
style A fill:#ffe6e6
style B fill:#e6f3ff
style C fill:#e6ffe6
style D fill:#fff9e6
```
**Bei Rückruf:**
1. Finde alle Produktionschargen mit betroffener Lieferanten-Charge
2. Finde alle Verkäufe dieser Produktionschargen
3. Informiere betroffene Kunden
**Code:** Siehe `07-bestandsfuehrungs-kontext.md` - Rückverfolgbarkeits-Beispiel
---
## 📊 Context Map
Die **Context Map** zeigt, wie die verschiedenen Bounded Contexts zusammenarbeiten:
**Siehe:** `02-bounded-contexts.md`
**Kernaussagen:**
- **Produktion** liefert Rezeptdaten an **Deklaration** (für Nährwertberechnung)
- **Produktion** verbraucht/produziert in **Bestandsführung**
- **Qualität** prüft **Wareneingang** (aus Beschaffung)
- **Bestandsführung** ist zentral - alle schreiben/lesen dort
---
## ✅ Validierung mit Domain-Experten
### Checkliste für Review-Meetings
**Vorbereitung:**
1. Relevanten Bounded Context öffnen (z.B. `04-produktions-kontext.md`)
2. Aggregate-Struktur durchgehen
3. Invarianten besprechen
**Fragen an Experten:**
- ✅ Sind alle Zutaten erfasst, die in einem Rezept sein können?
- ✅ Gibt es weitere Produktionsschritte, die dokumentiert werden müssen?
- ✅ Welche Grenzwerte gelten für Temperaturprotokolle?
- ✅ Welche Dokumente sind bei Wareneingang Pflicht?
- ✅ Wie läuft ein Interfilial-Transfer praktisch ab?
**Ergebnis:**
- Dokumentation anpassen basierend auf Feedback
- Neue Invarianten hinzufügen
- Fehlende Aggregate/Entities ergänzen
---
## 🚀 Nächste Schritte
1. ✅ **Phase 0-3 abgeschlossen** - Bounded Contexts & Aggregates modelliert
2. ⏳ **Phase 4: Invarianten** - Detaillierte Geschäftsregeln pro Aggregat
3. ⏳ **Phase 5: Code-Generierung** - Java 21+ Code aus Modellen
4. ⏳ **Phase 6: Validierung** - DDD-Rules-Checklist
---
## 📞 Kontakt
**Für Rückfragen zum Domain Model:**
- Dokumentation: `docs/mvp/ddd/`
- Code-Beispiele: `.claude/skills/ddd-model/languages/java/templates/`
- DDD-Regeln: `.claude/skills/ddd-model/rules/ddd-rules.md`