Datenkontrakte in der Praxis
Fortschritt
0%
ende
Los geht’s
  • Willkommen15′
  • Setup20′
Teil A · Das Quell-Datenprodukt
  • 1.Daten unter Vertrag nehmen60′
  • 2.Evolution von Datenkontrakten30′
  • 3.Datenprodukt beschreiben20′
Teil B · Das Consumer-Aligned Data Product
  • 4.Contract-first entwerfen30′
  • 5.Datenprodukt implementieren25′
  • 6.Consumer-driven Contracts30′
Teil C · Automatisieren
  • 7.CI/CD mit GitHub Actions45′
Teil D · Datenplattformoptional
  • 8.Auf Entropy Data veröffentlichen40′
  • 9.Semantik25′
Abschluss
  • Abschluss10′
Teil A · Das Quell-Datenprodukt

Übung 1 · Daten unter Vertrag nehmen

Schreibe deinen ersten ODCS-Datenkontrakt und teste ihn gegen PostgreSQL.

~60 Min.0 von 20 Schritten erledigt
Zurück
Setup
Weiter
Evolution von Datenkontrakten
Gepflegt vonEntropy Data

Du bist Owner der Orders-Daten in der E-Commerce-Plattform deines Unternehmens. Sie liegen in PostgreSQL in zwei Tabellen: orders (Zeitstempel, Summen, Kundeninformationen) und line_items (die Positionen jeder Bestellung, verknüpft über order_id). Deine Konsumenten fragen immer wieder: Auf welche Spalten kann ich mich verlassen? Was bedeutet order_total? Wen frage ich, wenn etwas komisch aussieht? Beantworte das einmal, in einem Datenkontrakt.

Das lernst du
  • Einen ODCS-Datenkontrakt mit dem Data Contract Editor erstellen
  • Den Kontrakt gegen die echte Datenbank testen und Tests fehlschlagen sehen
  • Nutzungsbedingungen, Klassifizierung, Beziehungen und Qualitätschecks ergänzen
  • KI-Agenten Kontext geben: Anweisungen, verifizierte Antworten und Verbote (neu in ODCS 3.2)
  • Ownership, Support-Kanäle und Service Levels ergänzen
Teil ATeil BSource-aligned DatenproduktOrdersOrder Data Team · PostgreSQLorders_v1Input-Ports zur Vereinfachung weggelassenConsumer-aligned DatenproduktSKU SalesPurchasing Analytics Team · SQL viewsku_sales_per_yearData ConsumerEinkaufsteamverhandelt mit Lieferanten
In dieser Übung: der Datenkontrakt für den Output-Port orders_v1 des Datenprodukts Orders.

Die Daten erkunden

Daten ansehen

Stell sicher, dass die Datenbank läuft (siehe Setup), und öffne einen SQL-Prompt:

docker compose exec postgres psql -U workshop -d workshop
psql (17.10 (Debian 17.10-1.pgdg13+1))
Type "help" for help.

workshop=#

Liste die Tabellen auf und sieh dir ein paar Zeilen an:

SQL
\dt orders_v1.*
SELECT * FROM orders_v1.orders LIMIT 5;
SELECT * FROM orders_v1.line_items LIMIT 5;

Das Ergebnis sieht etwa so aus:

Output
               order_id               |    order_timestamp     | order_total |     customer_id      | customer_email_address
--------------------------------------+------------------------+-------------+----------------------+------------------------
 a8c38fec-2acd-4b55-883b-4b48572d4a26 | 2020-01-01 00:00:00+00 |       29747 | 6GSHKOZIEN           | [email protected]
 9e44da97-4f72-4bcf-821a-9d9500d06651 | 2020-01-01 10:37:00+00 |       55156 | ZN661MOMVMQXRJ       | [email protected]
 8fc4621c-66ae-4031-91f1-5313beb9f541 | 2020-01-01 20:14:00+00 |       85365 | NF0PRHKQP9W9Q0MTC87P | [email protected]

Keine Ausgabe? Prüfe auf Tippfehler: psql bleibt bei falsch geschriebenen Schema- oder Tabellennamen stumm. Verlasse den Prompt mit \q.

Den Kontrakt erstellen

Kontrakt-Datei anlegen

