Skip to content

Latest commit

 

History

History
643 lines (514 loc) · 18.3 KB

File metadata and controls

643 lines (514 loc) · 18.3 KB
title Protocol Architecture
description How the Data, System, and UI protocols work together as one cohesive system

import { Database, Layout, Cpu, ArrowRight, CheckCircle, Workflow, Bot, Cloud } from 'lucide-react';

The Protocol Stack

The architecture is built on foundational protocols that work together as a unified system:

} title="Data Protocol" description="ObjectQL: Structure, queries, and constraints." /> } title="UI Protocol" description="ObjectUI: Presentation, interaction, and routing." /> } title="System Protocol" description="ObjectOS: Control, runtime, and governance." /> } title="Automation Protocol" description="Business Logic: Flow, Workflow, Triggers." /> } title="AI Protocol" description="Intelligence: Agents, RAG, Models." /> } title="Cloud Protocol" description="Management: Multi-tenancy, Marketplace, Licensing." />

Why Separated Layers?

Traditional applications tightly couple data, business logic, and presentation. This creates Implementation Coupling — changing one layer forces changes across the entire stack.

ObjectStack enforces Separation of Concerns through protocol boundaries:

┌───────────────────────────────────────────────────────────┐
│              UI Protocol & AI Protocol                    │
│  Apps, Views, Dashboards, Agents, RAG                     │
│  "How do users and agents interact?"                       │
└───────────────────────────┬───────────────────────────────┘
                            │ Interface
┌───────────────────────────┴───────────────────────────────┐
│    System Protocol & Automation Protocol & Cloud Protocol │
│  Auth, Permissions, Orchestration, Multi-tenancy          │
│  "Who/What can do what, when, and where?"                  │
└───────────────────────────┬───────────────────────────────┘
                            │ Control
┌───────────────────────────┴───────────────────────────────┐
│                     Data Protocol                         │
│  Objects, Fields, Queries, Drivers                        │
│  "What is the data structure?"                             │
└───────────────────────────────────────────────────────────┘

Layer 1: ObjectQL (Data Protocol)

Role: Define the Structure and Intent of data.

Responsibilities:

  • Object schema definitions (what is a "Customer"?)
  • Field types and validation rules
  • Query language (filtering, sorting, aggregation)
  • Database drivers (Postgres, MongoDB, SQLite)

Key Principle: ObjectQL knows nothing about users, permissions, or UI. It only cares about data structure and queries.

Example: Defining a Customer Object

// packages/crm/src/objects/customer.object.ts
import { ObjectSchema, Field } from '@objectstack/spec/data';

export const Customer = ObjectSchema.create({
  name: 'customer',
  label: 'Customer',
  icon: 'building',
  
  fields: {
    name: Field.text({
      label: 'Company Name',
      required: true,
      maxLength: 120,
    }),
    
    industry: Field.select({
      label: 'Industry',
      options: [
        { label: 'Technology', value: 'technology' },
        { label: 'Finance', value: 'finance' },
        { label: 'Healthcare', value: 'healthcare' },
        { label: 'Retail', value: 'retail' },
      ],
    }),
    
    annual_revenue: Field.currency({
      label: 'Annual Revenue',
      scale: 2,
    }),
    
    primary_contact: Field.lookup('contact', {
      label: 'Primary Contact',
    }),
  },
});

This definition is pure metadata. It doesn't know:

  • Who can see this data
  • How to render a form
  • When to trigger workflows

That's the job of the other layers.

Layer 2: ObjectOS (Control Protocol)

Role: Manage the Lifecycle and Governance of requests.

Responsibilities:

  • Authentication (who is this user?)
  • Authorization (can they access this field?)
  • Workflows and automations (what happens after save?)
  • Event processing (audit logs, notifications)
  • Multi-tenancy and data isolation

Key Principle: ObjectOS acts as the Gateway. No layer can directly access the database; all requests must pass through the OS Kernel.

Example: Permission Rules

// packages/crm/src/permissions/sales_rep.permission.ts
import { definePermissionSet } from '@objectstack/spec/security';

