Implementiere Marktmanagement-Datenmodell

This commit is contained in:
gitea_admin committed 2026-09-19 08:30:01 +02:00
1 parent 8492db2e39
commit 2607f448ad
4 files changed
+527 -3

No files matched your search

+215
View File
@@ -0,0 +1,215 @@
BEGIN;
-- Market Management base schema.
-- Fachliche Ownership: market_management.
CREATE TABLE market_category (
id BIGSERIAL PRIMARY KEY,
code TEXT NOT NULL,
name TEXT NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX uq_market_category_code ON market_category(code);
CREATE UNIQUE INDEX uq_market_category_name ON market_category(name);
INSERT INTO market_category (code, name)
VALUES
('herbstmarkt', 'Herbstmärkte'),
('weihnachtsmarkt', 'Weihnachtsmärkte'),
('fruehlingsmarkt', 'Frühlingsmärkte'),
('ostermarkt', 'Ostermärkte'),
('kunsthandwerksmarkt', 'Kunsthandwerksmärkte'),
('regionalmarkt', 'Regionalmärkte'),
('weitere_geeignete_maerkte', 'Weitere geeignete Märkte');
CREATE TABLE market_municipality_reference (
id BIGSERIAL PRIMARY KEY,
bfs_municipality_number INTEGER NOT NULL,
name TEXT NOT NULL,
canton_code TEXT NOT NULL,
data_version DATE NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX uq_market_municipality_reference_bfs_number_version
ON market_municipality_reference(bfs_municipality_number, data_version);
CREATE INDEX idx_market_municipality_reference_canton
ON market_municipality_reference(canton_code, name);
CREATE TABLE market_definition (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
category_id BIGINT NOT NULL REFERENCES market_category(id) ON DELETE RESTRICT,
archived_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX uq_market_definition_name ON market_definition(name);
CREATE INDEX idx_market_definition_category ON market_definition(category_id);
CREATE TABLE market_occurrence (
id BIGSERIAL PRIMARY KEY,
market_definition_id BIGINT NOT NULL REFERENCES market_definition(id) ON DELETE RESTRICT,
status TEXT NOT NULL DEFAULT 'open',
place TEXT,
address TEXT,
municipality_reference_id BIGINT REFERENCES market_municipality_reference(id) ON DELETE RESTRICT,
notes TEXT,
registration_deadline DATE,
request_or_registration_date DATE,
confirmation_date DATE,
payment_deadline DATE,
stand_size TEXT,
table_available BOOLEAN,
table_rentable BOOLEAN,
own_table_required BOOLEAN,
shelter_available BOOLEAN,
power_available BOOLEAN,
water_available BOOLEAN,
lighting_available BOOLEAN,
other_infrastructure TEXT,
own_stand_requirements TEXT,
travel_address TEXT,
travel_distance_km NUMERIC(10, 2),
travel_duration_minutes INTEGER,
parking_options TEXT,
delivery_access TEXT,
direct_unload_possible BOOLEAN,
storage_options TEXT,
travel_notes TEXT,
payment_status TEXT NOT NULL DEFAULT 'unpaid',
payment_date DATE,
paid_amount NUMERIC(14, 2),
payment_method_code TEXT,
cancellation_terms TEXT,
revenue_share_terms TEXT,
other_participation_conditions TEXT,
contract_folder_url TEXT,
archived_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
CONSTRAINT chk_market_occurrence_status CHECK (
status IN ('open', 'waitlist', 'registered', 'confirmation_pending', 'confirmed', 'cancelled', 'completed')
),
CONSTRAINT chk_market_occurrence_payment_status CHECK (
payment_status IN ('paid', 'unpaid')
),
CONSTRAINT chk_market_occurrence_travel_distance CHECK (
travel_distance_km IS NULL OR travel_distance_km >= 0
),
CONSTRAINT chk_market_occurrence_travel_duration CHECK (
travel_duration_minutes IS NULL OR travel_duration_minutes >= 0
),
CONSTRAINT chk_market_occurrence_paid_amount CHECK (
paid_amount IS NULL OR paid_amount >= 0
)
);
CREATE INDEX idx_market_occurrence_market ON market_occurrence(market_definition_id);
CREATE INDEX idx_market_occurrence_status ON market_occurrence(status);
CREATE INDEX idx_market_occurrence_municipality
ON market_occurrence(municipality_reference_id);
CREATE INDEX idx_market_occurrence_archived_at ON market_occurrence(archived_at);
CREATE TABLE market_occurrence_organizer (
id BIGSERIAL PRIMARY KEY,
occurrence_id BIGINT NOT NULL UNIQUE REFERENCES market_occurrence(id) ON DELETE RESTRICT,
organization_contact_id BIGINT NOT NULL,
contact_person_id BIGINT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_market_occurrence_organizer_organization
ON market_occurrence_organizer(organization_contact_id);
CREATE INDEX idx_market_occurrence_organizer_contact_person
ON market_occurrence_organizer(contact_person_id);
CREATE TABLE market_occurrence_day (
id BIGSERIAL PRIMARY KEY,
occurrence_id BIGINT NOT NULL REFERENCES market_occurrence(id) ON DELETE RESTRICT,
date DATE NOT NULL,
stand_start_time TIME,
stand_end_time TIME,
setup_start_time TIME,
setup_end_time TIME,
delivery_start_time TIME,
delivery_end_time TIME,
observations TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
CONSTRAINT chk_market_occurrence_day_stand_time CHECK (
stand_start_time IS NULL OR stand_end_time IS NULL OR stand_end_time >= stand_start_time
),
CONSTRAINT chk_market_occurrence_day_setup_time CHECK (
setup_start_time IS NULL OR setup_end_time IS NULL OR setup_end_time >= setup_start_time
),
CONSTRAINT chk_market_occurrence_day_delivery_time CHECK (
delivery_start_time IS NULL OR delivery_end_time IS NULL OR delivery_end_time >= delivery_start_time
)
);
CREATE UNIQUE INDEX uq_market_occurrence_day_date ON market_occurrence_day(occurrence_id, date);
CREATE INDEX idx_market_occurrence_day_date ON market_occurrence_day(date);
CREATE TABLE market_occurrence_cost_item (
id BIGSERIAL PRIMARY KEY,
occurrence_id BIGINT NOT NULL REFERENCES market_occurrence(id) ON DELETE RESTRICT,
cost_type TEXT NOT NULL,
description TEXT,
gross_amount NUMERIC(14, 2) NOT NULL,
tax_status TEXT NOT NULL,
tax_rate NUMERIC(5, 2) NOT NULL DEFAULT 0,
tax_amount NUMERIC(14, 2) GENERATED ALWAYS AS (
CASE
WHEN tax_status = 'tax_free' THEN 0
ELSE ROUND(gross_amount - gross_amount / (1 + tax_rate / 100), 2)
END
) STORED,
net_amount NUMERIC(14, 2) GENERATED ALWAYS AS (
gross_amount - CASE
WHEN tax_status = 'tax_free' THEN 0
ELSE ROUND(gross_amount - gross_amount / (1 + tax_rate / 100), 2)
END
) STORED,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
CONSTRAINT chk_market_occurrence_cost_gross_nonnegative CHECK (gross_amount >= 0),
CONSTRAINT chk_market_occurrence_cost_tax_status CHECK (tax_status IN ('tax_free', 'taxable')),
CONSTRAINT chk_market_occurrence_cost_tax_rate CHECK (
(tax_status = 'tax_free' AND tax_rate = 0)
OR (tax_status = 'taxable' AND tax_rate > 0)
)
);
CREATE INDEX idx_market_occurrence_cost_item_occurrence
ON market_occurrence_cost_item(occurrence_id);
CREATE TABLE market_mcp_idempotency (
id BIGSERIAL PRIMARY KEY,
user_subject TEXT NOT NULL,
client_id TEXT NOT NULL,
idempotency_key TEXT NOT NULL,
tool_name TEXT NOT NULL,
request_hash TEXT NOT NULL,
result_status TEXT NOT NULL,
result_entity_type TEXT,
result_entity_id TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
expires_at TIMESTAMP NOT NULL,
CONSTRAINT chk_market_mcp_idempotency_result_status CHECK (
result_status IN ('processing', 'succeeded', 'failed')
)
);
CREATE UNIQUE INDEX uq_market_mcp_idempotency_client_key
ON market_mcp_idempotency(client_id, idempotency_key);
CREATE INDEX idx_market_mcp_idempotency_expiry ON market_mcp_idempotency(expires_at);
COMMIT;
@@ -58,8 +58,8 @@ Logistik, Kosten, Zahlung, Vertragslink und Notizen.
- Datum des Durchführungstags
- Standzeit und tagesbezogene Durchführungszeiten
- Bruttoerlös
- Anzahl verkaufter Artikel
- Bruttoerlös aus zugeordneten OTC-Verkäufen
- Anzahl verkaufter Artikel aus zugeordneten OTC-Positionen
- Ereignisse und Beobachtungen
### Deterministische Referenzwerte
@@ -87,7 +87,8 @@ Logistik, Kosten, Zahlung, Vertragslink und Notizen.
Durchführung gespeichert.
- Mehrere Durchführungen werden nicht als wiederholte Spalten im Markt
gespeichert.
- Tageserlöse und Tagesverkaufszahlen werden nur am Durchführungstag erfasst.
- Tageserlöse und Tagesverkaufszahlen werden nur für den Durchführungstag aus
zugeordneten OTC-Verkäufen abgeleitet.
- Aggregierte Werte für Durchführung oder Markt werden aus den untergeordneten
Durchführungstagen abgeleitet oder als klar definierte Auswertung bereitgestellt.
- MCP-Schreiboperationen müssen eindeutig an die betroffene Ebene gebunden sein.
@@ -102,6 +102,12 @@ Normativ:
- Zahlungsstatus und Marktstatus sind unabhängig
- der Zahlungsstatus ist manuell `paid` oder `unpaid`
Minimale Pflichtfelder über alle Statuswerte hinweg:
- `market_definition_id`
- initialer Status `open`
- mindestens ein Durchführungstag
### 3.4 `market_occurrence_organizer`
Veranstalterbeziehung einer Durchführung.
@@ -124,6 +130,25 @@ Normativ:
- Stand-, Aufbau-, Abbau- und tagesbezogene Anlieferungszeiten gehören hierher
- Ereignisse und Beobachtungen gehören hierher
- OTC-Verkäufe werden eindeutig diesem Datensatz zugeordnet
- Bruttoerlös und Artikelmenge werden aus den zugeordneten OTC-Verkäufen
abgeleitet und nicht manuell gespeichert
Minimale Pflichtfelder:
- `occurrence_id`
- `date`
### Mindestpflichtfelder
Über alle Statuswerte hinweg gelten nur diese Pflichtfelder:
- Marktdefinition: `name`, `category_id`
- Durchführung: `market_definition_id`, initialer Status `open`, mindestens ein
Durchführungstag
- Durchführungstag: `occurrence_id`, `date`
Alle weiteren Felder bleiben bei der Anlage optional und werden unabhängig vom
Status ergänzt.
### 3.6 `market_occurrence_cost_item`
+283
View File
@@ -0,0 +1,283 @@
# Technische Architektur
Stand: 2026-04-08
Status: Verbindlicher Architektur-Master
Dokumentklasse: normative
## 1. Zweck und Geltung
Dieses Dokument ist die verbindliche Architekturvorgabe fuer die Entwicklung hochmodularer Webportale in diesem Repository. Es steuert Analyse, Implementierung, Refactoring und Review durch Mensch und LLM.
Verbindliche Betriebsregel:
- Entwicklung und erste Integration erfolgen lokal auf `feature/<thema>` im lokalen Docker-Stack.
- `main` ist der gemeinsame, auf DEV betriebene Integrationsstand. Ein Feature wird erst nach lokaler Verifikation und expliziter Freigabe nach `main` gemergt.
- PROD wird ausschliesslich durch das bestehende Release-Skript mit einem expliziten Release-Tag eines in DEV getesteten `main`-Commits ausgerollt und nur nach expliziter User-Freigabe angefasst.
Es definiert verbindlich:
- Modul-, Prozess-, Frontend-, API- und Datenbankgrenzen
- Ownership und oeffentliche Schnittstellen
- Standard-Scope fuer Analyse und Implementierung
- Wiederverwendungs- und Redundanzregeln
- Regeln fuer neue Module, Submodule und zentrale Bausteine
Dieses Dokument ist kein Inventar konkreter Tabellen, Prozesse, Dateien oder Runtime-Artefakte. Solche Details liegen in den zustaendigen Modul-, Prozess- oder DB-Vertraegen.
## 2. Architekturprinzipien
### 2.1 Module
Ein Modul ist ein fachlich oder technisch gekapselter Contract, nicht nur eine Verzeichnisstruktur.
Verbindliche Regel:
- jedes Modul besitzt genau eine klar abgegrenzte Primaerverantwortung
- jedes Modul kapselt seine Domain-Logik, Datenzugriffe, externen Schnittstellen, internen Datenstrukturen und Speicherdetails
- jedes Modul definiert eine explizite oeffentliche Schnittstelle fuer andere Module
- jedes Modul besitzt definierte Owned Artefakte und Schreibrechte
- interne Implementierungen anderer Module duerfen nicht direkt genutzt werden
- jede Modulgrenze muss als potenzielle spaetere Service-Grenze behandelbar bleiben
### 2.2 Prozesse
Ein Prozess ist ein sequenzieller Hauptablauf innerhalb genau eines owning Hauptmoduls.
Verbindliche Regel:
- ein Hauptmodul besitzt beliebig viele Hauptprozesse; jeder Hauptprozess gehoert genau einem Hauptmodul
- ein Hauptprozess darf Submodule desselben Hauptmoduls nutzen
- fachliche Hauptprozesse ueber Hauptmodulgrenzen hinweg sind nicht zulaessig
- Batching, Parallelisierung, Retry, Resume, Fehlerklassifikation, Statusermittlung und Ergebnisvertrag gehoeren in den owning Prozessvertrag
- zusaetzliche harte Schreib-, Feld- oder Datenbankschranken sind nur zulaessig, wenn sie explizit im Prozess- oder DB-Vertrag festgelegt sind
Verbindliche Kettenarten:
- asynchrone Prozessketten: ein Startprozess stoesst Folgeprozesse an, diese laufen als eigene Runs weiter
- synchrone Prozessketten: Subprozesse haengen direkt aneinander; jeder Schritt stoesst nur den naechsten an
- der Kettenmodus muss pro Prozessvertrag explizit festgelegt sein
- die Wahl des Kettenmodus erfolgt jeweils nach fachlichem Sinn und Prozessvertrag
Aktive Prozesswahrheit:
`docs/modules/<modul>/<submodul>/processes/`
Verbindliche Regel:
- Prozess- und Sub-Prozess-Dokumente unter diesem Pfad sind die aktive fachliche Wahrheit
- Prozessnomenklatur folgt `component.subcomponent.process_name`
- jeder Hauptprozess und jeder Sub-Prozess besitzt genau einen owning Modulpfad
- `docs/system_components/` darf fuer aktive Prozessreferenzen nicht verwendet werden
### 2.3 Implementierung
Module, Prozesse und Sub-Prozesse sind technisch so scharf wie moeglich zu trennen.
Verbindliche Regel:
- jedes Modul besitzt eine interne API oder Zugriffsschicht
- Cross-Modul-Kommunikation erfolgt nur ueber definierte Modul-Schnittstellen
- direkte Zugriffe auf interne Implementierungen oder Daten anderer Module sind verboten
- neue APIs im geschuetzten Bereich sind standardmaessig `protected`; die Abo-Pruefung greift vor jeder Datenlieferung
- Module, Prozesse und Sub-Prozesse muessen getrennt entwickelbar, testbar, ausrollbar, ueberwachbar, debugbar und optimierbar sein
- technische Kopplung zwischen Prozessen und Sub-Prozessen ist auf das notwendige Minimum zu reduzieren
- Module duerfen keine impliziten Abhaengigkeiten besitzen, die eine spaetere Extraktion verhindern
### 2.4 Frontend
Das Frontend ist bewusst minimalistisch. Ziel ist eine kleine Zahl konsistenter, wiederverwendbarer UI-Bausteine statt vieler seiten- oder feature-spezifischer Sonderkomponenten.
Verbindliche Regel:
- Seiten und Views komponieren Daten, Layouts und Komponenten, erzeugen aber keine Fachlogik
- UI-Komponenten duerfen keine fachliche Primaerlogik, fachliche Dateninterpretation oder eigenstaendige Fachwahrheit besitzen
- fachliche UI-Komponenten, fachliche Darstellungsregeln und fachlich interpretierende UI-Logik gehoeren zum owning Fachmodul
- fachlich neutrale UI-Komponenten, Layout-Komponenten, Formatierungshelfer, Interaktionsmuster und Frontend-Utilities gehoeren nach `shared`
- neue oder geaenderte Frontend-Komponenten muessen zuerst gegen bestehende Komponenten, Patterns und `shared`-Bausteine geprueft werden
- Varianten bestehender Komponenten sind ueber Props, Konfiguration oder dokumentierte Erweiterungspunkte umzusetzen, nicht durch Copy-Paste
- Sortierung, Filterung und Suche werden serverseitig im owning Modul bzw. in der API umgesetzt; das Frontend rendert nur die Ergebnisse
- Karten, Tabellen, Filter, Tabs, Buttons, Statusanzeigen, Ladezustaende, Empty States, Fehlermeldungen und Detailansichten sind als wiederverwendbare Patterns zu behandeln
- seitenlokale Sonderkomponenten sind nur zulaessig, wenn sie nachweislich nicht sinnvoll wiederverwendbar sind
- API-Responses duerfen frontendnah komponiert sein, aber keine Fachlogik aus dem owning Modul in API oder Frontend verschieben
- Seiten mit externen Redirect- oder Callback-Flows muessen Browser-History und Ruecksprungverhalten bewusst behandeln; der verbindliche Detailvertrag liegt zentral unter `docs/modules/shared/ui_foundation/`.
### 2.4.1 Mehrsprachigkeit
Mehrsprachigkeit ist ein verbindlicher Teil der Frontend-Architektur und gilt identisch fuer statische Texte, dynamisch gerenderte Texte und interaktive UI-Elemente.
Verbindliche Regel:
- jeder sichtbare GUI-String muss ueber einen i18n-Key aufgeloest werden, unabhaengig davon, ob er serverseitig, clientseitig oder aus einer API-Komposition gerendert wird
- dazu gehoeren insbesondere Navigationslabels, Tab-Titel, Buttontexte, Menuepunkte, Tooltips, Hilfetexte, Platzhalter, Empty States, Ladezustände, Fehlermeldungen, Bestätigungsdialoge, ARIA-Labels und sonstige Screenreader-Texte
- source-code-Defaults sind nur Fallback- bzw. Seed-Werte; die Anzeige-Wahrheit wird pro Locale aus dem i18n-Store bezogen
- interaktive Elemente muessen sichtbare Beschriftung und funktionalen Wert strikt trennen: der angezeigte Text ist uebersetzbar, der Wert fuer State, Transport, Routing, Filterung und Backend-Verarbeitung bleibt technisch stabil und locale-unabhaengig
- Auswahlwerte, Keys, IDs, Data-Attribute, Option-Keys, Tab-Keys und sonstige Steuerungswerte duerfen nicht aus uebersetzten Labels abgeleitet werden
- Locale-Wechsel darf ausschliesslich die Anzeigeschicht wechseln; Funktion, Auswahlzustand, Navigation, Filterlogik und Datenuebergabe muessen identisch weiter funktionieren
- wenn Text im DOM sichtbar ist, gilt er als GUI-String und unterliegt dieser Regel auch dann, wenn er clientseitig zusammengesetzt wird
- zusammengesetzte UI-Texte duerfen keine fachliche Logik enthalten; sie kombinieren nur locale-gebundene Textbausteine mit stabilen technischen Werten
## 3. Modulmodell
### 3.1 Zulaessige Modulbereiche
Fachliche Hauptmodule:
- `company`
- `ratings`
- `groups`
- `lists`
- `market_management`
Technische Modulbereiche:
- `system`
- `shared`
Verbindliche Regel:
- fachliche Hauptmodule besitzen Business-Logik und fachliche Ownership
- `system` enthaelt nur technische Runtime-, Start-, Trigger- und Laufzeitlogik
- `shared` enthaelt nur fachlich neutrale, wiederverwendbare technische Bausteine
- konkrete Submodule, Prozesse, Artefakte und Schnittstellen werden in Modulvertraegen dokumentiert, nicht in diesem Architektur-Master
### 3.2 `shared`
`shared` ist der zentrale technische Querschnittsbereich.
Verbindliche Regel:
- `shared` darf keine Business-Logik, Fachwahrheit oder primaere fachliche Ownership besitzen
- `shared` darf Services, Komponenten, Helper, technische Schnittstellen, UI-nahe und API-nahe Bausteine bereitstellen
- ein Baustein gehoert nur nach `shared`, wenn er fachlich neutral ist und keine Verantwortung eines Fachmoduls uebernimmt
- fachliche Regeln, Dateninterpretation, Statuslogik und Entscheidungen bleiben im owning Fachmodul
- technische Querschnittslogik ohne fachlichen Inhalt muss in `shared` oder einem dort dokumentierten zentralen Baustein liegen, sobald Wiederverwendung ueber mehr als eine Stelle absehbar ist
### 3.3 `admin`
`admin` ist kein Hauptmodul und besitzt keine fachliche Ownership.
Verbindliche Regel:
- Admin-Funktionen sind nur Bedienoberflaechen auf bestehende Hauptmodule
- Admin-spezifische Fachlogik liegt im owning Hauptmodul
- ein separates fachliches Modul `admin` ist nicht zulaessig
### 3.4 `public/api/`
`public/api/` ist die HTTP-Service- und Kompositionsschicht des modularen Monolithen.
Verbindliche Regel:
- `public/api/` besitzt keine fachliche Primaerverantwortung
- `public/api/` darf Modul- oder Submodul-Schnittstellen fuer konkrete Frontend-User-Stories zusammensetzen
- `public/api/` bleibt duenn und enthaelt nur HTTP-nahe und technische Kompositionslogik
- fachliche Ownership darf nicht aus Modulen in `public/api/` wandern
## 4. Datenbankgrenze
Die Plattform nutzt aktuell genau eine gemeinsame Datenbank. Diese gemeinsame Datenbank hebt Modul- und Ownership-Grenzen nicht auf.
Verbindliche Regel:
- jede Tabelle, View, materialisierte View und DB-Funktion besitzt genau eine primaere Ownership
- primaere Ownership folgt genau einem Hauptmodul oder einem technischen Shared-Bereich
- nur das owning Modul definiert Struktur, Write-Logik, Constraints und fachliche Semantik eines DB-Artefakts
- direkte Cross-Modul-Writes in fremde Owned Tabellen sind verboten
- andere Module duerfen fremde Artefakte nur ueber definierte Schnittstellen des owning Moduls nutzen
- physische DB-Splits sind kein aktueller Standard und beduerfen einer expliziten Architekturentscheidung
Operative DB-Referenz:
`docs/database/module_db_ownership.md`
Verbindliche Regel:
- konkrete DB-Artefaktlisten gehoeren in die DB-Ownership-Dokumentation, nicht in diesen Architektur-Master
- bei Analyse, Refactoring und Implementierung wird der DB-Scope zuerst ueber Modul plus Owned DB-Artefakte bestimmt
- fremde DB-Artefakte duerfen nur bei deklarierter Leseabhaengigkeit, FK-Beziehung oder Integrationspruefung in den Scope aufgenommen werden
## 5. Dokumentation und Lesescope
Aktive Dokumentation ist komponentennah.
Verbindliche Regel:
- aktive Modul- und Submodul-Dokumentation liegt unter `docs/modules/<modul>/...`
- aktive technische Shared-Dokumentation liegt unter `docs/modules/shared/...`
- technische Runtime-Dokumentation liegt unter `docs/modules/system/...`
- fachliche Hauptprozesse liegen innerhalb des owning Modulbaums
- historische oder abgeloeste Dokumentation liegt ausserhalb des aktiven Modulbaums
- `docs/technical_architecture.md` bleibt der einzige aktive Architektur-Master
Standard-Lesescope:
- Arbeit an einem Modul: `docs/technical_architecture.md` plus `docs/modules/<modul>/...`
- Arbeit an einem Submodul: `docs/technical_architecture.md` plus `docs/modules/<modul>/<submodul>/...`
- `docs/modules/shared/...`: nur bei betroffener Shared-Schnittstelle oder moeglicher Wiederverwendung
- `docs/modules/system/...`: nur bei Runtime-, Queue-, Scheduler-, Trigger- oder Supervisor-Themen
- andere Module: nur bei explizitem Integrationsbedarf
## 6. Wiederverwendung und Redundanzverbot
Vor jeder Aenderung an App, Frontend, UI-Komponenten, UI-Patterns, API, Prozesslogik, Datenzugriff, Infrastruktur oder Dokumentation ist Wiederverwendung vor Neuerstellung zu pruefen.
Ziel ist eine Architektur ohne redundante Fachlogik, technische Inselloesungen, doppelte UI-Komponenten oder mehrfach gepflegte Varianten desselben Musters.
Verbindliche Regel:
- zuerst bestehende Muster, Komponenten, Services, Modul-Schnittstellen und `shared`-Bausteine pruefen
- vorhandene passende Muster muessen wiederverwendet, erweitert oder als zentrale Schnittstelle bereitgestellt werden
- fachliche Inhalte gehoeren immer in das owning Fachmodul und werden von dort ueber definierte Schnittstellen bereitgestellt
- fachliche Logik darf nicht in `shared`, `public/api/`, `admin`, Skripte oder fachfremde Bereiche ausgelagert oder dupliziert werden
- fachlich neutrale technische Logik gehoert nach `shared`, sofern sie wiederverwendbar ist oder Wiederverwendung absehbar ist
- Frontend-Darstellung, Interaktionslogik, Layout, Formatierung und Zustandsdarstellung sind als bestehende oder neue zentrale UI-Komponenten beziehungsweise UI-Patterns in `shared` zu pruefen
- neue `shared`-Bausteine sind nur zulaessig, wenn sie keine Fachwahrheit enthalten, keine fachliche Primaerverantwortung uebernehmen und wiederverwendbar sind
- technische Querschnittsbausteine duerfen Module entkoppeln, aber keine fachlichen Entscheidungen aus owning Modulen herausziehen
- neue Loesungen sind nur zulaessig, wenn kein passendes bestehendes Muster, keine passende Modul-Schnittstelle und kein geeigneter `shared`-Baustein existiert
- Quickfixes, Workarounds, Copy-Paste-Varianten, seitenlokale UI-Duplikate und isolierte Sonderlogik fuer Einzelstellen sind verboten
- Redundanzvermeidung muss bei Analyse, Umsetzung, Review und Refactoring aktiv nachgewiesen werden koennen
## 7. Verbotene Strukturen
Nicht zulaessig sind:
- Module als reine Ordner ohne Contract
- direkte Cross-Modul-Zugriffe auf DB-Tabellen, interne Implementierungen oder Daten anderer Module
- versteckte oder implizite Modulabhaengigkeiten
- Vermischung mehrerer Primaerverantwortungen innerhalb eines Moduls
- parallele Fachlogik ausserhalb des owning Moduls
- redundante fachliche, technische oder UI-Loesungen, obwohl ein bestehendes Modul, eine Schnittstelle, ein Pattern oder ein `shared`-Baustein erweitert werden koennte
- neue generische Frameworks oder Orchestrator-Umbauten ohne explizite Architekturentscheidung
- implizite Schutz-, Schreib- oder Datenbankschranken ohne Prozess- oder DB-Vertrag
## 8. Erweiterungsregeln
Neue Hauptmodule, Submodule oder zentrale Bausteine duerfen nur eingefuehrt werden, wenn bestehende Strukturen die Verantwortung nicht sauber tragen koennen.
Prueffragen vor jeder Erweiterung:
1. Welches bestehende Hauptmodul ist primaer betroffen?
2. Reicht eine neue Datei, ein neues Submodul oder eine neue Schnittstelle im owning Hauptmodul aus?
3. Handelt es sich um fachlich neutrale Wiederverwendung, die nach `shared` gehoert?
4. Entsteht wirklich eine neue Primaerverantwortung mit eigenen Owned Artefakten und Schreibrechten?
5. Bleibt der Handshake zum Rest des Systems auf minimale Scope-Identifier begrenzt?
6. Reduziert der neue Schnitt Kopplung und Redundanz, statt sie zu erhoehen?
Verbindliche Regel:
- neue Hauptmodule sind nur zulaessig, wenn eine neue fachliche oder technische Primaerverantwortung entsteht, die nicht sauber in `company`, `ratings`, `groups`, `lists`, `market_management`, `system` oder `shared` aufgeht
- neue Submodule sind nur zulaessig, wenn innerhalb eines bestehenden Hauptmoduls eine klar isolierbare Verantwortung mit eigener Schnittstelle entsteht
- neue Prozess- und Sub-Prozess-Dokumente werden im owning Modulpfad angelegt und folgen `component.subcomponent.process_name`
- `admin`, `public/api/`, `scripts/` oder `lib/` begruenden kein neues fachliches Hauptmodul
- Bequemlichkeit, Dateigroesse oder kurzfristiger Umbauaufwand rechtfertigen kein neues Hauptmodul
- vor jeder normativen Architektur- oder Modulerweiterung muss dieses Dokument aktualisiert werden
## 9. Aenderungsregel
Jede strukturelle Aenderung an Hauptmodulen, Modulgrenzen, Prozesszuordnungen, Frontend-Prinzipien, API-Grenzen, DB-Ownership oder primaeren Ownership-Prinzipien muss zuerst in diesem Dokument normativ festgelegt und danach in Detaildokumenten nachgezogen werden.
Verbindlich ist:
- `docs/technical_architecture.md` ist der einzige aktive Architektur-Master des Repositories
- andere Dokumente duerfen diese Architektur erklaeren, anwenden oder historisieren, aber keine konkurrierende Master-Struktur definieren
- konkrete Inventare wie Tabellenlisten, Submodullisten, Prozesslisten oder Runtime-Artefakte werden in Detaildokumenten gepflegt und nicht hier dupliziert