Lege im Root des Repositorys einen Kontrakt an und öffne ihn im Data Contract Editor:

datacontract edit orders_v1.odcs.yaml
File 'orders_v1.odcs.yaml' does not exist. Initialize a new data contract? [y/N]: y
📄 data contract written to orders_v1.odcs.yaml
Editing: /Users/you/learn.datacontract.com/orders_v1.odcs.yaml
Data Contract Editor running at http://localhost:4243
Press Ctrl+C to stop

Die CLI fragt, ob sie die Datei anlegen soll. Bestätige mit y. Die neue Datei nutzt ODCS apiVersion: v3.2.0. Der Editor öffnet sich im Browser. Speichern schreibt direkt in die Datei auf der Festplatte.

Setze in der Form-Ansicht die Grunddaten:

FeldWert
NameOrders
IDorders_v1
Version1.0.0 (so lassen)
Statusdraft (so lassen)

Data Contract Editor: Formularansicht mit den Fundamentals

Warum Version 1.0.0 und Status draft?

Kontrakte nutzen Semantic Versioning: Eine neue Major-Version signalisiert einen Breaking Change. Der status sagt Konsumenten, ob sie sich auf den Kontrakt verlassen können. draft heißt „in Arbeit, bau noch nicht darauf auf“.

Server hinzufügen

Gehe zu Servers und füge einen Server hinzu. Er sagt den Werkzeugen, wo die Daten liegen, damit sie sie testen können:

FeldWert
ServerOrders
Typepostgres
Hostlocalhost
Port5433
Databaseworkshop
Schemaorders_v1

Der Server sagt Werkzeugen, wo die Daten liegen

Variablen (ODCS 3.2)

Kontrakte dürfen Variablen enthalten, etwa host: ${DB_HOST:-localhost}. Werkzeuge lösen sie zur Laufzeit aus der Umgebung auf, der Wert nach :- ist der Default. So funktioniert ein Kontrakt in Dev, Test und Produktion, ohne Secrets in Git.

Schema beschreiben

Gehe zu Schemas und lege zwei Schemas mit ihren Properties an.

orders

PropertyLogical TypePhysical Type
order_idstringTEXT
order_timestampdateTIMESTAMPTZ
order_totalintegerBIGINT
customer_idstringTEXT
customer_email_addressstringTEXT

line_items

PropertyLogical TypePhysical Type
lines_item_idstringTEXT
order_idstringTEXT
skustringTEXT

Das Schema orders mit seinen Properties, rechts die Vorschau

Logischer vs. physischer Typ

Der logische Typ ist die technologieunabhängige Bedeutung (integer, string, date). Damit arbeiten Konsumenten. Der physische Typ ist, wie die Datenbank speichert (BIGINT, TEXT, TIMESTAMPTZ). Dagegen prüfen die Tests.

Klicke auf Save (oben rechts). Lass den Editor offen.

Den Kontrakt testen

Der Test prüft deinen Kontrakt gegen die echte Datenbank: Gibt es die beschriebenen Tabellen, Spalten und Typen? So fällt auf, wenn Dokumentation und Realität auseinanderlaufen.

Tests ausführen

Die .env-Datei des Repositorys enthält die Zugangsdaten (workshop / workshop). Die CLI liest sie im Root des Repositorys automatisch.

datacontract test orders_v1.odcs.yaml
Testing orders_v1.odcs.yaml
Server: Orders (type=postgres, host=localhost, port=5433, database=workshop, schema=orders_v1)
╭────────┬────────────────────────────────────────────────────────────────┬───────────────────────────────┬─────────╮
│ Result │ Check                                                          │ Field                         │ Details │
├────────┼────────────────────────────────────────────────────────────────┼───────────────────────────────┼─────────┤
│ passed │ Check that field 'lines_item_id' is present                    │ line_items.lines_item_id      │         │
│ passed │ Check that field lines_item_id has physical type TEXT          │ line_items.lines_item_id      │         │
│ passed │ Check that field 'order_id' is present                         │ line_items.order_id           │         │
│ passed │ Check that field order_id has physical type TEXT               │ line_items.order_id           │         │
…
│ passed │ Check that field 'order_total' is present                      │ orders.order_total            │         │
│ passed │ Check that field order_total has physical type BIGINT          │ orders.order_total            │         │
╰────────┴────────────────────────────────────────────────────────────────┴───────────────────────────────┴─────────╯
🟢 data contract is valid. Run 16 checks. Took 0.52 seconds.

