Back to Blog

ObjectType Lifecycle: The State Machine from DRAFT to ARCHIVED

In traditional development, database Schema changes are dangerous operations:

CoomiaPublished on August 6, 202515 min read
Share this articleTwitter / X

ObjectType Lifecycle: The State Machine from DRAFT to ARCHIVED

Series: S4 Ontology Modeling · Article 3 | Level: Intermediate | Reading Time: 18 min

#TL;DR

  • The four-stage Schema lifecycle (DRAFT → ACTIVE → DEPRECATED → ARCHIVED) ensures the safety and traceability of Ontology changes, with strict precondition checks at every state transition.
  • The compatibility validation engine automatically detects breaking changes (removing required properties, modifying primary key types, etc.) at every change, preventing the catastrophic scenario of "changed the Schema but forgot the consumers."
  • The safe deletion protocol uses dependency graph analysis to ensure deleted types leave no dangling references, supporting both dry-run preview and force-delete modes.

#1. Why Schema Needs Lifecycle Management

In traditional development, database Schema changes are dangerous operations:

Code
Traditional Approach (Dangerous):
┌─────────────┐
│  ALTER TABLE │ → Takes effect immediately
│  DROP COLUMN │ → Cannot roll back
│  RENAME      │ → All consumers break
└─────────────┘

Developer: Write a Flyway script → DBA reviews (maybe) → Execute → Pray

coomia-dip Approach (Safe):
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│    DRAFT     │───►│   ACTIVE    │───►│ DEPRECATED  │───►│  ARCHIVED   │
│  (draft)     │    │ (production)│    │(winding down)│   │ (archived)  │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘
  Free to modify     Compatible only   Notify consumers    Read-only

#2. Four Lifecycle States in Detail

#2.1 DRAFT — Draft State

Code
┌──────────────────────────────────────┐
│              DRAFT                    │
│                                      │
│  Characteristics:                    │
│  ├── Free to add/remove/modify props │
│  ├── Can modify primary key          │
│  ├── Can modify relation definitions │
│  ├── No API generated               │
│  ├── No physical storage created     │
│  └── Can delete directly (no checks) │
│                                      │
│  Available Operations:               │
│  ├── addProperty                     │
│  ├── removeProperty                  │
│  ├── modifyProperty                  │
│  ├── setPrimaryKey                   │
│  ├── addRelation                     │
│  ├── publish  → transition to ACTIVE │
│  └── delete   → permanent deletion   │
└──────────────────────────────────────┘
Python
from ontology_sdk import OntologyClient

client = OntologyClient(base_url="http://localhost:8080")

# Create ObjectType in DRAFT state
result = client.schema.create_object_type({
    "name": "Equipment",
    "primaryKey": "equipmentId",
    "properties": {
        "equipmentId": {"type": "STRING", "required": True},
        "name": {"type": "STRING", "required": True},
    }
})
assert result.lifecycle == "DRAFT"

# Free to modify in DRAFT state
client.schema.add_property("Equipment", {
    "status": {"type": "ENUM", "enumValues": ["RUNNING", "STOPPED"]}
})

client.schema.remove_property("Equipment", "name")  # Can delete
client.schema.add_property("Equipment", {
    "displayName": {"type": "STRING", "required": True}  # Re-add
})

#2.2 ACTIVE — Active State

Code
┌──────────────────────────────────────┐
│              ACTIVE                   │
│                                      │
│  Characteristics:                    │
│  ├── Platform auto-generates CRUD API│
│  ├── Physical storage auto-created   │
│  ├── Can be referenced by other types│
│  ├── Only compatible changes allowed │
│  ├── Can create/query instance data  │
│  └── Version number auto-increments  │
│                                      │
│  Allowed Changes (Compatible):       │
│  ├── Add optional properties         │
│  ├── Add ENUM values                 │
│  ├── Relax constraints (widen max)   │
│  ├── Add indexes                     │
│  ├── Modify displayName/description  │
│  └── Add InterfaceType implementations│
│                                      │
│  Forbidden Changes (Breaking):       │
│  ├── Remove properties               │
│  ├── Change property types            │
│  ├── Modify primary key              │
│  ├── Add required prop (no default)  │
│  ├── Remove ENUM values              │
│  ├── Tighten constraints             │
│  └── Change relation cardinality     │
│                                      │
│  Available Operations:               │
│  ├── compatibleChange                │
│  ├── deprecate → DEPRECATED          │
│  └── Breaking changes require        │
│      Proposal workflow               │
└──────────────────────────────────────┘
Python
# Publish to ACTIVE
client.schema.publish_object_type("Equipment")

