|
| 1 | +# Runtime Core Architecture |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The `@objectql/runtime-core` package implements the core plugin system and query pipeline for ObjectQL, following the principle of **Protocol/Spec vs Runtime/Implementation** separation. |
| 6 | + |
| 7 | +## Architecture Principles |
| 8 | + |
| 9 | +### 1. Protocol Layer (from `@objectql/types`) |
| 10 | +- **BasePlugin**: Interface defining plugin structure with metadata and lifecycle |
| 11 | +- **QueryProcessorPlugin**: Interface for plugins that process queries |
| 12 | +- **PluginMetadata**: Standardized plugin information including dependencies |
| 13 | + |
| 14 | +### 2. Runtime Layer (this package) |
| 15 | +- **PluginManager**: Implements dependency resolution and lifecycle management |
| 16 | +- **QueryPipeline**: Implements async series waterfall query processing |
| 17 | +- **Runtime**: Orchestrates plugins and provides query execution |
| 18 | + |
| 19 | +## Key Components |
| 20 | + |
| 21 | +### PluginManager |
| 22 | + |
| 23 | +**Responsibilities:** |
| 24 | +- Register plugins |
| 25 | +- Resolve dependencies using topological sort |
| 26 | +- Boot plugins in dependency order |
| 27 | +- Manage plugin lifecycle (setup/teardown) |
| 28 | + |
| 29 | +**Algorithm: Topological Sort** |
| 30 | +```typescript |
| 31 | +// Ensures dependencies are initialized before dependents |
| 32 | +// Detects circular dependencies |
| 33 | +// Throws errors for missing dependencies |
| 34 | +``` |
| 35 | + |
| 36 | +**Example:** |
| 37 | +```typescript |
| 38 | +const manager = new PluginManager(); |
| 39 | +manager.register(pluginA); // No dependencies |
| 40 | +manager.register(pluginB); // Depends on A |
| 41 | +manager.register(pluginC); // Depends on B |
| 42 | + |
| 43 | +await manager.boot(runtime); |
| 44 | +// Execution order: A → B → C |
| 45 | +``` |
| 46 | + |
| 47 | +### QueryPipeline |
| 48 | + |
| 49 | +**Responsibilities:** |
| 50 | +- Execute queries through registered processors |
| 51 | +- Implement async series waterfall pattern |
| 52 | +- Validate queries before execution |
| 53 | +- Transform queries and results through plugin chain |
| 54 | + |
| 55 | +**Execution Flow:** |
| 56 | +``` |
| 57 | +1. validateQuery (all plugins) |
| 58 | + ↓ |
| 59 | +2. beforeQuery (waterfall: plugin1 → plugin2 → ...) |
| 60 | + ↓ |
| 61 | +3. execute (driver) |
| 62 | + ↓ |
| 63 | +4. afterQuery (waterfall: plugin1 → plugin2 → ...) |
| 64 | + ↓ |
| 65 | +5. return results |
| 66 | +``` |
| 67 | + |
| 68 | +**Waterfall Pattern:** |
| 69 | +- Each plugin receives output from previous plugin |
| 70 | +- Plugins can transform queries/results |
| 71 | +- Final output is returned to caller |
| 72 | + |
| 73 | +**Example:** |
| 74 | +```typescript |
| 75 | +// Plugin 1 adds field |
| 76 | +beforeQuery(query) { |
| 77 | + return { ...query, fields: ['id', 'name'] }; |
| 78 | +} |
| 79 | + |
| 80 | +// Plugin 2 adds filter (receives plugin 1's output) |
| 81 | +beforeQuery(query) { |
| 82 | + return { ...query, filters: [['active', '=', true]] }; |
| 83 | +} |
| 84 | + |
| 85 | +// Final query: { fields: ['id', 'name'], filters: [['active', '=', true]] } |
| 86 | +``` |
| 87 | + |
| 88 | +### Runtime |
| 89 | + |
| 90 | +**Responsibilities:** |
| 91 | +- Provide factory function `createRuntime()` |
| 92 | +- Manage plugin manager and query pipeline |
| 93 | +- Expose simple API for query execution |
| 94 | +- Handle initialization and shutdown |
| 95 | + |
| 96 | +**API:** |
| 97 | +```typescript |
| 98 | +interface Runtime { |
| 99 | + pluginManager: PluginManager; |
| 100 | + init(): Promise<void>; |
| 101 | + query(object, query, context): Promise<any[]>; |
| 102 | + shutdown(): Promise<void>; |
| 103 | + setQueryExecutor(executor): void; |
| 104 | +} |
| 105 | +``` |
| 106 | + |
| 107 | +## Usage Pattern |
| 108 | + |
| 109 | +```typescript |
| 110 | +// 1. Define plugins |
| 111 | +const myPlugin: BasePlugin = { |
| 112 | + metadata: { |
| 113 | + name: 'my-plugin', |
| 114 | + dependencies: ['base-plugin'] |
| 115 | + }, |
| 116 | + async setup(runtime) { |
| 117 | + // Initialize plugin |
| 118 | + } |
| 119 | +}; |
| 120 | + |
| 121 | +// 2. Create runtime |
| 122 | +const runtime = createRuntime({ |
| 123 | + plugins: [myPlugin] |
| 124 | +}); |
| 125 | + |
| 126 | +// 3. Set executor |
| 127 | +runtime.setQueryExecutor(async (object, query) => { |
| 128 | + // Execute query against database |
| 129 | +}); |
| 130 | + |
| 131 | +// 4. Initialize |
| 132 | +await runtime.init(); |
| 133 | + |
| 134 | +// 5. Execute queries |
| 135 | +const results = await runtime.query('project', { |
| 136 | + filters: [['status', '=', 'active']] |
| 137 | +}); |
| 138 | + |
| 139 | +// 6. Shutdown |
| 140 | +await runtime.shutdown(); |
| 141 | +``` |
| 142 | + |
| 143 | +## Design Decisions |
| 144 | + |
| 145 | +### 1. Separation of Concerns |
| 146 | +- **Types** define interfaces (what) |
| 147 | +- **Runtime** implements logic (how) |
| 148 | +- No circular dependencies between packages |
| 149 | + |
| 150 | +### 2. Topological Sort for Dependencies |
| 151 | +- Ensures correct initialization order |
| 152 | +- Detects circular dependencies early |
| 153 | +- Provides clear error messages |
| 154 | + |
| 155 | +### 3. Async Series Waterfall |
| 156 | +- Allows plugins to transform data sequentially |
| 157 | +- Each plugin sees previous plugin's changes |
| 158 | +- Enables powerful composition patterns |
| 159 | + |
| 160 | +### 4. Error Handling |
| 161 | +- Custom error types (PluginError, PipelineError) |
| 162 | +- Include plugin name in errors for debugging |
| 163 | +- Graceful shutdown even if teardown fails |
| 164 | + |
| 165 | +## Testing |
| 166 | + |
| 167 | +The package includes 39 tests covering: |
| 168 | +- Plugin registration and lifecycle |
| 169 | +- Dependency resolution (simple, complex, diamond, circular) |
| 170 | +- Query pipeline execution (validation, waterfall, errors) |
| 171 | +- Integration scenarios |
| 172 | + |
| 173 | +Run tests: |
| 174 | +```bash |
| 175 | +pnpm test |
| 176 | +``` |
| 177 | + |
| 178 | +## Future Enhancements |
| 179 | + |
| 180 | +Potential improvements: |
| 181 | +1. Plugin versioning and compatibility checking |
| 182 | +2. Hot plugin reload |
| 183 | +3. Plugin communication via events |
| 184 | +4. Performance monitoring hooks |
| 185 | +5. Plugin sandboxing for security |
0 commit comments