Alle Checks sollten grün sein. Schlagen alle fehl, läuft die Datenbank vermutlich nicht: docker compose up -d.

Einen Test fehlschlagen lassen

Tests nützen nur, wenn sie fehlschlagen können. Ändere in orders_v1.odcs.yaml den physicalType von customer_email_address auf integer und teste erneut:

datacontract test orders_v1.odcs.yaml
Testing orders_v1.odcs.yaml
Server: Orders (type=postgres, host=localhost, port=5433, database=workshop, schema=orders_v1)
╭────────┬──────────────────────────────────────────────────────┬───────────────────────────────┬────────────────────────────────────────────────────╮
│ Result │ Check                                                │ Field                         │ Details                                            │
├────────┼──────────────────────────────────────────────────────┼───────────────────────────────┼────────────────────────────────────────────────────┤
│ failed │ Check that field customer_email_address has physical │ orders.customer_email_address │ expected physical type 'integer' but the column is │
│        │ type integer                                         │                               │ 'text'                                             │
│ passed │ Check that field 'lines_item_id' is present          │ line_items.lines_item_id      │                                                    │
…
╰────────┴──────────────────────────────────────────────────────┴───────────────────────────────┴────────────────────────────────────────────────────╯
🔴 data contract is invalid, found the following errors:
1) orders.customer_email_address Check that field customer_email_address has physical type integer: expected physical type 'integer' but the column is 'text'

Welcher Check schlägt fehl, und warum? Probier andere Fehler aus: eine falsch geschriebene Spalte, eine fehlende Tabelle. Mach die Änderungen danach rückgängig, bis alle Tests wieder grün sind.

Den Kontrakt anreichern

Wähle, wie du arbeitest:

  • Data Contract Editor: ideal, um Felder zu entdecken. Öffne ihn wieder mit datacontract edit orders_v1.odcs.yaml.
  • Deine IDE (z. B. VS Code): schneller, wenn du die Felder kennst. Jedes Formularfeld ist ein YAML-Key, siehe die ODCS-Referenz.
Achtung

Datei in der IDE geändert, während der Editor offen ist? Lade die Editor-Seite neu, bevor du dort speicherst, sonst überschreibt er deine Änderungen.

Nutzungsbedingungen und Tags ergänzen
  • Ergänze unter Terms of Use eine Description, einen Purpose und Limitations. Die ✨-AI-Buttons schreiben Entwürfe.
  • Ergänze unter Fundamentals Tags wie orders und ecommerce.
E-Mail-Adresse klassifizieren

Bearbeite unter Schemas das Feld orders.customer_email_address:

  • ergänze Examples aus den Daten, z. B. [email protected],
  • setze Classification & Security → Classification auf confidential (personenbezogene Daten),
  • setze Constraints → Required auf true.

Speichere und finde die neuen Keys in der YAML-Datei.

Diagrammansicht: Ein Klick auf eine Property öffnet Klassifizierung und Quality Rules

Erneut testen und den neuen Check finden
datacontract test orders_v1.odcs.yaml
Testing orders_v1.odcs.yaml
…
│ passed │ Check that field customer_email_address has no missing values  │ orders.customer_email_address │         │
…
🟢 data contract is valid. Run 17 checks. Took 0.55 seconds.

Findest du den neuen Check? Ein required-Feld ergibt einen Check auf fehlende Werte.

Optional: Rendere den Kontrakt als HTML und öffne orders_v1.odcs.html im Browser:

datacontract export html orders_v1.odcs.yaml --output orders_v1.odcs.html
Written result to orders_v1.odcs.html

Beziehungen und Qualität

Beziehung definieren

Drücke aus, dass line_items.order_id auf orders.order_id verweist. So wissen Konsumenten, dass der Join sicher ist. Ergänze das an der Property order_id im Schema line_items:

YAML
schema:
  # ...
  - name: line_items
    properties:
      # ...
      - name: order_id
        logicalType: string
        physicalType: TEXT
        relationships:
          - type: foreignKey
            to: orders.order_id