// A permission set is keyed by object name. Each object grants the four
// CRUD verbs (allowCreate / allowRead / allowEdit / allowDelete), and
// optional field-level security keyed by "object.field".
export const SalesRep = definePermissionSet({
  name: 'sales_rep',
  isProfile: true,
  objects: {
    customer: {
      allowCreate: true,
      allowRead: true,
      allowEdit: true,
      allowDelete: false, // Only managers can delete
    },
  },
  fields: {
    'customer.annual_revenue': { readable: true, editable: false }, // Read-only
  },
});

Example: Workflow Automation

Automations are authored with defineFlow. A flow is a node graph — a typed start/end plus decision and action nodes wired together by edges — driven by a trigger type (here record_change):

// packages/crm/src/flows/high_value_customer.flow.ts
import { defineFlow } from '@objectstack/spec';

export const HighValueCustomer = defineFlow({
  name: 'high_value_customer',
  label: 'High-Value Customer Routing',
  type: 'record_change', // runs when a customer record changes
  nodes: [
    { id: 'start', type: 'start', label: 'Customer created' },
    { id: 'assign', type: 'action', label: 'Assign to enterprise team' },
    { id: 'notify', type: 'action', label: 'Alert sales leadership' },
    { id: 'end', type: 'end', label: 'End' },
  ],
  edges: [
    // Only branch when annual_revenue > 1,000,000 (CEL condition)
    { id: 'e1', source: 'start', target: 'assign', condition: 'record.annual_revenue > 1000000' },
    { id: 'e2', source: 'assign', target: 'notify' },
    { id: 'e3', source: 'notify', target: 'end' },
  ],
});
The node/edge wiring above is illustrative. For the full set of node types, trigger semantics, and condition syntax, see the [automation skill](/docs/protocol/objectos) and the `defineFlow` schema in `@objectstack/spec/automation`.

ObjectOS orchestrates these rules at runtime, independent of the data structure or UI.

Layer 3: ObjectUI (View Protocol)

Role: Render the Presentation and handle User Interaction.

Responsibilities:

  • App navigation and branding
  • List views (grid, kanban, calendar)
  • Form layouts (simple, tabbed, wizard)
  • Dashboards and reports
  • Actions and buttons

Key Principle: ObjectUI is a Rendering Engine, not a hardcoded interface. It asks ObjectQL "What is the schema?" and dynamically generates the UI.

Example: List View

// packages/crm/src/views/customer.view.ts
import { defineView } from '@objectstack/spec';

// A view is a container bound to an object; its `list` slot holds the
// list-view config. Columns are field names; sort is { field, order }.
export const CustomerView = defineView({
  object: 'customer',
  list: {
    label: 'All Customers',
    type: 'grid',
    columns: ['name', 'industry', 'annual_revenue', 'primary_contact'],
    sort: [{ field: 'name', order: 'asc' }],
  },
});

Example: Form View

// packages/crm/src/views/customer.view.ts
import { defineView } from '@objectstack/spec';

// The same view container can also carry a `form` slot. A form is laid out
// with `sections`, each section listing the fields it renders.
export const CustomerView = defineView({
  object: 'customer',
  form: {
    label: 'Customer Details',
    type: 'tabbed',
    sections: [
      {
        label: 'Company Information',
        columns: 2,
        fields: ['name', 'industry', 'annual_revenue'],
      },
      {
        label: 'Contact',
        fields: ['primary_contact'],
      },
    ],
  },
});

The UI doesn't "know" the field types. It asks ObjectQL for the schema and renders accordingly:

  • Field.text → Text input
  • Field.select → Dropdown
  • Field.lookup → Autocomplete lookup
  • Field.currency → Number input with currency formatting

How They Work Together

Let's trace a real-world scenario: A sales rep creates a new high-value customer.

The snippets in this walkthrough (`Auth.getCurrentUser`, `Permission.check`, `ObjectQL.getSchema`, `driver.insert`, `Workflow.getTriggersFor`, …) are **conceptual pseudo-code** that illustrate the control flow between layers. They are not literal exported APIs — don't search for these exact symbols.

Step 1: User Action (ObjectUI)