# Compatible change — succeeds
client.schema.add_property("Equipment", {
    "description": {"type": "STRING"}  # Optional property, OK
})

# Compatible change — add enum value, OK
client.schema.add_enum_value("Equipment", "status", "MAINTENANCE")

# Breaking change — will be rejected!
try:
    client.schema.remove_property("Equipment", "displayName")
except IncompatibleChangeError as e:
    print(f"Rejected: {e}")
    # Rejected: Cannot remove required property 'displayName' from ACTIVE ObjectType.
    # Use a Proposal to schedule this breaking change.

#2.3 DEPRECATED — Deprecated State

Code
┌──────────────────────────────────────┐
│            DEPRECATED                 │
│                                      │
│  Characteristics:                    │
│  ├── API still works (with warnings) │
│  ├── Instance data still read/write  │
│  ├── New code should not reference   │
│  ├── Platform generates deprecation  │
│  │   notifications                   │
│  ├── Can set sunsetDate              │
│  └── Auto-transitions to ARCHIVED   │
│      after sunset                    │
│                                      │
│  Entry Conditions:                   │
│  ├── Must transition from ACTIVE     │
│  ├── Must specify alternative or     │
│  │   migration guide                 │
│  └── Must notify all known consumers │
│                                      │
│  Available Operations:               │
│  ├── reactivate → back to ACTIVE     │
│  ├── archive    → to ARCHIVED        │
│  └── Limited compatible changes only │
└──────────────────────────────────────┘
Python
# Deprecate
client.schema.deprecate_object_type(
    name="Equipment",
    reason="Replaced by EquipmentV2 with better property design",
    replacement="EquipmentV2",
    sunset_date="2026-06-01",
    migration_guide="See docs/migration/equipment-v2.md"
)

# API responses include deprecation warnings
# HTTP/1.1 200 OK
# Deprecation: true
# Sunset: Sat, 01 Jun 2026 00:00:00 GMT
# Link: <docs/migration/equipment-v2.md>; rel="successor-version"

# Can revert — back to ACTIVE
client.schema.reactivate_object_type("Equipment")

#2.4 ARCHIVED — Archived State

Code
┌──────────────────────────────────────┐
│             ARCHIVED                  │
│                                      │
│  Characteristics:                    │
│  ├── API no longer available (410)   │
│  ├── Instance data migrated/deleted  │
│  ├── Schema definition retained      │
│  │   (for audit purposes)           │
│  ├── Cannot modify or revert         │
│  └── Preserved in SchemaRegistry     │
│      history                         │
│                                      │
│  Entry Conditions:                   │
│  ├── Must transition from DEPRECATED │
│  ├── All instance data must be zero  │
│  │   or migrated                     │
│  ├── All RelationTypes referencing   │
│  │   this type must be deleted       │
│  └── All ActionTypes targeting       │
│      this type must be deleted       │
│                                      │
│  No modification operations allowed  │
└──────────────────────────────────────┘
Python
# Archive (must satisfy all preconditions)
try:
    client.schema.archive_object_type("Equipment")
except ArchivePreConditionError as e:
    print(f"Preconditions not met: {e}")
    # Preconditions not met:
    #   - 3 active relations reference 'Equipment'
    #   - 127 instances still exist
    #   - 2 ActionTypes target 'Equipment'

# Clean up dependencies first
client.schema.delete_relation_type("EquipmentBelongsToLine")
client.data.migrate_instances("Equipment", "EquipmentV2", mapping={...})
client.schema.delete_action_type("ScheduleMaintenance")

# Archive again
client.schema.archive_object_type("Equipment")  # Success