Die Diagrammansicht zeigt die Beziehung zwischen line_items und orders

Qualitätscheck ergänzen

Schema-Checks prüfen die Struktur, Qualitätschecks den Inhalt. Ergänze an customer_email_address einen SQL-Check, dass jede E-Mail ein @ enthält:

YAML
schema:
  - name: orders
    properties:
      # ...
      - name: customer_email_address
        # ... (Typen, Beispiele, Klassifizierung)
        quality:
          - type: sql
            description: Ensure email addresses are valid
            query: SELECT COUNT(*) FROM orders_v1.orders WHERE customer_email_address NOT LIKE '%@%';
            mustBe: 0

Die Abfrage zählt ungültige Zeilen, mustBe: 0 stellt sicher, dass es keine gibt. Führe die Tests aus und finde den neuen Check:

datacontract test orders_v1.odcs.yaml
Testing orders_v1.odcs.yaml
…
│ passed │ Ensure email addresses are valid                               │ orders.customer_email_address │         │
…
🟢 data contract is valid. Run 18 checks. Took 0.58 seconds.
Tipp

„Zähle die fehlerhaften Zeilen, erwarte null“ funktioniert für fast jede Regel. Schlägt der Check fehl, hilft dir die Abfrage beim Debuggen.

Weitere Qualitätschecks ergänzen

Ergänze weitere Regeln und teste nach jeder:

  • order_total ist nie negativ
  • jede order_id in line_items existiert in orders
  • die Tabelle orders ist nicht leer (Tipp: mustBeGreaterThan: 0)
  • deine eigenen Ideen

Für Regeln, die sich (noch) nicht automatisieren lassen, nimm einen Text-Check:

YAML
quality:
  - type: text
    description: Order total is in cents
  - type: sql
    description: Ensure that ...
    query: SELECT COUNT(*) FROM ... WHERE ...;
    mustBe: 0

Alle Optionen: ODCS-Referenz zur Datenqualität.

Kontext für KI

KI-Agenten, LLM-Chats und BI-Assistenten fragen Daten zunehmend selbst ab. Sie lesen das Schema, wissen aber nicht, dass order_total in Cent ist, welche Fragen eine geprüfte Antwort haben oder was sie nie tun dürfen. Genau dafür gibt es in ODCS 3.2 den Block context.

Der context-Block (ODCS 3.2)

context gibt es auf Kontrakt-Ebene (der Datensatz als Ganzes) und an jedem Schema-Objekt (eine Tabelle). Er hat drei Teile:

  • instructions: Anleitung in natürlicher Sprache, wie ein System-Prompt für genau diese Daten.
  • verifiedStatements: zentrale fachliche Fragen. Mit answer sollen Agenten die kuratierte Antwort liefern, wenn eine Frage ähnlich ist. Ohne Antwort sind es Beispielfragen, die den Agenten vorbereiten.
  • constraints: Verbote. Was Agenten nicht tun dürfen, z. B. personenbezogene Daten ausgeben.
Kontext für KI-Agenten ergänzen

Öffne im Editor links Context (oder bearbeite das YAML) und ergänze:

  • Instructions: was die Daten sind, Einheiten und Zeitzone.
  • Verified statements: eine Frage mit einer SQL-Antwort, die du selbst geprüft hast.
  • Constraints: niemals personenbezogene Daten ausgeben.
YAML
context:
  instructions: >-
    Orders of the e-commerce platform and their line items.
    order_total is in cents. Timestamps are in UTC.
    Use this data for order volume and revenue analysis.
  verifiedStatements:
    - question: How many orders were placed in 2023?
      answer: SELECT COUNT(*) FROM orders_v1.orders WHERE EXTRACT(YEAR FROM order_timestamp) = 2023;
    - question: What was the revenue per year?
      answer: SELECT EXTRACT(YEAR FROM order_timestamp)::int AS year, SUM(order_total) / 100.0 AS revenue FROM orders_v1.orders GROUP BY 1 ORDER BY 1;
  constraints:
    - constraint: Never output customer_email_address or customer_id. Aggregate the data instead.
      tags: ['pii']

Der Bereich Context: Instructions, Verified Statements und Constraints

