Files
low-code-react-app-b/docs/architecture/JSON_ORCHESTRATION_COMPLETE.md
2026-01-17 00:33:02 +00:00

15 KiB

JSON Orchestration System - Complete Guide

Overview

The JSON Orchestration System allows you to define entire pages, components, data sources, and actions using JSON schemas. This enables:

  • Zero-code page creation: Build pages without writing React components
  • Dynamic configuration: Change page structure without rebuilding
  • Type safety: Full TypeScript validation of schemas
  • Testability: JSON schemas are easy to validate and test
  • Rapid prototyping: Create new pages by editing JSON files

Architecture

┌─────────────────────────────────────────────────┐
│              JSON Page Schema                    │
│  (Structure, Data Sources, Actions, Components) │
└───────────────┬─────────────────────────────────┘
                │
        ┌───────▼──────┐
        │ PageRenderer │
        └───────┬──────┘
                │
    ┌───────────┼───────────┐
    │           │           │
┌───▼────┐ ┌───▼─────┐ ┌──▼────────┐
│ Data   │ │ Action  │ │ Component │
│ Source │ │ Executor│ │ Registry  │
│ Manager│ │         │ │           │
└────────┘ └─────────┘ └───────────┘

Core Concepts

1. Page Schema

A complete page definition:

interface PageSchema {
  id: string                    // Unique identifier
  name: string                  // Display name
  description?: string          // Optional description
  icon?: string                 // Icon name
  layout: Layout                // Layout configuration
  components: ComponentDef[]    // Component tree
  dataSources?: DataSource[]    // Data sources
  actions?: Action[]            // Available actions
  hooks?: HookConfig[]          // Custom hooks
  seedData?: Record<string, any> // Initial data
}

2. Data Sources

Define where data comes from and how it's managed:

interface DataSource {
  id: string                              // Unique ID for referencing
  type: 'kv' | 'api' | 'computed' | 'static'
  key?: string                            // KV store key
  endpoint?: string                       // API endpoint
  transform?: string                      // Transform function name
  defaultValue?: any                      // Default/fallback value
  dependencies?: string[]                 // Other data sources needed
}

Example:

{
  "dataSources": [
    {
      "id": "todos",
      "type": "kv",
      "key": "user-todos",
      "defaultValue": []
    },
    {
      "id": "user",
      "type": "api",
      "endpoint": "/api/user",
      "defaultValue": null
    },
    {
      "id": "stats",
      "type": "computed",
      "dependencies": ["todos"],
      "transform": "calculateStats"
    }
  ]
}

3. Actions

Define what happens when users interact:

interface Action {
  id: string                                           // Action identifier
  type: 'create' | 'update' | 'delete' | 'navigate'
       | 'api' | 'transform' | 'custom'
  target?: string                                      // Target data source
  payload?: Record<string, any>                        // Action payload
  handler?: string                                     // Custom handler name
}

Action Types:

  • create: Add new item to data source
  • update: Modify existing item
  • delete: Remove item
  • navigate: Change route/tab
  • api: Make HTTP request
  • transform: Transform data using custom function
  • custom: Execute custom handler

Example:

{
  "actions": [
    {
      "id": "add-todo",
      "type": "create",
      "target": "todos"
    },
    {
      "id": "complete-todo",
      "type": "update",
      "target": "todos"
    },
    {
      "id": "delete-todo",
      "type": "delete",
      "target": "todos"
    },
    {
      "id": "refresh-data",
      "type": "api",
      "target": "todos",
      "payload": {
        "endpoint": "/api/todos",
        "method": "GET"
      }
    },
    {
      "id": "export",
      "type": "custom",
      "handler": "handleExport"
    }
  ]
}

4. Components

Define the component tree structure:

interface ComponentDef {
  id: string                                // Unique component ID
  type: string                              // Component name (from registry)
  props?: Record<string, any>               // Component props
  children?: ComponentDef[]                 // Child components
  dataBinding?: string                      // Bind to data source
  eventHandlers?: Record<string, string>    // Event → Action mapping
}

Example:

{
  "components": [
    {
      "id": "todo-list",
      "type": "Card",
      "props": {
        "className": "max-w-2xl mx-auto"
      },
      "children": [
        {
          "id": "header",
          "type": "CardHeader",
          "children": [
            {
              "id": "title",
              "type": "CardTitle",
              "props": {
                "children": "My Todos"
              }
            }
          ]
        },
        {
          "id": "content",
          "type": "CardContent",
          "dataBinding": "todos",
          "children": [
            {
              "id": "add-button",
              "type": "Button",
              "props": {
                "children": "Add Todo"
              },
              "eventHandlers": {
                "onClick": "add-todo"
              }
            }
          ]
        }
      ]
    }
  ]
}

5. Layout

Define how components are arranged:

interface Layout {
  type: 'single' | 'split' | 'grid' | 'tabs' | 'flex'
  direction?: 'horizontal' | 'vertical' | 'row' | 'column'
  panels?: PanelConfig[]
}

interface PanelConfig {
  id: string
  minSize?: number
  maxSize?: number
  defaultSize?: number
}

Example:

{
  "layout": {
    "type": "split",
    "direction": "horizontal",
    "panels": [
      {
        "id": "sidebar",
        "minSize": 15,
        "maxSize": 30,
        "defaultSize": 20
      },
      {
        "id": "main",
        "defaultSize": 80
      }
    ]
  }
}

Complete Examples

Example 1: Simple Todo List Page

{
  "id": "todo-list",
  "name": "Todo List",
  "description": "Simple todo list application",
  "icon": "CheckSquare",
  "layout": {
    "type": "single"
  },
  "dataSources": [
    {
      "id": "todos",
      "type": "kv",
      "key": "user-todos",
      "defaultValue": []
    }
  ],
  "components": [
    {
      "id": "root",
      "type": "Card",
      "props": {
        "className": "max-w-2xl mx-auto mt-8"
      },
      "children": [
        {
          "id": "header",
          "type": "CardHeader",
          "children": [
            {
              "id": "title",
              "type": "CardTitle",
              "props": {
                "children": "My Tasks"
              }
            }
          ]
        },
        {
          "id": "content",
          "type": "CardContent",
          "props": {
            "className": "space-y-4"
          },
          "children": [
            {
              "id": "input",
              "type": "Input",
              "props": {
                "placeholder": "What needs to be done?",
                "id": "todo-input"
              },
              "eventHandlers": {
                "onKeyDown": "add-on-enter"
              }
            },
            {
              "id": "add-button",
              "type": "Button",
              "props": {
                "children": "Add Task",
                "className": "w-full"
              },
              "eventHandlers": {
                "onClick": "add-todo"
              }
            }
          ]
        }
      ]
    }
  ],
  "actions": [
    {
      "id": "add-todo",
      "type": "create",
      "target": "todos"
    },
    {
      "id": "toggle-todo",
      "type": "update",
      "target": "todos"
    },
    {
      "id": "delete-todo",
      "type": "delete",
      "target": "todos"
    },
    {
      "id": "add-on-enter",
      "type": "custom",
      "handler": "handleEnterKey"
    }
  ],
  "seedData": {
    "todos": [
      {
        "id": "1",
        "text": "Welcome to JSON-driven pages!",
        "completed": false
      }
    ]
  }
}

Example 2: Dashboard with Multiple Data Sources

{
  "id": "dashboard",
  "name": "Project Dashboard",
  "description": "Overview of project metrics",
  "icon": "ChartBar",
  "layout": {
    "type": "grid"
  },
  "dataSources": [
    {
      "id": "files",
      "type": "kv",
      "key": "project-files",
      "defaultValue": []
    },
    {
      "id": "models",
      "type": "kv",
      "key": "project-models",
      "defaultValue": []
    },
    {
      "id": "components",
      "type": "kv",
      "key": "project-components",
      "defaultValue": []
    },
    {
      "id": "stats",
      "type": "computed",
      "dependencies": ["files", "models", "components"],
      "transform": "calculateProjectStats"
    }
  ],
  "components": [
    {
      "id": "stats-grid",
      "type": "div",
      "props": {
        "className": "grid grid-cols-3 gap-4 p-4"
      },
      "children": [
        {
          "id": "files-card",
          "type": "Card",
          "children": [
            {
              "id": "files-content",
              "type": "CardContent",
              "props": {
                "className": "pt-6"
              },
              "dataBinding": "files",
              "children": [
                {
                  "id": "files-count",
                  "type": "div",
                  "props": {
                    "className": "text-2xl font-bold"
                  }
                },
                {
                  "id": "files-label",
                  "type": "div",
                  "props": {
                    "children": "Files",
                    "className": "text-muted-foreground"
                  }
                }
              ]
            }
          ]
        },
        {
          "id": "models-card",
          "type": "Card",
          "children": [
            {
              "id": "models-content",
              "type": "CardContent",
              "props": {
                "className": "pt-6"
              },
              "dataBinding": "models"
            }
          ]
        },
        {
          "id": "components-card",
          "type": "Card",
          "children": [
            {
              "id": "components-content",
              "type": "CardContent",
              "props": {
                "className": "pt-6"
              },
              "dataBinding": "components"
            }
          ]
        }
      ]
    }
  ],
  "actions": [
    {
      "id": "navigate-to-files",
      "type": "navigate",
      "target": "code"
    },
    {
      "id": "navigate-to-models",
      "type": "navigate",
      "target": "models"
    }
  ]
}