#3. Complete State Transition Diagram

Code
                    ┌──────────┐
                    │  CREATE   │
                    └────┬─────┘
                         │
                         ▼
              ┌──────────────────┐
              │      DRAFT       │◄──────────────────────┐
              │                  │                       │
              │  Free to modify  │          clone()      │
              │  No API          │    (clone from any     │
              │  No storage      │     state)             │
              └────┬────────┬───┘                       │
                   │        │                           │
              publish    delete                         │
                   │        │                           │
                   ▼        ▼                           │
         ┌─────────────┐  (permanent)                   │
         │   ACTIVE     │                               │
         │              │                               │
         │  API enabled  │ ◄─── reactivate ────┐        │
         │  Storage on   │                      │        │
         │  Compat. only │                      │        │
         └────┬─────────┘                      │        │
              │                                │        │
          deprecate                            │        │
              │                                │        │
              ▼                                │        │
    ┌──────────────────┐                       │        │
    │   DEPRECATED      │──────────────────────┘        │
    │                   │                               │
    │  API with warnings│                               │
    │  Notify consumers │  ──── clone("EquipmentV3") ───┘
    │  Sunset date set  │
    └────┬──────────────┘
         │
      archive
         │
         ▼
    ┌──────────────────┐
    │    ARCHIVED       │
    │                   │
    │  API returns 410  │
    │  Read-only history│
    │  Cannot revert    │
    └──────────────────┘

#4. Compatibility Validation Engine

#4.1 Change Type Classification

Code
┌─────────────────────────────────────────────────┐
│          Change Type Classification Matrix        │
├─────────────────────┬───────────┬───────────────┤
│ Change Operation     │ Compat.   │ Allowed ACTIVE│
├─────────────────────┼───────────┼───────────────┤
│ Add optional property│ Compat ✅ │ Yes           │
│ Add req'd w/ default │ Compat ✅ │ Yes           │
│ Add ENUM value       │ Compat ✅ │ Yes           │
│ Relax maxLength      │ Compat ✅ │ Yes           │
│ Add index            │ Compat ✅ │ Yes           │
│ Modify displayName   │ Compat ✅ │ Yes           │
│ Add interface impl   │ Compat ✅ │ Yes           │
├─────────────────────┼───────────┼───────────────┤
│ Remove optional prop │ Breaking ❌│ Need Proposal │
│ Remove required prop │ Breaking ❌│ Need Proposal │
│ Change property type │ Breaking ❌│ Need Proposal │
│ Modify primary key   │ Breaking ❌│ Need Proposal │
│ Add req'd no default │ Breaking ❌│ Need Proposal │
│ Remove ENUM value    │ Breaking ❌│ Need Proposal │
│ Tighten constraints  │ Breaking ❌│ Need Proposal │
│ Change cardinality   │ Breaking ❌│ Need Proposal │
│ Remove interface impl│ Breaking ❌│ Need Proposal │
└─────────────────────┴───────────┴───────────────┘

#4.2 Compatibility Check Flow

Code
                    ┌───────────────┐
                    │ Change Request │
                    └───────┬───────┘
                            │
                    ┌───────▼───────┐
                    │  State Check   │
                    └───────┬───────┘
                            │
              ┌─────────────┼─────────────┐
              │             │             │
         DRAFT          ACTIVE       DEPRECATED
              │             │             │
         Apply         ┌────▼────┐    Bug fix only
         directly      │Compat.  │
                       │Analysis │
                       └────┬────┘
                            │
                  ┌─────────┼─────────┐
                  │                   │
             Compatible          Breaking
                  │                   │
             Apply               ┌────▼─────────┐
             directly            │  Proposal     │
                                 │  Workflow     │
                                 │ 1. Create     │
                                 │ 2. Impact     │
                                 │ 3. Review     │
                                 │ 4. Execute    │
                                 └───────────────┘

#4.3 Impact Analysis Report

Python
# Analyze the impact of a breaking change
impact = client.schema.analyze_impact(
    object_type="Equipment",
    change={
        "type": "REMOVE_PROPERTY",
        "property": "status"
    }
)

