8 Datenmodell: Dokumente, Collections, Datenbanken

Das Datenmodell von MongoDB unterscheidet sich fundamental von relationalen Datenbanken. Statt Tabellen mit festen Spalten und Zeilen arbeitet MongoDB mit flexiblen Dokumenten, die in Collections organisiert sind. Dieses Modell folgt einer klaren Hierarchie: Datenbanken enthalten Collections, Collections enthalten Dokumente. Diese drei Ebenen bilden das Fundament, auf dem alle weiteren Konzepte aufbauen.

8.1 Dokumente: Die atomare Einheit

Ein Dokument ist die kleinste Einheit in MongoDB – vergleichbar mit einer Zeile in SQL, aber weitaus mächtiger. Technisch ist ein Dokument ein BSON-Objekt (Binary JSON), das aus Schlüssel-Wert-Paaren besteht. Die Syntax ähnelt JSON und ist für Entwickler sofort vertraut. Ein einfaches Benutzerdokument könnte etwa Name, Alter und E-Mail-Adresse enthalten.

Die eigentliche Stärke von Dokumenten liegt in ihrer Fähigkeit, komplexe Strukturen abzubilden. Anders als eine SQL-Zeile, die nur atomare Werte enthalten kann, unterstützt ein MongoDB-Dokument verschachtelte Objekte und Arrays. Ein Dokument kann andere Dokumente enthalten – sogenannte eingebettete Dokumente oder Embedded Documents. Diese Verschachtelung ermöglicht es, zusammengehörige Daten als kohärente Einheit zu speichern.

Betrachten wir ein realistisches Beispiel – ein Benutzerprofil für eine Social-Media-Plattform:

{
  "_id": ObjectId("507f1f77bcf86cd799439011"),
  "username": "anna.mueller",
  "email": "anna@example.com",
  "profile": {
    "firstName": "Anna",
    "lastName": "Müller",
    "birthDate": ISODate("1994-03-15T00:00:00Z"),
    "bio": "Fotografin aus Berlin",
    "avatar": "https://cdn.example.com/avatars/anna.jpg"
  },
  "address": {
    "street": "Hauptstraße 45",
    "city": "Berlin",
    "zip": "10115",
    "country": "DE"
  },
  "interests": ["Fotografie", "Reisen", "Kochen"],
  "settings": {
    "newsletter": true,
    "privacy": "friends",
    "theme": "dark"
  },
  "createdAt": ISODate("2023-01-10T14:23:00Z"),
  "lastLogin": ISODate("2024-01-05T09:15:00Z")
}

Dieses Dokument zeigt mehrere wichtige Charakteristiken. Das _id-Feld ist der Primärschlüssel – MongoDB generiert automatisch eine ObjectId, wenn keine explizit angegeben wird. Diese 12-Byte-Sequenz ist global eindeutig und enthält einen Timestamp, was praktisch für Sortierung und Debugging ist.

Die Felder profile und address sind eingebettete Dokumente – vollständige Objekte innerhalb des Hauptdokuments. Statt wie in SQL separate Tabellen für Profile und Adressen zu haben, die über Fremdschlüssel verknüpft werden, speichert MongoDB alles zusammen. Dies hat einen entscheidenden Vorteil: Eine einzige Read-Operation liefert alle Daten. Keine Joins, keine mehrfachen Abfragen – ein Dokument enthält alles.

Das Array interests demonstriert, wie MongoDB Listen von Werten handhabt. In SQL würde dies typischerweise eine separate Tabelle user_interests mit Fremdschlüsseln erfordern. In MongoDB ist es einfach ein Array im Dokument. Dies vereinfacht nicht nur die Datenmodellierung, sondern auch Queries: “Finde alle Benutzer, die sich für Fotografie interessieren” ist eine simple Array-Abfrage.

Die Datentypen in diesem Dokument zeigen die Vielfalt von BSON. Strings, Numbers und Booleans stammen aus JSON. ISODate ist ein BSON-spezifischer Typ für präzise Zeitstempel. ObjectId ist ebenfalls BSON-spezifisch. Diese zusätzlichen Typen machen BSON mächtiger als reines JSON und ermöglichen effiziente Operationen wie Datumsvergleiche oder numerische Berechnungen.

Ein fundamentales Prinzip von MongoDB ist die Schemafreiheit auf Dokumentebene. Zwei Dokumente in derselben Collection können völlig unterschiedliche Felder haben. Ein Benutzer könnte ein address-Feld haben, ein anderer nicht. Dies ist in SQL unmöglich – jede Zeile muss die gleichen Spalten haben, auch wenn viele NULL sind. In MongoDB ist ein fehlendes Feld einfach nicht vorhanden, was Speicher spart und die Modellierung vereinfacht.

