Data Engine

Aus Dokument
Version vom 12. August 2026, 01:14 Uhr von Thorsten (Diskussion | Beiträge) (Die Seite wurde neu angelegt: „ == Übersicht == Dieser Artikel dokumentiert ein Rust-Datenmodell, das die zentralen Entity-Konzepte von Drupal (Node, User, Taxonomy Term, Media, Paragraph) als <code>struct</code>/<code>enum</code>-Typen abbildet. Das Modell enthält bewusst '''keine Logik''' (keine <code>impl</code>-Methoden) – es dient als reine Datenrepräsentation. Eine (De-)Serialisierung (z. B. mit [https://serde.rs/ serde] für Drupal-JSON:API-Antworten) ist bewusst noch nich…“)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Zur Navigation springen Zur Suche springen

Übersicht

Dieser Artikel dokumentiert ein Rust-Datenmodell, das die zentralen Entity-Konzepte von Drupal (Node, User, Taxonomy Term, Media, Paragraph) als struct/enum-Typen abbildet. Das Modell enthält bewusst keine Logik (keine impl-Methoden) – es dient als reine Datenrepräsentation. Eine (De-)Serialisierung (z. B. mit serde für Drupal-JSON:API-Antworten) ist bewusst noch nicht Teil dieses Modells und folgt in einem späteren Schritt.

Motivation

Drupal modelliert Inhalte über ein generisches Entity-System:

  • Entity-Typen (node, user, taxonomy_term, media, …) definieren die grundlegende Art eines Objekts.
  • Bundles (bei Nodes „Content Types" genannt, z. B. article, page) spezialisieren einen Entity-Typ.
  • Felder (field_*) sind pro Bundle konfigurierbar, typisiert und können einfach- oder mehrwertig (Kardinalität) sein.

Um mit einem Drupal-Backend aus Rust heraus zu arbeiten, braucht man typisierte Gegenstücke zu diesen Konzepten. Das hier beschriebene Modell bildet diese Struktur 1:1 in Rust ab.

Grundtypen

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EntityType {
    Node,
    User,
    TaxonomyTerm,
    Media,
    Paragraph,
    File,
    Menu,
    Block,
    Custom(String),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PublishStatus {
    Published,
    Unpublished,
}

pub type LangCode = String;

EntityType bildet die von Drupal vordefinierten Entity-Typen ab; der Custom-Zweig deckt eigene, per Modul definierte Entity-Typen ab, ohne das Enum ändern zu müssen.

Feldwerte: FieldValue

Drupal-Felder sind stark typisiert (Text, Zahl, Datum, Referenz, Bild, …). Dies wird über ein Enum mit Payload pro Variante abgebildet:

#[derive(Debug, Clone)]
pub enum FieldValue {
    Text(String),
    TextLong { value: String, format: Option<String> },
    Integer(i64),
    Decimal(f64),
    Boolean(bool),
    DateTime(DateTime<Utc>),
    Email(String),
    Link { uri: String, title: Option<String> },
    EntityReference { target_id: u64, target_type: EntityType },
    Image {
        target_id: u64,
        alt: Option<String>,
        title: Option<String>,
        width: Option<u32>,
        height: Option<u32>,
    },
    List(Vec<FieldValue>),
}

pub type FieldMap = HashMap<String, FieldValue>;
  • List(Vec<FieldValue>) bildet mehrwertige Felder ab (Kardinalität > 1).
  • FieldMap ist die generische Ablage für alle field_*-Werte eines Bundles, adressiert über den Feldmaschinennamen (z. B. field_tags).

Entity-Typen im Detail

Gemeinsame Basis

#[derive(Debug, Clone)]
pub struct EntityBase {
    pub id: u64,
    pub uuid: Uuid,
    pub entity_type: EntityType,
    pub bundle: String,
    pub langcode: LangCode,
    pub created: DateTime<Utc>,
    pub changed: DateTime<Utc>,
}

EntityBase fasst Eigenschaften zusammen, die praktisch jede Content-Entity in Drupal besitzt, und wird per Komposition in konkrete Entity-Structs eingebettet (siehe Node, Media). Beim späteren Ergänzen von serde bietet sich dafür #[serde(flatten)] an, damit die Felder von EntityBase beim (De-)Serialisieren auf derselben Ebene wie die restlichen Felder erscheinen.

Node

#[derive(Debug, Clone)]
pub struct Node {
    pub base: EntityBase,
    pub title: String,
    pub status: PublishStatus,
    pub author_id: u64,
    pub fields: FieldMap,
}

User

#[derive(Debug, Clone)]
pub struct User {
    pub id: u64,
    pub uuid: Uuid,
    pub name: String,
    pub mail: String,
    pub status: bool,
    pub roles: Vec<String>,
    pub created: DateTime<Utc>,
    pub fields: FieldMap,
}

User nutzt bewusst kein EntityBase, da User-Entities in Drupal kein bundle/langcode im klassischen Sinn besitzen und stattdessen roles und einen einfachen status: bool (aktiv/blockiert) haben.

TaxonomyTerm

#[derive(Debug, Clone)]
pub struct TaxonomyTerm {
    pub id: u64,
    pub uuid: Uuid,
    pub vocabulary: String,
    pub name: String,
    pub description: Option<String>,
    pub parent_id: Option<u64>,
    pub weight: i32,
    pub fields: FieldMap,
}

Bildet Taxonomiebegriffe inkl. hierarchischer Eltern-Referenz (parent_id) und Sortiergewicht (weight) ab.

Media & Paragraph

#[derive(Debug, Clone)]
pub struct Media {
    pub base: EntityBase,
    pub name: String,
    pub fields: FieldMap,
}

#[derive(Debug, Clone)]
pub struct Paragraph {
    pub id: u64,
    pub uuid: Uuid,
    pub bundle: String,
    pub fields: FieldMap,
}

Paragraph ist bewusst schlank gehalten, da Paragraphs in Drupal keine eigenständigen, direkt aufrufbaren Entities mit URL/Alias sind, sondern stets über eine Host-Entity referenziert werden.

Schema-Introspektion (optional)

Für Anwendungsfälle, in denen Feld- und Bundle-Definitionen selbst (nicht nur Werte) abgebildet werden müssen – etwa um ein Drupal-Schema zu spiegeln oder Formulare dynamisch zu generieren:

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Cardinality {
    Single,
    Multiple,
    Unlimited,
}

#[derive(Debug, Clone)]
pub struct FieldDefinition {
    pub field_name: String,
    pub field_type: String,
    pub cardinality: Cardinality,
    pub required: bool,
}

#[derive(Debug, Clone)]
pub struct BundleDefinition {
    pub entity_type: EntityType,
    pub bundle: String,
    pub label: String,
    pub fields: Vec<FieldDefinition>,
}

Design-Entscheidungen

Entscheidung Begründung
FieldValue als Enum mit Payload pro Variante Bildet Drupals starke Feldtypisierung typsicher ab, statt alles als String zu behandeln.
FieldMap = HashMap<String, FieldValue> Bundles sind zur Compile-Zeit nicht bekannt; generische Map bleibt flexibel für beliebige field_*-Namen.
EntityBase als Kompositionsfeld Vermeidet Duplikation gemeinsamer Properties zwischen Node/Media, ohne Vererbung (die es in Rust nicht gibt).
Kein impl-Block Modell bleibt reine Datenschicht; Geschäftslogik (Validierung, API-Calls) gehört in separate Module.
EntityType::Custom(String) Deckt durch Contrib-/Custom-Module definierte Entity-Typen ab, ohne das Enum erweitern zu müssen.

Offene Erweiterungspunkte

  • Weitere FieldValue-Varianten je nach genutzten Feldtypen (z. B. Geolocation, Address, File losgelöst von Image).
  • Übersetzungen (translations: HashMap<LangCode, FieldMap>), falls Drupal-Mehrsprachigkeit (Content Translation) abgebildet werden soll.
  • Konkretes Mapping auf die JSON:API-Hüllstruktur (data, attributes, relationships, included), falls direkt gegen die Drupal-JSON:API deserialisiert werden soll, statt ein eigenes flaches Modell zu pflegen.

Verwendete Crates

  • chrono – Zeitstempel (created, changed)
  • uuid – Entity-UUIDs

Hinweis: serde (für die (De-)Serialisierung) ist bewusst noch nicht Teil dieses Modells und wird in einem späteren Schritt ergänzt.

Siehe auch