Implementiere Marktmanagement-Datenmodell
This commit is contained in:
1 parent
8492db2e39
commit
2607f448ad
4 files changed
+527
-3
No files matched your search
@@ -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
|
- Datum des Durchführungstags
|
||||||
- Standzeit und tagesbezogene Durchführungszeiten
|
- Standzeit und tagesbezogene Durchführungszeiten
|
||||||
- Bruttoerlös
|
- Bruttoerlös aus zugeordneten OTC-Verkäufen
|
||||||
- Anzahl verkaufter Artikel
|
- Anzahl verkaufter Artikel aus zugeordneten OTC-Positionen
|
||||||
- Ereignisse und Beobachtungen
|
- Ereignisse und Beobachtungen
|
||||||
|
|
||||||
### Deterministische Referenzwerte
|
### Deterministische Referenzwerte
|
||||||
@@ -87,7 +87,8 @@ Logistik, Kosten, Zahlung, Vertragslink und Notizen.
|
|||||||
Durchführung gespeichert.
|
Durchführung gespeichert.
|
||||||
- Mehrere Durchführungen werden nicht als wiederholte Spalten im Markt
|
- Mehrere Durchführungen werden nicht als wiederholte Spalten im Markt
|
||||||
gespeichert.
|
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
|
- Aggregierte Werte für Durchführung oder Markt werden aus den untergeordneten
|
||||||
Durchführungstagen abgeleitet oder als klar definierte Auswertung bereitgestellt.
|
Durchführungstagen abgeleitet oder als klar definierte Auswertung bereitgestellt.
|
||||||
- MCP-Schreiboperationen müssen eindeutig an die betroffene Ebene gebunden sein.
|
- MCP-Schreiboperationen müssen eindeutig an die betroffene Ebene gebunden sein.
|
||||||
|
|||||||
@@ -102,6 +102,12 @@ Normativ:
|
|||||||
- Zahlungsstatus und Marktstatus sind unabhängig
|
- Zahlungsstatus und Marktstatus sind unabhängig
|
||||||
- der Zahlungsstatus ist manuell `paid` oder `unpaid`
|
- 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`
|
### 3.4 `market_occurrence_organizer`
|
||||||
|
|
||||||
Veranstalterbeziehung einer Durchführung.
|
Veranstalterbeziehung einer Durchführung.
|
||||||
@@ -124,6 +130,25 @@ Normativ:
|
|||||||
- Stand-, Aufbau-, Abbau- und tagesbezogene Anlieferungszeiten gehören hierher
|
- Stand-, Aufbau-, Abbau- und tagesbezogene Anlieferungszeiten gehören hierher
|
||||||
- Ereignisse und Beobachtungen gehören hierher
|
- Ereignisse und Beobachtungen gehören hierher
|
||||||
- OTC-Verkäufe werden eindeutig diesem Datensatz zugeordnet
|
- 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`
|
### 3.6 `market_occurrence_cost_item`
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in new issue
Block a user