print(impact.report())
# Impact Analysis Report
# ═══════════════════════════════════════════
# Change: Remove property 'status' from Equipment
# Severity: BREAKING
#
# Affected Components:
# ┌──────────────────────────────────────────┐
# │ Component          │ Count │ Severity    │
# ├────────────────────┼───────┼─────────────┤
# │ RelationTypes      │   0   │ -           │
# │ ActionTypes        │   2   │ HIGH        │
# │   - ScheduleMaint  │       │ Uses status │
# │   - TransferEquip  │       │ Uses status │
# │ Derived Props      │   1   │ HIGH        │
# │   - isOperational  │       │ Depends on  │
# │ Metrics            │   1   │ MEDIUM      │
# │   - StatusDistrib  │       │ Groups by   │
# │ API Consumers      │   5   │ HIGH        │
# │ Dashboard Widgets  │   3   │ MEDIUM      │
# └──────────────────────────────────────────┘
#
# Recommendation: Create a Proposal with migration plan

#5. Version Management

#5.1 Semantic Versioning

Code
Version format: MAJOR.MINOR.PATCH

MAJOR: Breaking changes (require Proposal)
MINOR: Compatible additions (new properties, new enum values)
PATCH: Metadata modifications (displayName, description)

Equipment v1.0.0 → v1.1.0 (add optional property description)
Equipment v1.1.0 → v1.2.0 (add ENUM value MAINTENANCE)
Equipment v1.2.0 → v2.0.0 (remove property oldField via Proposal)

#5.2 Version History Query

Python
# View version history
history = client.schema.get_version_history("Equipment")

for version in history:
    print(f"v{version.number} | {version.timestamp} | "
          f"{version.change_type} | {version.author}")

# v1.0.0 | 2026-01-15 | CREATED     | admin
# v1.1.0 | 2026-01-20 | ADD_PROP    | dev-team
# v1.2.0 | 2026-02-01 | ADD_ENUM    | dev-team
# v2.0.0 | 2026-03-01 | BREAKING    | architect (Proposal #42)

# View Schema at a specific version
schema_v1 = client.schema.get_object_type("Equipment", version="1.0.0")

#5.3 Version Comparison

Python
diff = client.schema.diff_versions("Equipment", "1.0.0", "2.0.0")

print(diff.summary())
# Schema Diff: Equipment v1.0.0 → v2.0.0
# ════════════════════════════════════════
# Added Properties:
#   + description: STRING (optional)
#   + maintenanceDate: TIMESTAMP (optional)
#
# Modified Properties:
#   ~ status: ENUM added value 'MAINTENANCE'
#
# Removed Properties:
#   - oldField: STRING (was optional)
#
# Compatibility: BREAKING (removed property)

#6. Safe Deletion Protocol

#6.1 Dependency Graph Analysis

Code
ObjectType to delete: Equipment

Dependency Graph:
                    ┌──────────────┐
                    │  Equipment   │ ← Want to delete this
                    └──────┬───────┘
                           │
           ┌───────────────┼───────────────┐
           │               │               │
     ┌─────▼─────┐  ┌─────▼─────┐  ┌─────▼─────┐
     │ Relations  │  │ Actions   │  │ Derived   │
     │            │  │           │  │ Props     │
     │ BelongsTo  │  │ Schedule  │  │ OEE       │
     │ Line (3)   │  │ Maint.(2) │  │ Metrics(1)│
     └────────────┘  └───────────┘  └───────────┘
           │
     ┌─────▼──────┐
     │ ProductLine │
     │ (back-ref)  │
     └────────────┘

Safe deletion requires cleaning all dependency nodes first

#6.2 Dry-Run Preview

Python
# Dry-run mode: check only, don't execute
result = client.schema.delete_object_type(
    "Equipment",
    mode="DRY_RUN"
)

if result.can_delete:
    print("Safe to delete")
else:
    print(f"Cannot delete. Reasons:")
    for blocker in result.blockers:
        print(f"  - {blocker.type}: {blocker.name} ({blocker.reason})")