Diese Flexibilität ist mächtig, aber nicht ohne Risiken. Ohne Disziplin können inkonsistente Datenstrukturen entstehen. Ein Teil der Anwendung schreibt firstName, ein anderer first_name. Solche Inkonsistenzen führen zu Bugs. MongoDB bietet deshalb Schema-Validierung an – optionale Regeln, die Dokumentstrukturen erzwingen können. Dies ist ein bewusster Trade-off: Flexibilität wo sinnvoll, Struktur wo nötig.

8.2 Collections: Logische Gruppierung

Eine Collection ist eine Gruppe von Dokumenten, konzeptionell vergleichbar mit einer Tabelle in SQL. Der entscheidende Unterschied: Collections erzwingen kein Schema. Dokumente mit völlig unterschiedlichen Strukturen können friedlich in derselben Collection koexistieren. Dies ist bewusst so designed – nicht als Versehen, sondern als Feature.

Der Name einer Collection sollte ihren Zweck widerspiegeln. Konventionen wie Plural (users, orders, products) und Kleinschreibung sind weit verbreitet, aber nicht erzwungen. MongoDB erlaubt Unicode in Collection-Namen, was auch deutsche Umlaute ermöglicht – wovon allerdings abzuraten ist, da es Tool-Kompatibilität beeinträchtigen kann.

Betrachten wir eine realistische Collection products für einen E-Commerce-Shop:

// Elektronik-Produkt
{
  "_id": ObjectId("..."),
  "sku": "LAPTOP-001",
  "name": "ThinkPad X1 Carbon",
  "category": "electronics",
  "price": 1499.99,
  "specs": {
    "cpu": "Intel i7-1165G7",
    "ram": "16GB",
    "storage": "512GB SSD",
    "display": "14\" FHD"
  },
  "warranty": 36,
  "inStock": true,
  "quantity": 15
}

// Kleidungsstück
{
  "_id": ObjectId("..."),
  "sku": "SHIRT-042",
  "name": "Bio-Baumwolle T-Shirt",
  "category": "clothing",
  "price": 29.99,
  "sizes": ["S", "M", "L", "XL"],
  "colors": ["weiß", "schwarz", "navy"],
  "material": "100% Bio-Baumwolle",
  "care": "30°C Maschinenwäsche",
  "inStock": true,
  "inventory": {
    "S": 20,
    "M": 35,
    "L": 28,
    "XL": 12
  }
}

// Lebensmittel
{
  "_id": ObjectId("..."),
  "sku": "FOOD-089",
  "name": "Bio Haferflocken",
  "category": "food",
  "price": 3.99,
  "weight": "500g",
  "nutrition": {
    "calories": 379,
    "protein": 13.5,
    "carbs": 67.7,
    "fat": 7.0
  },
  "allergens": ["Gluten"],
  "organic": true,
  "expiryDate": ISODate("2024-12-31T00:00:00Z"),
  "inStock": true,
  "quantity": 150
}

Diese drei Dokumente leben in derselben Collection, haben aber völlig unterschiedliche Felder. Das Laptop hat specs und warranty, das T-Shirt hat sizes und material, die Haferflocken haben nutrition und allergens. Gemeinsam ist ihnen nur das Notwendigste: sku, name, category, price, inStock.

In SQL würde dies typischerweise eine von zwei Lösungen erfordern. Entweder separate Tabellen pro Produkttyp (mit der Herausforderung, über alle Typen hinweg zu suchen), oder eine Tabelle mit Dutzenden von Spalten, wo jedes Produkt nur einen Bruchteil nutzt. MongoDB macht beides unnötig – heterogene Produkte koexistieren natürlich.

Die Schemafreiheit von Collections bedeutet nicht Beliebigkeit. In der Praxis haben Dokumente einer Collection meist ähnliche Strukturen, weil sie vom gleichen Code geschrieben und gelesen werden. Ein gewisser gemeinsamer Nenner – wie die Felder name, price, inStock oben – entsteht organisch aus den Anforderungen der Anwendung.

Collections können explizit erstellt werden, aber MongoDB erzeugt sie auch automatisch beim ersten Insert. Ein db.newcollection.insertOne({...}) auf einer nicht existierenden Collection erzeugt diese implizit. Für spezielle Anforderungen – wie Schema-Validierung oder Capped Collections mit fester Größe – ist explizite Erstellung sinnvoll.

8.3 Datenbanken: Organisatorische Container

