Ich entwerfe eine Datenbank mit Postgres. Für meinen Anwendungsfall benötige ich ein modulares Datenmodell. Konkret muss ich Entitäten verschiedener Typen speichern. Alle Entitäten haben eine Reihe von gemeinsamen Feldern und dann haben sie zusätzliche Eigenschaften, abhängig von ihrem Typ. Angenommen, Entitäten repräsentieren Ereignisse. Alle Ereignisse haben einen Titel und eine Beschreibung. Aber wenn ein Ereignis vom Typ Kommentar ist, hat es auch zusätzliche Felder wie Thema und Anzahl der Likes.
Meine Hauptanforderungen sind wie folgt:
- Ich muss NOT NULL-Beschränkungen für die typspezifischen Spalten durchsetzen
- Das Datenmodell sollte leicht erweiterbar sein. Das Hinzufügen eines neuen Entitätstyps sollte einfach sein und keine Ausfallzeit für die Datenbank erfordern
- In meiner Anwendung habe ich eine Ansicht, die alle Entitäten nach Datum sortiert anzeigt. Daher muss ich in der Lage sein, alle Entitäten unabhängig von ihrem Typ in einer schnellen und effizienten Weise abzufragen

Können Sie mir ein Entwurfsmuster erklären, mit dem diese Anforderungen erfüllt werden können? Bitte beschreiben Sie es anhand eines konkreten Beispiels.
Ein mögliches Entwurfsmuster, mit dem diese Anforderungen erfüllt werden können, ist das Entität-Attribut-Wert (EAV)-Muster. Beim EAV-Muster werden die gemeinsamen Felder in einer Tabelle (der "Entität"-Tabelle) und die typspezifischen Felder in einer anderen Tabelle (der "Attribut"-Tabelle) zusammen mit ihren Werten gespeichert. Dadurch wird ein flexibles und erweiterbares Datenmodell ermöglicht.

Hier ist ein Beispiel, wie du das EAV-Muster verwenden könntest, um Ereignisse verschiedener Typen in einer Postgres-Datenbank zu speichern:
1. Erstelle zunächst eine Tabelle, um die gemeinsamen Felder aller Ereignisse zu speichern:
``` 
CREATE TABLE ereignisse (
    id SERIAL PRIMARY KEY,
    titel TEXT NOT NULL,
    beschreibung TEXT NOT NULL,
    datum TIMESTAMP NOT NULL
);
``` 

2. Erstelle dann eine Tabelle, um die typspezifischen Felder der Ereignisse zu speichern:
``` 
CREATE TABLE ereignis_attribute (
    id SERIAL PRIMARY KEY,
    ereignis_id INTEGER NOT NULL,
    attribut_name TEXT NOT NULL,
    attribut_wert TEXT NOT NULL,
    CONSTRAINT ereignis_attribute_ereignis_id_fkey FOREIGN KEY (ereignis_id) REFERENCES ereignisse(id) ON DELETE CASCADE
);
``` 
In dieser Tabelle repräsentiert jede Zeile ein spezifisches Attribut eines spezifischen Ereignisses. Die Spalte "attribut_name" enthält den Namen des Attributes (z. B. "Thema" oder "Anzahl der Likes") und die Spalte "attribut_wert" enthält den Wert des Attributes.

3. Um einen neuen Ereignistyp hinzuzufügen, fügst du einfach neue Zeilen zur Tabelle "ereignis_attribute" mit den entsprechenden "attribut_name"- und "attribut_wert"-Werten hinzu. Um beispielsweise ein Kommentarereignis mit einem "Thema"-Attribut und einem "Anzahl der Likes"-Attribut hinzuzufügen, könntest du Folgendes tun:
``` 
-- Füge ein neues Kommentarereignis hinzu
INSERT INTO ereignisse (titel, beschreibung, datum) VALUES ('Mein Kommentarereignis', 'Dies ist mein Kommentar', NOW());
-- Füge das "Thema"-Attribut hinzu
INSERT INTO ereignis_attribute (ereignis_id, attribut_name, attribut_wert) VALUES (1, 'Thema', 'Mein Thema');
-- Füge das "Anzahl der Likes"-Attribut hinzu
INSERT INTO ereignis_attribute (ereignis_id, attribut_name, attribut_wert) VALUES (1, 'Anzahl der Likes', '10');
``` 

4. Um alle Ereignisse unabhängig von ihrem Typ abzufragen, könntest du eine JOIN-Abfrage verwenden:
``` 
SELECT ereignisse.*, ereignis_attribute.attribut_name, ereignis_attribute.attribut_wert
FROM ereignisse
LEFT JOIN ereignis_attribute ON ereignisse.id = ereignis_attribute.ereignis_id
ORDER BY ereignisse.datum DESC;
``` 

Diese Abfrage liefert alle Ereignisse zusammen mit ihren Attributen, sortiert nach Datum.

Mit diesem EAV-Muster kannst du NOT NULL-Beschränkungen für die typspezifischen Spalten durch einfaches Hinzufügen der Beschränkungen zur Tabelle "ereignis_attribute" durchsetzen. Und da das Datenmodell modular und erweiterbar ist, kannst du problemlos neue Arten von Entitäten hinzufügen, ohne dass es zu einer Ausfallzeit für die Datenbank kommt.