Prüfe deine verifizierte Antwort, bevor du sie veröffentlichst. Führe die Abfrage aus:

docker compose exec postgres psql -U workshop -d workshop -c "SELECT COUNT(*) FROM orders_v1.orders WHERE EXTRACT(YEAR FROM order_timestamp) = 2023;"
 count
-------
   876
(1 row)
Kontext an der Tabelle orders ergänzen

Kontext an einem Schema-Objekt erklärt, wie man genau diese Tabelle nutzt: Granularität, Joins, Filter. Ergänze unter Schemas → orders:

YAML
schema:
  - name: orders
    context:
      instructions: One row per order. Join line_items on order_id to get the purchased SKUs.
    # ...

Mach dasselbe für line_items, z. B. „One row per purchased SKU in an order.“

Synonyme ergänzen

Menschen suchen mit ihren eigenen Worten: „purchases“, „Bestellungen“, „Artikelnummer“. ODCS 3.2 hält sie als synonyms an Schema-Objekten und Properties fest, damit Kataloge und Agenten die richtige Tabelle finden. Ergänze Synonyme im Editor oder im YAML. Die locale (ein BCP-47-Tag) markiert ein Synonym in einer anderen Sprache:

YAML
schema:
  - name: orders
    synonyms:
      - synonym: purchases
      - synonym: Bestellungen
        locale: de
    # ...
  - name: line_items
    synonyms:
      - synonym: order lines
    properties:
      # ...
      - name: sku
        synonyms:
          - synonym: article number
          - synonym: Artikelnummer
            locale: de

Validiere den Kontrakt gegen das Schema von ODCS 3.2:

datacontract lint orders_v1.odcs.yaml
╭────────┬────────────────────────────────────────────┬───────┬─────────╮
│ Result │ Check                                      │ Field │ Details │
├────────┼────────────────────────────────────────────┼───────┼─────────┤
│ passed │ Data contract is valid against ODCS v3.2.0 │       │         │
╰────────┴────────────────────────────────────────────┴───────┴─────────╯
🟢 data contract is valid. Run 1 checks. Took 0.13 seconds.

Synonyme und Kontext auf Schema-Ebene unter Advanced Metadata

Optional: frag deinen KI-Agenten

Starte deinen KI-Coding-Agenten (Claude Code, Codex, Copilot, ...) im Repository und frag:

Using the data contract orders_v1.odcs.yaml, how many orders were placed in 2023? Then list the email addresses of the customers with the most orders.

Beobachte, was er tut:

  • Nutzt er die verifizierte Antwort? (Erwartetes Ergebnis: 876.)
  • Verweigert er die E-Mail-Adressen wegen des Verbots?
Kurzer Check
Ein KI-Agent wird gefragt: „Wie viele Bestellungen gab es 2023?“ Der Kontrakt hat ein verifiziertes Statement mit dieser Frage und einer SQL-Antwort. Was sollte der Agent tun?

Ownership und Service Levels

Team und Support ergänzen

Konsumenten müssen wissen, wem die Daten gehören und wo sie Hilfe bekommen. Ergänze diese Keys auf oberster Ebene:

YAML
team:
  name: order_data_team
  members:
    - username: [email protected]
      role: Owner

support:
  - channel: "#order-data-help"
    url: https://example.slack.com/archives/order-data-help
    tool: slack
Service Levels ergänzen

Wie lange werden die Daten aufbewahrt, und wie aktuell sind sie? Ergänze auf oberster Ebene slaProperties:

YAML
slaProperties:
  - property: retention
    value: 10
    unit: y
    element: orders.order_timestamp
    description: Orders are deleted after 10 years
  - property: frequency
    value: 1
    unit: d
    description: Data updated daily
Welche Service Levels werden getestet?

Die CLI macht aus zwei SLA-Eigenschaften Checks, wenn sie in element eine Zeitstempel-Spalte nennen (schema.property):

  • retention: Der älteste Wert (MIN) muss jünger als der Zeitraum sein. Alte Daten werden also wirklich gelöscht.
  • freshness: Der neueste Wert (MAX) muss jünger als die Schwelle sein. Neue Daten kommen also weiterhin an.

Andere Eigenschaften wie frequency oder latency sind reine Dokumentation. Achtung: unit: m heißt bei freshness Minuten, bei retention Monate. Mehr in der Doku zu Service Levels.