Eine Datenbank in MongoDB ist ein Container für Collections. Eine MongoDB-Instanz kann mehrere Datenbanken hosten, typischerweise eine pro Anwendung oder Microservice. Diese Trennung ist nicht nur organisatorisch, sondern auch operativ: Jede Datenbank hat eigene Dateien auf dem Dateisystem, eigene Locks und kann unabhängig gesichert werden.

Die Namensgebung folgt ähnlichen Konventionen wie Collections: aussagekräftig, Kleinschreibung, keine Sonderzeichen. Namen wie ecommerce, analytics oder user_profiles sind typisch. Systemdatenbanken wie admin, config und local sind reserviert und haben spezielle Bedeutungen in MongoDBs interner Verwaltung.

Eine typische MongoDB-Instanz für ein Unternehmen könnte mehrere Datenbanken hosten:

Die physische Trennung auf Datenbankebene hat praktische Konsequenzen. Backups können pro Datenbank erfolgen, was bei großen Systemen Zeit spart. Zugriffsrechte werden typischerweise auf Datenbankebene vergeben – ein Benutzer hat Leserechte auf ecommerce, aber keine auf analytics. Diese Granularität unterstützt das Prinzip der minimalen Rechte.

Queries sind datenbankspezifisch. Eine Verbindung zur MongoDB-Shell oder ein Application-Connection geschieht immer im Kontext einer Datenbank. use ecommerce wechselt in der Shell zur Datenbank ecommerce, danach beziehen sich alle Befehle auf deren Collections. Ein db.products.find() sucht in ecommerce.products, nicht in analytics.products, selbst wenn beide existieren.

Cross-Database-Operationen sind limitiert. Ein $lookup (MongoDBs Äquivalent zu SQL Joins) kann Collections aus derselben Datenbank verknüpfen. Über Datenbanken hinweg ist dies nicht möglich. Diese Limitation ist bewusst gewählt – sie fördert klare Architekturen, wo zusammengehörige Daten auch zusammen liegen.

8.4 Datenmodellierung in der Praxis

Die Freiheiten des dokumentenorientierten Modells erfordern bewusste Entscheidungen bei der Datenmodellierung. Die zentrale Frage ist oft: Daten einbetten oder referenzieren? Diese Entscheidung prägt Performance, Konsistenz und Wartbarkeit.

Einbettung (Embedding) bedeutet, zusammengehörige Daten als verschachtelte Dokumente zu speichern. Das Benutzer-Beispiel oben bettet Adresse und Profil ein. Vorteil: Eine Query liefert alles, keine Joins nötig. Nachteil: Redundanz, wenn dieselben Daten an mehreren Stellen benötigt werden, und Größenlimitierung – ein Dokument darf maximal 16 MB groß sein.

Referenzierung bedeutet, Dokumente über IDs zu verknüpfen, ähnlich Fremdschlüsseln in SQL. Ein Order-Dokument könnte etwa nur die customer_id speichern, nicht das gesamte Customer-Dokument. Vorteil: Keine Redundanz, flexiblere Updates. Nachteil: Mehrere Queries oder $lookup-Operationen nötig, was Performance kostet.

Die Entscheidung hängt von Zugriffsmustern ab. Werden Daten fast immer gemeinsam gelesen? Einbetten. Werden sie unabhängig voneinander aktualisiert oder haben ein stark differierendes Wachstum? Referenzieren. Ein klassisches Beispiel: Blog-Posts und Kommentare. Bei wenigen Kommentaren pro Post kann Einbettung sinnvoll sein. Bei Tausenden wird Referenzierung notwendig, um das 16-MB-Limit nicht zu überschreiten.

Die folgende Tabelle fasst die Entscheidungskriterien zusammen:

Kriterium Einbettung bevorzugen Referenzierung bevorzugen
Lesemuster Daten werden fast immer zusammen gelesen Daten werden oft unabhängig gelesen
Schreibmuster Kind-Daten ändern sich selten Kind-Daten ändern sich häufig
Datenmenge Vorhersehbar klein (< 1 MB pro Dokument) Potenziell groß oder unbegrenzt wachsend
Redundanz Akzeptabel oder nicht vorhanden Problematisch
Konsistenz Atomare Updates nötig Separate Updates sind OK

Das dokumentenorientierte Modell von MongoDB ist nicht komplexer als SQL – es ist anders. Die Denkweise verschiebt sich von normalisierten Tabellen hin zu natürlichen Objektstrukturen. Entwickler, die von SQL kommen, neigen anfangs dazu, zu stark zu normalisieren. Mit Erfahrung kommt die Erkenntnis, dass MongoDB Denormalisierung nicht nur toleriert, sondern oft bevorzugt. Ein gut modelliertes MongoDB-Schema folgt den Anforderungen der Anwendung, nicht abstrakten Normalisierungsregeln.