Using the PageRenderer

In your application:

import { PageRenderer } from '@/config/orchestration'
import dashboardSchema from '@/config/pages/dashboard.json'

function DashboardPage() {
  const handleNavigate = (path: string) => {
    // Your navigation logic
    router.push(path)
  }

  const customHandlers = {
    handleExport: async () => {
      // Custom export logic
      const data = await exportData()
      downloadFile(data)
    },
    calculateStats: (todos: any[]) => {
      return {
        total: todos.length,
        completed: todos.filter(t => t.completed).length
      }
    }
  }

  return (
    <PageRenderer
      schema={dashboardSchema}
      onNavigate={handleNavigate}
      customHandlers={customHandlers}
    />
  )
}

Component Registry

Register all components that can be used in JSON schemas:

// src/config/orchestration/component-registry.ts
import { ComponentType } from 'react'
import { Button } from '@/components/ui/button'
import { MyCustomComponent } from '@/components/MyCustomComponent'

export const ComponentRegistry: Record<string, ComponentType<any>> = {
  Button,
  Input,
  Card,
  // ... shadcn components
  
  // Custom components
  MyCustomComponent,
  TodoList,
  Dashboard,
}

Advanced Patterns

Conditional Rendering

Use computed data sources:

{
  "dataSources": [
    {
      "id": "user",
      "type": "kv",
      "key": "current-user"
    },
    {
      "id": "isAdmin",
      "type": "computed",
      "dependencies": ["user"],
      "transform": "checkIsAdmin"
    }
  ]
}

Nested Data Binding

Bind to nested properties:

{
  "id": "email-input",
  "type": "Input",
  "dataBinding": "user.profile.email"
}

Batch Actions

Execute multiple actions:

{
  "id": "save-all",
  "type": "custom",
  "handler": "saveAllChanges",
  "payload": {
    "actions": ["save-files", "save-models", "save-components"]
  }
}

Best Practices

  1. Keep schemas focused: One page = one schema
  2. Use meaningful IDs: Makes debugging easier
  3. Leverage seed data: For examples and testing
  4. Document custom handlers: In the schema description
  5. Validate schemas: Use Zod validation before runtime
  6. Version your schemas: Track changes over time
  7. Test with different data: Ensure schemas work with various inputs

Migration Strategy

To migrate existing components to JSON:

  1. Identify static structure: What doesn't change?
  2. Extract data dependencies: What data does it need?
  3. Map actions: What can users do?
  4. Create JSON schema: Follow the structure
  5. Test with PageRenderer: Verify functionality
  6. Add custom handlers: For complex logic
  7. Remove old component: Once verified

Performance Considerations

  • JSON schemas are parsed once
  • Component registry lookups are O(1)
  • Data sources use React hooks (memoized)
  • Actions are executed asynchronously
  • Large schemas can be code-split

Debugging

Enable debug mode:

<PageRenderer
  schema={schema}
  debug={true} // Logs all actions and data changes
/>

TypeScript Support

All schemas are fully typed:

import { PageSchema } from '@/config/orchestration'

const mySchema: PageSchema = {
  // Full type checking and autocomplete
}

Testing

Test schemas independently:

import { PageSchemaDefinition } from '@/config/orchestration/schema'
import dashboardSchema from '@/config/pages/dashboard.json'

test('dashboard schema is valid', () => {
  const result = PageSchemaDefinition.safeParse(dashboardSchema)
  expect(result.success).toBe(true)
})

Future Enhancements

  • Visual schema editor
  • Schema hot-reloading
  • A/B testing support
  • Schema versioning
  • Analytics integration
  • Performance profiling