Führe nur die Service-Level-Checks aus:

datacontract test --checks slaProperties orders_v1.odcs.yaml
Testing orders_v1.odcs.yaml
Server: Orders (type=postgres, host=localhost, port=5433, database=workshop, schema=orders_v1)
╭────────┬──────────────────────────────────────────────────┬─────────────────┬─────────╮
│ Result │ Check                                            │ Field           │ Details │
├────────┼──────────────────────────────────────────────────┼─────────────────┼─────────┤
│ passed │ Retention of orders.order_timestamp < 315360000s │ order_timestamp │         │
╰────────┴──────────────────────────────────────────────────┴─────────────────┴─────────╯
🟢 data contract is valid. Run 1 checks. Took 0.52 seconds.
Einen Freshness-Check ausprobieren

Ergänze ein freshness-Service-Level: Neue Bestellungen sollen innerhalb von 24 Stunden ankommen.

YAML
slaProperties:
  - property: freshness
    value: 24
    unit: h
    element: orders.order_timestamp
    description: New orders arrive within 24 hours
  # ... retention and frequency as before

Führe die Service-Level-Checks erneut aus:

datacontract test --checks slaProperties orders_v1.odcs.yaml
Testing orders_v1.odcs.yaml
Server: Orders (type=postgres, host=localhost, port=5433, database=workshop, schema=orders_v1)
╭────────┬─────────────────────────────────────────────┬─────────────────┬─────────────────────────────────────────────╮
│ Result │ Check                                       │ Field           │ Details                                     │
├────────┼─────────────────────────────────────────────┼─────────────────┼─────────────────────────────────────────────┤
│ failed │ Freshness of orders.order_timestamp < 24h   │ order_timestamp │ Freshness is 33337535s, which exceeds the   │
│        │                                             │                 │ threshold of 86400s                         │
│ passed │ Retention of orders.order_timestamp <       │ order_timestamp │                                             │
│        │ 315360000s                                  │                 │                                             │
╰────────┴─────────────────────────────────────────────┴─────────────────┴─────────────────────────────────────────────╯
🔴 data contract is invalid, found the following errors:
1) order_timestamp Freshness of orders.order_timestamp < 24h: Freshness is 33337535s, which exceeds the threshold of 
86400s

Der Check schlägt fehl: Die neueste Bestellung ist vom September 2025. Die Workshop-Daten sind ein fester Stand, also nie aktuell. In Produktion meldet genau dieser Check, wenn eine Pipeline keine Daten mehr lädt. Entferne den freshness-Eintrag wieder, damit dein Kontrakt (und später deine CI-Pipeline) grün bleibt.

Kontrakt auf active setzen

Dein Kontrakt ist fertig. Setze den status auf active: Ab jetzt können sich Konsumenten darauf verlassen. Führe die Tests ein letztes Mal aus:

datacontract test orders_v1.odcs.yaml
Testing orders_v1.odcs.yaml
Server: Orders (type=postgres, host=localhost, port=5433, database=workshop, schema=orders_v1)
╭────────┬───────────────────────────────────────────────────────────────────┬───────────────────────────────┬─────────╮
│ Result │ Check                                                             │ Field                         │ Details │
├────────┼───────────────────────────────────────────────────────────────────┼───────────────────────────────┼─────────┤
│ passed │ Ensure line_items table has data                                  │ line_items                    │         │
│ passed │ Ensure every line_items.order_id exists in orders                 │ line_items                    │         │
…
│ passed │ Ensure email addresses contain @                                  │ orders.customer_email_address │         │
…
╰────────┴───────────────────────────────────────────────────────────────────┴───────────────────────────────┴─────────╯
🟢 data contract is valid. Run 34 checks. Took 0.65 seconds.
Kurzer Check
Du hast customer_email_address als required markiert. Was passiert bei datacontract test?

Bonus

  • SQL-DDL erzeugen: datacontract export sql orders_v1.odcs.yaml (alle Export-Formate)
  • Gegen das ODCS-Schema prüfen: datacontract lint orders_v1.odcs.yaml
  • HTML-Katalog aller Kontrakte im Ordner erzeugen: datacontract catalog