Successfully implemented the dual-table projection logic for the metadata service. This establishes the architectural foundation for treating metadata as queryable data following industry best practices from Salesforce, ServiceNow, and Kubernetes.
The dual-table pattern maintains two layers:
-
sys_metadata (Source of Truth)
- Package management
- Version control
- Deployment tracking
- History via sys_metadata_history
-
Type-Specific Tables (Queryable Projections)
- sys_object — Object definitions
- sys_view — View configurations
- sys_agent — AI agent metadata
- sys_tool — AI tool registry
- sys_flow — Automation flows
Created packages/metadata/src/projection/metadata-projector.ts with:
- Project transformation functions for each metadata type
- Denormalization logic to flatten complex structures for querying
- Support for both IDataDriver and IDataEngine (ObjectQL)
- Automatic CRUD operations on projection tables
Key methods:
project(type, name, data)— Create/update projectiondeleteProjection(type, name)— Remove projectiontransformToProjection(type, name, data)— Type-specific transformation
Updated packages/metadata/src/loaders/database-loader.ts:
- Added
enableProjectionconfiguration option (default: true) - Integrated
MetadataProjectorinto save() flow - Added projection cleanup in delete() flow
- Projection occurs AFTER sys_metadata save (async safety)
Updated packages/metadata/src/plugin.ts:
- Added dependency on
@objectstack/objectos - Registered all system objects from SystemObjects registry
- Objects registered via manifest service during plugin init()
- Includes: sys_object, sys_view, sys_agent, sys_tool, sys_flow
Each metadata type is projected with denormalized fields for efficient querying:
- Complex structures (fields, indexes, validations) → JSON columns
- Capabilities → individual boolean columns for filtering
- Denormalized field_count for sorting/filtering
- Columns, filters, sort, config → JSON columns
- Display options → individual columns
- Object reference for joins
- Model configuration → individual columns
- Tools, skills → JSON columns
- Memory settings → individual columns
- Parameters schema → JSON column
- Handler code → text column
- Nodes, edges, variables → JSON columns
- Trigger configuration → individual columns
- Active status for filtering
- Unified Query Protocol: Metadata can be queried using Object Protocol API
- Studio Auto-Generation: UI can use
/api/v1/data/sys_*endpoints - Efficient Filtering: Denormalized fields enable fast queries
- Preserved History: sys_metadata maintains full version control
- Package Tracking: All projections include package_id and managed_by
packages/metadata/src/projection/metadata-projector.ts— Projection servicepackages/metadata/src/projection/index.ts— Module exports
packages/metadata/src/loaders/database-loader.ts— Added projection integrationpackages/metadata/src/plugin.ts— Registered system objectspackages/metadata/src/index.ts— Exported projection modulepackages/metadata/package.json— Added @objectstack/objectos dependencyOBJECTOS_IMPLEMENTATION.md— Updated Phase 1 status
// Projection happens automatically when saving metadata
await metadataService.register('object', 'account', {
name: 'account',
label: 'Account',
fields: { /* ... */ },
// ... object definition
});
// Results in TWO database writes:
// 1. sys_metadata: Full envelope with JSON payload
// 2. sys_object: Denormalized projection with queryable columns
// Studio can now query via Object Protocol
const objects = await client.data.find('sys_object', {
filter: { namespace: 'crm' },
sort: 'name',
});Phase 2 will focus on Studio integration:
- Update Studio to use type-specific table queries
- Auto-generate metadata management UI
- Leverage existing grid/form/kanban components
Phase 3 will add:
- Comprehensive test coverage
- Documentation updates
- Migration guides for existing deployments