User fills out the "Create Customer" form:
- Name: "Acme Corp"
- Industry: "Technology"
- Annual Revenue: $5,000,000
- Primary Contact: "John Doe"

User clicks "Save"

Step 2: UI Layer Sends Request

// ObjectUI dispatches an action to ObjectOS
const request = {
  action: 'create',
  object: 'customer',
  data: {
    name: 'Acme Corp',
    industry: 'technology',
    annual_revenue: 5000000,
    primary_contact: 'contact_12345',
  },
};

Step 3: ObjectOS Validates Permissions

// Kernel checks: Does this user have permission?
const user = await Auth.getCurrentUser();
const canCreate = await Permission.check({
  user,
  object: 'customer',
  operation: 'create',
});

if (!canCreate) {
  throw new Error('Permission denied');
}

Step 4: ObjectOS Validates Data

// Kernel asks ObjectQL: Is this data valid?
const schema = ObjectQL.getSchema('customer');
const validation = schema.validate(request.data);

if (!validation.success) {
  throw new ValidationError(validation.errors);
}

Step 5: ObjectQL Writes to Database

// ObjectQL compiles the request into a database operation
const driver = ObjectQL.getDriver(); // Postgres, MongoDB, etc.
const result = await driver.insert('customer', {
  name: 'Acme Corp',
  industry: 'technology',
  annual_revenue: 5000000,
  primary_contact_id: 'contact_12345',
});

Step 6: ObjectOS Triggers Workflows

// Kernel checks: Are there any workflows for this event?
const workflows = Workflow.getTriggersFor('customer', 'after_create');

for (const workflow of workflows) {
  if (workflow.conditionsMet(result)) {
    await workflow.execute(result);
  }
}

// In this case:
// ✅ Annual revenue > $1M
// → Assign to enterprise sales team
// → Send alert email to leadership

Step 7: ObjectUI Updates Display

// Kernel returns success response
// UI optimistically updates the screen
// UI shows toast notification: "Customer created successfully"
// UI navigates to the new customer detail page

The Full Stack in Action

Here's how all three protocols collaborate for a Kanban Board feature:

1. ObjectQL: Define the Data

import { ObjectSchema, Field } from '@objectstack/spec/data';

export const Opportunity = ObjectSchema.create({
  name: 'opportunity',
  label: 'Opportunity',
  icon: 'target',
  
  fields: {
    title: Field.text({ 
      label: 'Title',
      required: true,
    }),
    
    stage: Field.select({
      label: 'Stage',
      options: [
        { label: 'Prospecting', value: 'prospecting', default: true },
        { label: 'Qualification', value: 'qualification' },
        { label: 'Proposal', value: 'proposal' },
        { label: 'Closed Won', value: 'closed_won' },
      ],
    }),
    
    amount: Field.currency({
      label: 'Amount',
      scale: 2,
    }),
    
    customer: Field.lookup('customer', {
      label: 'Customer',
    }),
  },
});

2. ObjectOS: Define Business Rules

import { defineFlow } from '@objectstack/spec';

export const OpportunityWon = defineFlow({
  name: 'opportunity_won',
  label: 'On Opportunity Won',
  type: 'record_change',
  nodes: [
    { id: 'start', type: 'start', label: 'Stage changed' },
    { id: 'invoice', type: 'action', label: 'Create invoice' },
    { id: 'notify', type: 'action', label: 'Notify sales team' },
    { id: 'end', type: 'end', label: 'End' },
  ],
  edges: [
    { id: 'e1', source: 'start', target: 'invoice', condition: "record.stage == 'closed_won'" },
    { id: 'e2', source: 'invoice', target: 'notify' },
    { id: 'e3', source: 'notify', target: 'end' },
  ],
});

3. ObjectUI: Define the Kanban View

import { defineView } from '@objectstack/spec';

// For a kanban list, set type: 'kanban' and put the board config under
// `kanban` (groupByField selects the column axis). `columns` is the list
// of card fields. Drag-and-drop between columns is built in.
export const OpportunityBoard = defineView({
  object: 'opportunity',
  list: {
    type: 'kanban',
    columns: ['title', 'amount', 'customer'],
    kanban: {
      groupByField: 'stage',
      summarizeField: 'amount',
      columns: ['title', 'amount'],
    },
  },
});