# Cannot delete. Reasons:
#   - RELATION: EquipmentBelongsToLine (references Equipment as source)
#   - RELATION: LineContainsEquipment (references Equipment as target)
#   - ACTION: ScheduleMaintenance (targets Equipment)
#   - ACTION: TransferEquipment (targets Equipment)
#   - DERIVED_PROP: Equipment.oee (depends on Equipment properties)
#   - METRIC: EquipmentOEE (aggregates Equipment.oee)
#   - INSTANCES: 1,247 instances exist

#6.3 Cascade Delete

Python
# Force delete (cascade-clean all dependencies)
result = client.schema.delete_object_type(
    "Equipment",
    mode="CASCADE",
    confirm=True,          # Explicit confirmation required
    backup=True            # Backup Schema definition before deletion
)

# Execution order:
# 1. Backup Schema definition → schema-backups/Equipment-v2.0.0.json
# 2. Delete dependent Metrics
# 3. Delete dependent ActionTypes
# 4. Delete dependent Derived Properties
# 5. Delete dependent RelationTypes
# 6. Migrate/delete instance data
# 7. Delete physical storage (Doris table)
# 8. Remove from SchemaRegistry

#7. Multi-Environment Lifecycle Management

Code
┌─────────────────────────────────────────────────────────┐
│                    Environment Promotion                 │
│                                                         │
│  DEV          STAGING        PRODUCTION                 │
│  ┌────────┐  ┌────────┐    ┌────────┐                  │
│  │ DRAFT  │  │        │    │        │                  │
│  │   ↓    │  │        │    │        │                  │
│  │ ACTIVE │──promote──│ ACTIVE │──promote──│ ACTIVE │  │
│  │        │  │   ↓    │    │        │                  │
│  │        │  │ test   │    │        │                  │
│  └────────┘  └────────┘    └────────┘                  │
│                                                         │
│  Each environment has independent lifecycle states      │
│  Promote operation copies Schema + runs compat checks   │
└─────────────────────────────────────────────────────────┘
Python
# Promote from DEV to STAGING
client.schema.promote(
    object_type="Equipment",
    from_env="dev",
    to_env="staging",
    version="2.0.0"
)

# Promote from STAGING to PRODUCTION
client.schema.promote(
    object_type="Equipment",
    from_env="staging",
    to_env="production",
    version="2.0.0",
    require_approval=True,        # Requires approval
    approvers=["architect-team"]
)

#8. Lifecycle Events and Hooks

Python
# Register lifecycle event listener
@client.schema.on_lifecycle_change("Equipment")
def on_equipment_lifecycle(event):
    """
    event.object_type: "Equipment"
    event.old_state: "ACTIVE"
    event.new_state: "DEPRECATED"
    event.reason: "Replaced by EquipmentV2"
    event.actor: "architect@company.com"
    event.timestamp: "2026-03-01T10:00:00Z"
    """
    if event.new_state == "DEPRECATED":
        notify_consumers(event.object_type, event.reason)
        create_migration_ticket(event)
    elif event.new_state == "ARCHIVED":
        cleanup_resources(event.object_type)

# Register pre-hook (can block state transitions)
@client.schema.before_lifecycle_change("Equipment")
def before_equipment_change(event):
    if event.new_state == "ARCHIVED":
        if get_api_call_count(event.object_type, last_days=30) > 0:
            raise BlockTransitionError(
                "Cannot archive: still has API calls in the last 30 days"
            )

#9. Lifecycle Management Best Practices

#9.1 Schema Evolution Strategies

Code
Strategy 1: In-Place Evolution
Use for: Compatible changes
┌──────────────┐         ┌──────────────┐
│ Equipment    │         │ Equipment    │
│ v1.0.0       │  ──►   │ v1.1.0       │
│ ACTIVE       │         │ ACTIVE       │
│              │         │ +description │
└──────────────┘         └──────────────┘

Strategy 2: Side-by-Side
Use for: Breaking changes needing migration time
┌──────────────┐         ┌──────────────┐
│ Equipment    │         │ EquipmentV2  │
│ v2.0.0       │         │ v1.0.0       │
│ DEPRECATED   │  ──►   │ ACTIVE       │
│ sunset: 6/1  │         │ (new design) │
└──────────────┘         └──────────────┘
Both versions run in parallel until old version sunsets

