Back to Blog

InterfaceType and StructType: Advanced Type Features

Scenario: Multiple entities have "geographic location"

CoomiaPublished on August 10, 202516 min read
Share this articleTwitter / X

InterfaceType and StructType: Advanced Type Features

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

#TL;DR

  • InterfaceType gives the Ontology object-oriented "interface inheritance" — multiple ObjectTypes can implement the same Interface, enabling polymorphic queries ("find all locatable objects" without caring whether they're equipment, warehouses, or vehicles).
  • StructType is a lightweight "value object" — unlike ObjectType, which has independent identity and lifecycle, Structs are composite data structures embedded in properties, supporting multi-level nesting for addresses, coordinates, contact info, and similar scenarios.
  • Interface inheritance + struct nesting + polymorphic queries work together to elevate the Ontology's expressiveness from simple "table-row-column" to type-safe domain modeling.

#1. Why InterfaceType Is Needed

#1.1 The Problem: How to Express Commonality Across Types?

Code
Scenario: Multiple entities have "geographic location"

┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐
│ Equipment│  │ Warehouse│  │ Vehicle  │  │ Employee │
│          │  │          │  │          │  │          │
│ lat: 31.2│  │ lat: 39.9│  │ lat: 22.3│  │ lat: 30.6│
│ lng: 121 │  │ lng: 116 │  │ lng: 114 │  │ lng: 104 │
│ address  │  │ address  │  │ address  │  │ address  │
└──────────┘  └──────────┘  └──────────┘  └──────────┘

Problems:
├── How to query "all locatable objects within 10 km of me"?
├── Traditional: Query 4 tables separately → UNION ALL → client merges
├── If a new ObjectType (e.g., Sensor) is added, query logic must change
└── No way to express "these types share a common capability" at Schema level

Solution: InterfaceType

┌──────────────────────────────────┐
│    InterfaceType: Locatable      │
│                                  │
│    properties:                   │
│    ├── latitude:  DOUBLE         │
│    ├── longitude: DOUBLE         │
│    └── address:   STRING         │
│                                  │
│    implementedBy:                │
│    ├── Equipment                 │
│    ├── Warehouse                 │
│    ├── Vehicle                   │
│    └── Employee                  │
└──────────────────────────────────┘

Now you can:
  client.objects.search("Locatable", filters={"nearPoint": ...})
  → Auto-queries across all implementing types, returns mixed results

#1.2 InterfaceType vs Traditional Inheritance

Code
┌────────────────┬───────────────────────┬─────────────────────┐
│                │  Traditional OOP      │  InterfaceType       │
├────────────────┼───────────────────────┼─────────────────────┤
│  Multiple      │  Usually unsupported/ │  ✅ One type can     │
│  inheritance   │  diamond problem      │     implement many   │
├────────────────┼───────────────────────┼─────────────────────┤
│  Data storage  │  Usually mapped to    │  Each ObjectType     │
│                │  table inheritance    │  stored independently│
│                │  (STI/MTI)            │  no shared tables    │
├────────────────┼───────────────────────┼─────────────────────┤
│  Query method  │  Query parent table   │  Polymorphic query   │
│                │                       │  auto-aggregates     │
├────────────────┼───────────────────────┼─────────────────────┤
│  Runtime       │  Tight coupling       │  Loose coupling      │
│  coupling      │                       │  Interface changes   │
│                │                       │  don't affect storage│
└────────────────┴───────────────────────┴─────────────────────┘

#2. InterfaceType Data Model

#2.1 Defining an Interface

Python
from ontology_sdk import OntologyClient

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

# Create InterfaceType
interface = client.schema.create_interface_type({
    "apiName": "Locatable",
    "displayName": "Locatable",
    "description": "Represents an entity with geographic location",

    # Properties defined by the interface (implementors must have these)
    "properties": {
        "latitude": {
            "type": "DOUBLE",
            "required": True,
            "description": "Latitude",
            "validations": [
                {"rule": "min", "value": -90},
                {"rule": "max", "value": 90},
            ],
        },
        "longitude": {
            "type": "DOUBLE",
            "required": True,
            "description": "Longitude",
            "validations": [
                {"rule": "min", "value": -180},
                {"rule": "max", "value": 180},
            ],
        },
        "address": {
            "type": "STRING",
            "required": False,
            "description": "Human-readable address",
        },
    },

    # Actions defined by the interface (optional)
    "actions": [
        {
            "apiName": "updateLocation",
            "parameters": {
                "newLatitude": {"type": "DOUBLE", "required": True},
                "newLongitude": {"type": "DOUBLE", "required": True},
            },
        },
    ],
})

#2.2 ObjectType Implementing an Interface

Python
# Make Equipment implement the Locatable interface
client.schema.create_object_type({
    "name": "Equipment",
    "primaryKey": "equipmentId",

    # Declare implemented interfaces
    "implements": ["Locatable", "Auditable", "Maintainable"],

    "properties": {
        "equipmentId": {"type": "STRING", "required": True},
        "name": {"type": "STRING", "required": True},
        "model": {"type": "STRING"},

        # Properties required by Locatable
        "latitude": {"type": "DOUBLE", "required": True},
        "longitude": {"type": "DOUBLE", "required": True},
        "address": {"type": "STRING"},

        # Properties required by Auditable
        "createdAt": {"type": "TIMESTAMP", "required": True},
        "updatedAt": {"type": "TIMESTAMP", "required": True},
        "createdBy": {"type": "STRING", "required": True},

        # Properties required by Maintainable
        "lastMaintenanceDate": {"type": "DATE"},
        "nextMaintenanceDate": {"type": "DATE"},
        "maintenanceStatus": {"type": "ENUM", "enumValues": ["NORMAL", "DUE", "OVERDUE"]},

        # Equipment's own properties
        "serialNumber": {"type": "STRING"},
        "purchaseDate": {"type": "DATE"},
        "warrantyExpiry": {"type": "DATE"},
    },
})

#2.3 Interface Compliance Checking

Python
# System automatically validates whether ObjectType satisfies Interface requirements
try:
    client.schema.create_object_type({
        "name": "Sensor",
        "primaryKey": "sensorId",
        "implements": ["Locatable"],
        "properties": {
            "sensorId": {"type": "STRING", "required": True},
            "latitude": {"type": "DOUBLE", "required": True},
            # Missing longitude!
        },
    })
except InterfaceComplianceError as e:
    print(f"Interface compliance check failed: {e}")
    # "ObjectType 'Sensor' does not satisfy interface 'Locatable':
    #  Missing required property: longitude (DOUBLE)"
    print(f"Missing properties: {e.missing_properties}")
    print(f"Type mismatches: {e.type_mismatches}")

#3. Polymorphic Queries

#3.1 Cross-Type Queries via Interface

Python
# Query all objects implementing the Locatable interface
# No need to know which specific ObjectTypes exist
results = client.objects.search_by_interface("Locatable", {
    "filters": {
        "nearPoint": {
            "latitude": 31.23,
            "longitude": 121.47,
            "radiusKm": 10,
        },
    },
    "orderBy": "distance",
    "maxResults": 50,
})

for obj in results:
    print(f"[{obj.object_type}] {obj.display_name}")
    print(f"  Location: ({obj.latitude}, {obj.longitude})")
    print(f"  Distance: {obj.distance_km:.1f} km")

# Example output:
# [Equipment] CNC-001 CNC Machine
#   Location: (31.24, 121.48)
#   Distance: 1.2 km
# [Warehouse] Pudong Warehouse
#   Location: (31.22, 121.50)
#   Distance: 2.8 km
# [Vehicle] Transport Truck SH-12345
#   Location: (31.20, 121.45)
#   Distance: 3.5 km

#3.2 How Polymorphic Query Execution Works

Code
Polymorphic query execution flow:

1. Resolve Interface
   ┌─────────────────┐
   │  Locatable      │
   │  ├── Equipment  │
   │  ├── Warehouse  │
   │  ├── Vehicle    │
   │  └── Employee   │
   └─────────────────┘

2. Parallel query each implementing type (Fan-out)
   ┌─────────────┐
   │ Equipment   │──── Query with filters ──── Results[0..n]
   │ Warehouse   │──── Query with filters ──── Results[0..m]
   │ Vehicle     │──── Query with filters ──── Results[0..k]
   │ Employee    │──── Query with filters ──── Results[0..j]
   └─────────────┘

3. Merge sort (Fan-in)
   Results[0..n] ─┐
   Results[0..m] ─┼── Merge Sort by distance ── Final Results
   Results[0..k] ─┤
   Results[0..j] ─┘

Performance characteristics:
├── Parallelism = number of implementing types (typically 3-10)
├── Response time ≈ slowest implementing type's query time
├── Result count = sum of all implementations (capped by maxResults)
└── Auto-optimization: skip query if an implementation can't possibly match filters

#3.3 Interface Inheritance

Python
# InterfaceType can also extend other InterfaceTypes
client.schema.create_interface_type({
    "apiName": "TrackableAsset",
    "displayName": "Trackable Asset",

    # Extends Locatable (inherits location properties)
    "extends": ["Locatable"],

    # Additional properties
    "properties": {
        "assetTag": {
            "type": "STRING",
            "required": True,
            "description": "Asset tag number",
        },
        "assetValue": {
            "type": "DOUBLE",
            "required": True,
            "description": "Asset value",
        },
        "depreciationRate": {
            "type": "DOUBLE",
            "description": "Annual depreciation rate",
        },
    },
})

# Equipment satisfies both Locatable and TrackableAsset
# Querying TrackableAsset also finds Equipment

#4. StructType: Value Object Modeling

#4.1 Why StructType Is Needed

Code
Scenario: Modeling address information

Option A: Flat properties (Bad)
┌─────────────────────────────────┐
│  Employee                       │
│  ├── homeCountry: STRING        │
│  ├── homeProvince: STRING       │
│  ├── homeCity: STRING           │
│  ├── homeStreet: STRING         │
│  ├── homePostcode: STRING       │
│  ├── workCountry: STRING        │  ← Property explosion!
│  ├── workProvince: STRING       │
│  ├── workCity: STRING           │
│  ├── workStreet: STRING         │
│  └── workPostcode: STRING       │
└─────────────────────────────────┘

Option B: Independent ObjectType (Overkill)
┌──────────┐  1:N  ┌──────────┐
│ Employee │──────►│ Address  │
└──────────┘       └──────────┘
├── Does address need its own ID? No
├── Does address need an independent lifecycle? No
├── Does address need to be referenced by other objects? No
└── Unnecessary relation management overhead

Option C: StructType (Just Right)
┌─────────────────────────────────┐
│  Employee                       │
│  ├── homeAddress: Address       │  ← Embedded composite structure
│  └── workAddress: Address       │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│  StructType: Address            │
│  ├── country: STRING            │
│  ├── province: STRING           │
│  ├── city: STRING               │
│  ├── street: STRING             │
│  └── postcode: STRING           │
└─────────────────────────────────┘

#4.2 Defining StructType

Python
# Create StructType
client.schema.create_struct_type({
    "apiName": "Address",
    "displayName": "Address",
    "description": "Represents a mailing address",
    "properties": {
        "country": {
            "type": "STRING",
            "required": True,
            "defaultValue": "China",
        },
        "province": {"type": "STRING", "required": True},
        "city": {"type": "STRING", "required": True},
        "district": {"type": "STRING"},
        "street": {"type": "STRING", "required": True},
        "postcode": {
            "type": "STRING",
            "validations": [
                {"rule": "pattern", "value": "^[0-9]{5,6}$", "message": "Invalid postal code format"},
            ],
        },
        "isDefault": {"type": "BOOLEAN", "defaultValue": "false"},
    },
})

# Create GeoPoint StructType
client.schema.create_struct_type({
    "apiName": "GeoPoint",
    "displayName": "Geographic Point",
    "properties": {
        "latitude": {
            "type": "DOUBLE",
            "required": True,
            "validations": [
                {"rule": "min", "value": -90},
                {"rule": "max", "value": 90},
            ],
        },
        "longitude": {
            "type": "DOUBLE",
            "required": True,
            "validations": [
                {"rule": "min", "value": -180},
                {"rule": "max", "value": 180},
            ],
        },
        "altitude": {"type": "DOUBLE", "description": "Altitude (meters)"},
        "accuracy": {"type": "DOUBLE", "description": "Accuracy (meters)"},
    },
})

#4.3 Using StructType in ObjectType

Python
# Reference StructType in ObjectType properties
client.schema.create_object_type({
    "name": "Employee",
    "primaryKey": "employeeId",
    "properties": {
        "employeeId": {"type": "STRING", "required": True},
        "name": {"type": "STRING", "required": True},

        # Using StructType as property type
        "homeAddress": {
            "type": "STRUCT",
            "structType": "Address",
            "required": True,
        },
        "workAddress": {
            "type": "STRUCT",
            "structType": "Address",
        },

        # StructType array
        "emergencyContacts": {
            "type": "ARRAY",
            "itemType": "STRUCT",
            "structType": "ContactInfo",
            "maxItems": 3,
        },
    },
})

# Create objects with embedded Struct values
employee = client.objects.create("Employee", {
    "employeeId": "emp-001",
    "name": "John Smith",
    "homeAddress": {
        "country": "USA",
        "province": "California",
        "city": "San Francisco",
        "district": "SOMA",
        "street": "123 Main St",
        "postcode": "94105",
        "isDefault": True,
    },
    "workAddress": {
        "country": "USA",
        "province": "California",
        "city": "San Francisco",
        "street": "456 Market St",
        "postcode": "94107",
    },
    "emergencyContacts": [
        {"name": "Jane Smith", "phone": "+1-555-0101", "relationship": "Spouse"},
        {"name": "Bob Smith", "phone": "+1-555-0102", "relationship": "Parent"},
    ],
})

#4.4 Nested Queries

Python
# Query nested Struct fields
results = client.objects.search("Employee", {
    "filters": {
        "homeAddress.city": {"eq": "San Francisco"},
        "homeAddress.district": {"eq": "SOMA"},
    },
})

# Query fields within Struct arrays
results = client.objects.search("Employee", {
    "filters": {
        "emergencyContacts.relationship": {"eq": "Spouse"},
    },
})

#5. Struct Nesting: Multi-Level Composite Structures

#5.1 Multi-Level Nesting Definition

Python
# Three-level nesting example: Order → Line Items → Price Breakdown

# Level 1: PriceBreakdown
client.schema.create_struct_type({
    "apiName": "PriceBreakdown",
    "properties": {
        "basePrice": {"type": "DOUBLE", "required": True},
        "discount": {"type": "DOUBLE", "defaultValue": "0"},
        "discountReason": {"type": "STRING"},
        "tax": {"type": "DOUBLE", "required": True},
        "taxRate": {"type": "DOUBLE", "required": True},
        "finalPrice": {"type": "DOUBLE", "required": True},
    },
})

# Level 2: OrderLineItem (contains PriceBreakdown)
client.schema.create_struct_type({
    "apiName": "OrderLineItem",
    "properties": {
        "productId": {"type": "STRING", "required": True},
        "productName": {"type": "STRING", "required": True},
        "quantity": {"type": "INTEGER", "required": True, "validations": [{"rule": "min", "value": 1}]},
        "unit": {"type": "STRING", "defaultValue": "pcs"},
        "pricing": {
            "type": "STRUCT",
            "structType": "PriceBreakdown",  # Nested reference
            "required": True,
        },
        "notes": {"type": "STRING"},
    },
})

# Level 3: Used in Order ObjectType
client.schema.create_object_type({
    "name": "Order",
    "primaryKey": "orderId",
    "properties": {
        "orderId": {"type": "STRING", "required": True},
        "customerId": {"type": "STRING", "required": True},
        "lineItems": {
            "type": "ARRAY",
            "itemType": "STRUCT",
            "structType": "OrderLineItem",
            "minItems": 1,
            "maxItems": 100,
        },
        "shippingAddress": {
            "type": "STRUCT",
            "structType": "Address",
        },
        "totalAmount": {"type": "DOUBLE"},
    },
})

#5.2 Nesting Limits and Best Practices

Code
Nesting depth limits:

coomia-dip supports up to 5 levels of nesting:
  ObjectType
    └── Struct Level 1
        └── Struct Level 2
            └── Struct Level 3
                └── Struct Level 4
                    └── Struct Level 5 (maximum depth)

Alternatives when exceeding 5 levels:
├── Split into independent ObjectType + RelationType
├── Re-evaluate whether the data model is overly complex
└── Consider using JSON type for semi-structured data

Best practices:
├── Keep nesting depth to 2-3 levels
├── Struct properties should not exceed 15 fields
├── Struct arrays should not exceed 50 elements
├── Create indexes for frequently queried nested fields
└── Avoid placing large text fields in Structs (impacts storage efficiency)

#6. InterfaceType + StructType Combined Usage

Python
# Combined example: Monitorable equipment

# StructType: Sensor Reading
client.schema.create_struct_type({
    "apiName": "SensorReading",
    "properties": {
        "sensorId": {"type": "STRING", "required": True},
        "value": {"type": "DOUBLE", "required": True},
        "unit": {"type": "STRING", "required": True},
        "timestamp": {"type": "TIMESTAMP", "required": True},
        "quality": {"type": "ENUM", "enumValues": ["GOOD", "UNCERTAIN", "BAD"]},
    },
})

# InterfaceType: Monitorable
client.schema.create_interface_type({
    "apiName": "Monitorable",
    "displayName": "Monitorable",
    "properties": {
        "healthStatus": {
            "type": "ENUM",
            "enumValues": ["HEALTHY", "WARNING", "CRITICAL", "UNKNOWN"],
            "required": True,
        },
        "lastHeartbeat": {"type": "TIMESTAMP", "required": True},
        "currentReadings": {
            "type": "ARRAY",
            "itemType": "STRUCT",
            "structType": "SensorReading",
        },
    },
    "actions": [
        {"apiName": "resetHealth", "parameters": {}},
        {"apiName": "acknowledgeAlert", "parameters": {
            "alertId": {"type": "STRING", "required": True},
        }},
    ],
})

# ObjectType using both InterfaceType and StructType
client.schema.create_object_type({
    "name": "IndustrialRobot",
    "primaryKey": "robotId",
    "implements": ["Locatable", "Monitorable", "TrackableAsset"],
    "properties": {
        "robotId": {"type": "STRING", "required": True},
        "name": {"type": "STRING", "required": True},
        "model": {"type": "STRING"},
        "manufacturer": {"type": "STRING"},

        # From Locatable
        "latitude": {"type": "DOUBLE", "required": True},
        "longitude": {"type": "DOUBLE", "required": True},
        "address": {"type": "STRING"},

        # From Monitorable
        "healthStatus": {"type": "ENUM", "enumValues": ["HEALTHY", "WARNING", "CRITICAL", "UNKNOWN"], "required": True},
        "lastHeartbeat": {"type": "TIMESTAMP", "required": True},
        "currentReadings": {
            "type": "ARRAY",
            "itemType": "STRUCT",
            "structType": "SensorReading",
        },

        # From TrackableAsset
        "assetTag": {"type": "STRING", "required": True},
        "assetValue": {"type": "DOUBLE", "required": True},
        "depreciationRate": {"type": "DOUBLE"},

        # Own properties
        "operatingHours": {"type": "DOUBLE"},
        "jointConfiguration": {
            "type": "STRUCT",
            "structType": "RobotJointConfig",
        },
    },
})

#7. Type System Comparison: When to Use What

Code
┌─────────────────┬────────────────┬────────────────┬───────────────┐
│     Feature      │  ObjectType    │  InterfaceType │  StructType   │
├─────────────────┼────────────────┼────────────────┼───────────────┤
│  Has identity(PK)│  ✅ Yes        │  ❌ No         │  ❌ No        │
│  Exists alone    │  ✅ Yes        │  ❌ No         │  ❌ No        │
│  Can be ref'd    │  ✅ Yes        │  ❌ No         │  ❌ No        │
│  Has lifecycle   │  ✅ DRAFT→...  │  ✅ DRAFT→...  │  ✅ DRAFT→... │
│  Independently   │  ✅ Yes        │  ✅ Polymorphic│  ❌ No        │
│  queryable       │               │  queries       │              │
│  Can be nested   │  ❌ No         │  ❌ No         │  ✅ Yes       │
│  Storage         │  Dedicated     │  No storage    │  Embedded in  │
│                  │  table/part    │                │  host object  │
│  Typical use     │  Business      │  Common        │  Value        │
│                  │  entities      │  abstractions  │  objects      │
└─────────────────┴────────────────┴────────────────┴───────────────┘

Decision tree:

Need to model a concept?
  │
  ├── Does it have independent identity and lifecycle?
  │   ├── Yes → ObjectType
  │   └── No  → Does it represent commonality across multiple types?
  │             ├── Yes → InterfaceType
  │             └── No  → Is it a composite value?
  │                       ├── Yes → StructType
  │                       └── No  → Simple property (STRING/DOUBLE/...)

#8. Protobuf Definitions

PROTOBUF
message InterfaceType {
    string api_name = 1;
    string display_name = 2;
    string description = 3;

    // Parent interfaces extended
    repeated string extends = 4;

    // Properties defined by the interface
    map<string, PropertyDef> properties = 5;

    // Actions defined by the interface
    repeated ActionSignature actions = 6;

    // ObjectTypes implementing this interface (auto-maintained)
    repeated string implemented_by = 7;

    LifecycleState lifecycle = 8;
}

message StructType {
    string api_name = 1;
    string display_name = 2;
    string description = 3;

    // Struct fields
    map<string, PropertyDef> properties = 4;

    // Types referencing this struct (auto-maintained)
    repeated StructUsage usages = 5;

    LifecycleState lifecycle = 6;
}

message StructUsage {
    string object_type = 1;
    string property_name = 2;
    bool is_array = 3;
}

#9. Real-World Case: Smart Factory Type System

Python
# Complete smart factory type system design

# === StructTypes ===

# Maintenance Record
client.schema.create_struct_type({
    "apiName": "MaintenanceRecord",
    "properties": {
        "date": {"type": "DATE", "required": True},
        "technician": {"type": "STRING", "required": True},
        "type": {"type": "ENUM", "enumValues": ["PREVENTIVE", "CORRECTIVE", "PREDICTIVE"]},
        "description": {"type": "STRING", "required": True},
        "duration_hours": {"type": "DOUBLE"},
        "cost": {"type": "DOUBLE"},
        "parts_replaced": {"type": "ARRAY", "itemType": "STRING"},
    },
})

# Alert Rule
client.schema.create_struct_type({
    "apiName": "AlertRule",
    "properties": {
        "metric": {"type": "STRING", "required": True},
        "operator": {"type": "ENUM", "enumValues": ["GT", "LT", "EQ", "GTE", "LTE"]},
        "threshold": {"type": "DOUBLE", "required": True},
        "severity": {"type": "ENUM", "enumValues": ["INFO", "WARNING", "CRITICAL"]},
        "cooldownMinutes": {"type": "INTEGER", "defaultValue": "15"},
    },
})

# === InterfaceTypes ===

# Auditable
client.schema.create_interface_type({
    "apiName": "Auditable",
    "properties": {
        "createdAt": {"type": "TIMESTAMP", "required": True},
        "updatedAt": {"type": "TIMESTAMP", "required": True},
        "createdBy": {"type": "STRING", "required": True},
        "updatedBy": {"type": "STRING"},
    },
})

# Maintainable
client.schema.create_interface_type({
    "apiName": "Maintainable",
    "extends": ["Auditable"],
    "properties": {
        "lastMaintenanceDate": {"type": "DATE"},
        "nextMaintenanceDate": {"type": "DATE"},
        "maintenanceHistory": {
            "type": "ARRAY",
            "itemType": "STRUCT",
            "structType": "MaintenanceRecord",
        },
        "alertRules": {
            "type": "ARRAY",
            "itemType": "STRUCT",
            "structType": "AlertRule",
        },
    },
    "actions": [
        {"apiName": "scheduleMaintenance", "parameters": {
            "date": {"type": "DATE", "required": True},
            "type": {"type": "STRING", "required": True},
        }},
    ],
})

# === ObjectTypes ===

# CNC Machine
client.schema.create_object_type({
    "name": "CNCMachine",
    "primaryKey": "machineId",
    "implements": ["Locatable", "Monitorable", "Maintainable", "TrackableAsset"],
    "properties": {
        "machineId": {"type": "STRING", "required": True},
        "name": {"type": "STRING", "required": True},
        # ... Locatable properties
        "latitude": {"type": "DOUBLE", "required": True},
        "longitude": {"type": "DOUBLE", "required": True},
        "address": {"type": "STRING"},
        # ... Monitorable properties
        "healthStatus": {"type": "ENUM", "enumValues": ["HEALTHY", "WARNING", "CRITICAL", "UNKNOWN"], "required": True},
        "lastHeartbeat": {"type": "TIMESTAMP", "required": True},
        "currentReadings": {"type": "ARRAY", "itemType": "STRUCT", "structType": "SensorReading"},
        # ... Maintainable properties (includes Auditable)
        "createdAt": {"type": "TIMESTAMP", "required": True},
        "updatedAt": {"type": "TIMESTAMP", "required": True},
        "createdBy": {"type": "STRING", "required": True},
        "updatedBy": {"type": "STRING"},
        "lastMaintenanceDate": {"type": "DATE"},
        "nextMaintenanceDate": {"type": "DATE"},
        "maintenanceHistory": {"type": "ARRAY", "itemType": "STRUCT", "structType": "MaintenanceRecord"},
        "alertRules": {"type": "ARRAY", "itemType": "STRUCT", "structType": "AlertRule"},
        # ... TrackableAsset properties
        "assetTag": {"type": "STRING", "required": True},
        "assetValue": {"type": "DOUBLE", "required": True},
        "depreciationRate": {"type": "DOUBLE"},
        # CNC-specific properties
        "spindleSpeed": {"type": "INTEGER", "description": "Spindle speed RPM"},
        "axisCount": {"type": "INTEGER", "description": "Number of axes (3/4/5)"},
        "maxWorkpieceSize": {"type": "STRUCT", "structType": "Dimensions3D"},
    },
})

# Polymorphic query example
print("=== All Equipment Due for Maintenance ===")
overdue = client.objects.search_by_interface("Maintainable", {
    "filters": {
        "nextMaintenanceDate": {"lt": "2026-03-24"},
        "healthStatus": {"in": ["WARNING", "CRITICAL"]},
    },
    "orderBy": "nextMaintenanceDate",
})

for item in overdue:
    print(f"[{item.object_type}] {item.name}")
    print(f"  Next maintenance: {item.nextMaintenanceDate}")
    print(f"  Health status: {item.healthStatus}")
    print(f"  Maintenance history: {len(item.maintenanceHistory)} records")

#10. Version Evolution and Compatibility

Python
# InterfaceType version evolution

# Safe changes (backward compatible):
# ✅ Add optional properties
# ✅ Add new Actions
# ✅ Relax validation rules (e.g., increase max value)

# Breaking changes (require Schema Change process):
# ❌ Remove existing properties
# ❌ Add required properties (existing implementors may not satisfy)
# ❌ Change property types
# ❌ Tighten validation rules

# StructType version evolution follows the same principles
# Additional note: when StructType is referenced by multiple ObjectTypes,
# changes have a larger blast radius

#Key Takeaways

  1. InterfaceType enables Ontology-level polymorphism — a single query spans multiple ObjectTypes without hardcoding type lists, and new implementing types are automatically included in query scope.
  2. StructType is a property-level composite type — it avoids property explosion (flattening) and over-modeling (independent ObjectType), supporting up to 5 levels of nesting.
  3. An ObjectType can implement multiple Interfaces — similar to multiple inheritance but without the diamond problem, with each Interface's properties independent and conflict-free.
  4. The three type system elements work together: ObjectType (business entities) + InterfaceType (common abstractions) + StructType (value objects) = complete domain modeling capability.
  5. Interface compliance is automatically checked — when creating or modifying an ObjectType, the system validates whether all declared Interface requirements are satisfied.

#Next Article

The next article, S4-07 Derived Properties, will introduce how to let data "compute itself" — through 9 reducers, expression evaluation, and SQL-backed derived property mechanisms, achieving "define once, auto-update."

#ontology #interface-type #struct-type #polymorphic-query #type-system #inheritance #value-object