The Result

When a user drags an opportunity card from "Proposal" to "Closed Won":

  1. ObjectUI captures the drag-drop event
  2. ObjectOS checks if the user has permission to update the stage field
  3. ObjectQL validates that "closed_won" is a valid option
  4. ObjectQL writes the update to the database
  5. ObjectOS triggers the workflow (create invoice, send notification)
  6. ObjectUI updates the kanban board to reflect the new state

All from metadata. Zero hardcoded logic.

Benefits of the Three-Layer Architecture

1. Technology Independence

Swap implementations without breaking the system:

Same Metadata Definitions
          ↓
┌─────────┼─────────┐
ObjectQL: │         │
Postgres  │    MongoDB
          │
ObjectOS: │
Node.js   │    Python
          │
ObjectUI: │
React     │    Flutter

2. Parallel Development

Teams can work independently on each layer:

  • Data Team: Define objects in ObjectQL
  • Backend Team: Build workflows in ObjectOS
  • Frontend Team: Create views in ObjectUI

All communicate through protocol contracts, not code dependencies.

3. Incremental Migration

Adopt ObjectStack gradually:

  • Phase 1: Use ObjectQL as an ORM replacement
  • Phase 2: Add ObjectOS for permissions and workflows
  • Phase 3: Build ObjectUI views to replace custom forms

Each layer is independently useful.

4. Testability

Mock any layer for testing:

// Test ObjectOS workflows without a real database
const mockObjectQL = {
  getSchema: () => CustomerSchema,
  insert: jest.fn(),
};

// Test ObjectUI rendering without a real backend
const mockObjectOS = {
  checkPermission: () => true,
  executeQuery: () => mockData,
};

Summary

Layer Role Knows About Doesn't Know About
ObjectQL Data structure & queries Schema, fields, drivers Users, permissions, UI
ObjectOS Runtime & governance Auth, workflows, events Data structure, UI layout
ObjectUI Presentation & interaction Layout, navigation, actions Business logic, data storage

The three protocols are loosely coupled but tightly integrated:

  • They communicate through standard contracts (Zod schemas)
  • They can be swapped or upgraded independently
  • They form a complete system when combined

Next Steps


Appendix: Protocol Dependencies

Understanding the dependency chain helps you design applications correctly.

Data Layer (ObjectQL)

Field Protocol (Core)
    ↓
    ├→ Object Protocol (uses Fields)
    ├→ Query Protocol (references Fields)
    ├→ Filter Protocol (filters on Fields)
    └→ Validation Protocol (validates Fields)
         ↓
         └→ Hook Protocol (extends validation with code)

UI Layer (ObjectUI)

Theme Protocol (Foundation)
    ↓
View Protocol (uses Object, Query)
    ├→ ListView (uses Query, Filter)
    ├→ FormView (uses Object, Field)
    └→ Dashboard (uses View, Widget)
         ↓
         ├→ Page Protocol (composes Views)
         └→ App Protocol (organizes Pages)
              ↓
              └→ Action Protocol (UI interactions)

System Layer (ObjectOS)

Data Driver Contracts (Database Abstraction)
    ↓
Datasource Protocol (Connection Config)
    ↓
    ├→ Context Protocol (Runtime State)
    ├→ Events Protocol (Event Bus)
    └→ Plugin Protocol (Extensibility)
         ↓
         ├→ Security Protocol (Access Control)
         ├→ API Protocol (External Access)
         └→ Automation Protocol (Workflows)
              ↓
              └→ AI Protocol (Intelligence)

Appendix: Usage Patterns

Pattern 1: Data-Driven UI

UI auto-generated from data definitions:

Object → Field → View → Page → App

Pattern 2: Custom UI with Data Binding

Custom UI connected to data:

Page → Component → View (Custom) → Query → Object

Pattern 3: Automation & Integration

Business logic automation:

Object → Hook → Flow → Automation → API/Webhook

Pattern 4: AI-Enhanced Applications

AI capabilities on top of data:

Object → RAG Pipeline → Agent → Conversation → UI