Strategy 3: Clone and Evolve
Use for: Large-scale refactoring
┌──────────────┐    clone    ┌──────────────┐
│ Equipment    │ ──────────► │ EquipmentV3  │
│ ACTIVE       │             │ DRAFT        │
└──────────────┘             └──────┬───────┘
                                    │ modify
                                    ▼
                             ┌──────────────┐
                             │ EquipmentV3  │
                             │ ACTIVE       │
                             └──────────────┘

#9.2 Checklists

Code
□ Before DRAFT → ACTIVE:
  ├── □ Exactly one primary key property
  ├── □ At least one non-primary-key property
  ├── □ All required properties have sensible defaults
  ├── □ All REFERENCE properties point to existing ObjectTypes
  ├── □ All StructType references are registered
  ├── □ All InterfaceType required properties are implemented
  └── □ Naming follows camelCase convention

□ Before ACTIVE → DEPRECATED:
  ├── □ Alternative or migration guide specified
  ├── □ Sunset date set (at least 30 days out)
  ├── □ All known consumer teams notified
  └── □ Migration plan created

□ Before DEPRECATED → ARCHIVED:
  ├── □ All instance data migrated or deleted
  ├── □ All dependencies cleaned up
  ├── □ All ActionTypes redirected or deleted
  ├── □ No API calls in the last 30 days
  └── □ Schema definition backed up

#10. Frequently Asked Questions

#Q1: What if I must make a breaking change to an ACTIVE type?

Use the Proposal workflow (see S4-09 for details):

Python
proposal = client.schema.create_proposal(
    title="Remove deprecated field 'oldStatus' from Equipment",
    changes=[
        {"type": "REMOVE_PROPERTY", "objectType": "Equipment",
         "property": "oldStatus"}
    ],
    migration_plan="Step 1: Update all consumers to use 'status'...",
    rollback_plan="Re-add 'oldStatus' as optional with data backfill"
)

proposal.submit_for_review(reviewers=["architect-team"])
proposal.execute()  # Auto-executes change + bumps to MAJOR version

#Q2: How do I notify consumers after a Schema change?

Python
consumers = client.schema.get_consumers("Equipment")

for consumer in consumers:
    print(f"{consumer.type}: {consumer.name} "
          f"(last access: {consumer.last_access})")

# SDK_CLIENT: frontend-app (last access: 2 min ago)
# SDK_CLIENT: data-pipeline (last access: 1 hour ago)
# DASHBOARD: equipment-monitor (last access: 5 min ago)

client.schema.notify_consumers(
    "Equipment",
    message="Property 'oldStatus' will be removed on 2026-06-01.",
    channel=["email", "webhook"]
)

#Q3: What if I accidentally deprecated an ACTIVE type?

Python
# Can revert before the sunset date
client.schema.reactivate_object_type("Equipment")
# State returns to ACTIVE, API resumes normally, deprecation warnings removed

#Key Takeaways

  1. The four-stage lifecycle is the safety net for Schema evolution. DRAFT allows free experimentation, ACTIVE restricts to compatible changes, DEPRECATED gives consumers time to migrate, ARCHIVED preserves audit history. This is 100x safer than "ALTER TABLE + pray."

  2. The compatibility validation engine is an automatic gatekeeper. Every change to an ACTIVE type goes through compatibility checking; breaking changes must go through the Proposal workflow — eliminating the classic disaster of "developer changed the Schema but forgot to notify the frontend."

  3. The safe deletion protocol prevents dangling references. Through dry-run preview and dependency graph analysis, deletion operations are guaranteed to leave no broken relation chains. Even cascade deletion backs up the Schema definition first.

#Next Article

S4-04: RelationType and Knowledge Graphs: Connecting the Business World with Relations — We'll dive into RelationType's relation modeling, multi-hop traversal, and graph layouts.

tags: object-type, lifecycle, state-machine, schema-evolution, compatibility, versioning, coomia-dip, deprecation