From 2607f448adaf2479aedd79b1e901010cbf62b450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Gla=CC=88ser?= Date: Sat, 19 Sep 2026 08:30:01 +0200 Subject: [PATCH] Implementiere Marktmanagement-Datenmodell --- db/migrations/0023_market_management.sql | 215 +++++++++++++ .../market_management_data_architecture.md | 7 +- .../market_management_technisches_konzept.md | 25 ++ docs/technical_architecture.md | 283 ++++++++++++++++++ 4 files changed, 527 insertions(+), 3 deletions(-) create mode 100644 db/migrations/0023_market_management.sql create mode 100644 docs/technical_architecture.md diff --git a/db/migrations/0023_market_management.sql b/db/migrations/0023_market_management.sql new file mode 100644 index 0000000..260ff4d --- /dev/null +++ b/db/migrations/0023_market_management.sql @@ -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; diff --git a/docs/modules/market_management/data/market_management_data_architecture.md b/docs/modules/market_management/data/market_management_data_architecture.md index bf8902f..cac1ee4 100644 --- a/docs/modules/market_management/data/market_management_data_architecture.md +++ b/docs/modules/market_management/data/market_management_data_architecture.md @@ -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. diff --git a/docs/modules/market_management/konzepte/market_management_technisches_konzept.md b/docs/modules/market_management/konzepte/market_management_technisches_konzept.md index 771da29..db08566 100644 --- a/docs/modules/market_management/konzepte/market_management_technisches_konzept.md +++ b/docs/modules/market_management/konzepte/market_management_technisches_konzept.md @@ -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` diff --git a/docs/technical_architecture.md b/docs/technical_architecture.md new file mode 100644 index 0000000..b2776c0 --- /dev/null +++ b/docs/technical_architecture.md @@ -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/` 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///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//...` +- 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//...` +- Arbeit an einem Submodul: `docs/technical_architecture.md` plus `docs/modules///...` +- `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