Data Contracts Registry

Version, publish, and manage your schemas centrally with the Data Contracts Registry.

Quick Start

# Publish a schema version
fake_data_dsl contracts publish User -v 1.0.0 -s schemas/ -m "Initial release"

# List all contracts
fake_data_dsl contracts list

# Check compatibility
fake_data_dsl contracts diff User 1.0.0 2.0.0

CLI Commands

Publish

fake_data_dsl contracts publish <Schema> -v <version> [options]

Options:
  -s, --schema-dir DIR   Schema directory
  -m, --message MSG      Changelog message
  -d, --dir DIR          Contracts directory (default: contracts)

Example:

fake_data_dsl contracts publish User -v 1.0.0 -s schemas/ -m "Initial User schema"

Deprecate

fake_data_dsl contracts deprecate <Schema> -v <version> [options]

Options:
  --sunset DATE          Sunset date (YYYY-MM-DD)
  -m, --message MSG      Deprecation message

Example:

fake_data_dsl contracts deprecate User -v 1.0.0 --sunset 2026-06-01 -m "Use v2.0.0 instead"

List

fake_data_dsl contracts list

Output:

Data Contracts:

  User:
    ✅ v2.0.0 (5 fields)
    ⚠️ v1.0.0 (3 fields)

  Order:
    ✅ v1.0.0 (8 fields)

History

fake_data_dsl contracts history User

Output:

History for User:

  2026-01-24T10:30:00Z - v2.0.0 published
    Added avatar field, changed email validation
  2026-01-01T09:00:00Z - v1.0.0 deprecated
    Use v2.0.0 instead
  2025-06-15T14:00:00Z - v1.0.0 published
    Initial release

Diff

fake_data_dsl contracts diff User 1.0.0 2.0.0

Output:

Comparing User v1.0.0 → v2.0.0

❌ Breaking changes detected!

Changes:
  Removed: legacy_field
  Added: avatar, updated_at
  Type changed: age (text → number)

Ruby API

Initialize Registry

# Create registry with storage directory
registry = FakeDataDSL::ContractsRegistry.new("contracts/")

Publish

schema = FakeDataDSL.load("schemas/user.dsl")

contract = registry.publish(
  "User",
  version: "1.0.0",
  schema: schema,
  changelog: "Initial release"
)

puts contract[:schema_hash]  # Content hash for integrity
puts contract[:fields]       # Field signatures

Deprecate

registry.deprecate(
  "User",
  version: "1.0.0",
  sunset_date: Date.new(2026, 6, 1),
  message: "Migrate to v2.0.0"
)

Retire

registry.retire("User", version: "1.0.0")

Get Contract

# Get specific version
contract = registry.get("User", version: "1.0.0")

# Get latest published version
latest = registry.get("User")  # Returns newest published

List Versions

versions = registry.versions_for("User")
# => ["1.0.0", "1.1.0", "2.0.0"]

Check Compatibility

result = registry.compatible?("User", "1.0.0", "2.0.0")

if result[:breaking]
  puts "Breaking changes detected!"
  puts "Removed fields: #{result[:changes][:removed_fields]}"
  puts "Type changes: #{result[:changes][:type_changes]}"
else
  puts "Compatible!"
end

Compatibility result:

{
  compatible: false,
  breaking: true,
  changes: {
    removed_fields: ["legacy_field"],
    added_fields: ["avatar", "updated_at"],
    type_changes: [{ field: "age", from: "text", to: "number" }],
    required_changes: [{ field: "email", change: "optional → required" }]
  },
  summary: "Removed fields: legacy_field; Type changed: age (text → number)"
}

Validate Against Contract

current_schema = FakeDataDSL.load("schemas/user.dsl")

result = registry.validate("User", version: "1.0.0", schema: current_schema)

if result[:valid]
  puts "Schema matches contract!"
else
  puts "Schema drift detected!"
  puts "Missing: #{result[:missing_fields]}"
  puts "Extra: #{result[:extra_fields]}"
end

History

history = registry.history("User")

history.each do |entry|
  puts "#{entry[:timestamp]} - v#{entry[:version]} #{entry[:action]}"
  puts "  #{entry[:message]}" if entry[:message]
end

Export/Import

# Export all contracts to directory
registry.export("exported_contracts/")

# Import contracts from directory
registry.import("imported_contracts/")

Contract States

State Description
draft Not yet published
published Active and available
deprecated Scheduled for removal
retired No longer available

Breaking Changes

The following changes are considered breaking:

Change Breaking? Description
Field removed ✅ Yes Consumers depend on this field
Type changed ✅ Yes numbertext breaks parsing
Optional → Required ✅ Yes Consumers may not provide
Field added ❌ No Backwards compatible
Required → Optional ❌ No Backwards compatible

CI Integration

Use contracts in CI pipelines:

# .github/workflows/schema-check.yml
name: Schema Validation

on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2'
          bundler-cache: true

      - name: Check for breaking changes
        run: |
          # Publish current schema
          bundle exec fake_data_dsl contracts publish User \
            -v ${{ github.sha }} \
            -s schemas/

          # Compare with main branch
          bundle exec fake_data_dsl contracts diff User main ${{ github.sha }}

Storage Format

Contracts are stored as JSON files:

contracts/
├── schemas/
│   ├── User_v1.0.0.json
│   ├── User_v2.0.0.json
│   └── Order_v1.0.0.json
└── history/
    ├── User.json
    └── Order.json

Contract file structure:

{
  "name": "User",
  "version": "1.0.0",
  "state": "published",
  "schema_hash": "abc123def456",
  "fields": [
    { "name": "id", "type": "uuid", "optional": false, "nullable": false }
  ],
  "published_at": "2026-01-24T10:30:00Z",
  "changelog": "Initial release"
}