command-registry-pattern.md

Guide to CommandRegistry for dynamic command loading, metadata-driven composition, and AI-collaborative workflows

command-registry-pattern.mdv1.0.015.4 KB
command-registry-pattern.md(markdown)
1# CommandRegistry Pattern Guide
2
3## Overview
4
5The CommandRegistry is a central discovery and orchestration service in the micro-block architecture pattern. It provides dynamic command loading, automatic dependency injection, and metadata-driven workflow composition capabilities for AI-collaborative development.
6
7**Key Capabilities:**
8- Dynamic command discovery and loading
9- Automatic service and command dependency injection
10- Contract-based workflow composition
11- Metadata-driven AI orchestration
12- Lazy loading with performance optimization
13
14## Core Architecture
15
16### Registry Design
17
18The CommandRegistry follows a singleton pattern with lazy loading capabilities:
19
20```typescript
21interface CommandRegistry extends BaseService {
22  // Primary access methods
23  get<T extends BaseCommand>(commandClass: CommandConstructor, input?: any, logger?: ICommandLogger): Promise<T>;
24  createCommandByName<T extends BaseCommand>(fullCommandName: string, input?: any, logger?: ICommandLogger): Promise<T>;
25  
26  // Discovery methods
27  findByCategory(category: string): CommandMetadata[];
28  findByDependency(serviceName: string): CommandMetadata[];
29  findByDataFlow(inputType?: string, outputType?: string): CommandMetadata[];
30  
31  // Workflow composition
32  findNextCommands(producingCommandName: string): CommandMetadata[];
33  findPreviousCommands(consumingCommandName: string): CommandMetadata[];
34  findAlternativeCommands(commandName: string): CommandMetadata[];
35  findWorkflowChains(startContract: string, endContract: string): WorkflowChain[];
36}
37```
38
39### Command Registration Structure
40
41Commands are registered using the `CommandRegistration` interface:
42
43```typescript
44interface CommandRegistration {
45  constructor: CommandConstructor;     // Command class constructor
46  metadata: CommandMetadata;           // Static metadata from command
47  registeredAt: Date;                  // Registration timestamp
48}
49```
50
51### Command Constructor Requirements
52
53All commands must implement the `CommandConstructor` interface:
54
55```typescript
56interface CommandConstructor {
57  new(input?: any, logger?: ICommandLogger, services?: Record<string, any>): BaseCommand;
58  readonly metadata: CommandMetadata;  // Static metadata required
59}
60```
61
62## Access Patterns
63
64### Pattern 1: Direct Command Access (Recommended)
65
66Use `get()` method with command class for type-safe access:
67
68```typescript
69const serviceRegistry = ServiceRegistry.getInstance();
70const commandRegistry = serviceRegistry.getCommandRegistry();
71
72// Type-safe command creation
73const command = await commandRegistry.get(CreateUserCommand, {
74  email: "user@example.com",
75  password: "securepassword",
76  name: "John Doe"
77});
78
79const result = await command.execute();
80```
81
82### Pattern 2: Dynamic Command Loading
83
84Use `createCommandByName()` for runtime command discovery:
85
86```typescript
87const commandRegistry = serviceRegistry.getCommandRegistry();
88
89// Dynamic command loading by name
90const command = await commandRegistry.createCommandByName<CreateUserCommand>(
91  "user/CreateUserCommand",
92  { email: "user@example.com", password: "pass", name: "John" }
93);
94
95const result = await command.execute();
96```
97
98### Pattern 3: Service Integration
99
100CommandRegistry integrates with ServiceRegistry for dependency injection:
101
102```typescript
103// In ServiceRegistry initialization
104const commandRegistry = CommandRegistry.getInstance();
105commandRegistry.setServiceResolver((serviceName: string) => {
106  return serviceRegistry.get(serviceName);
107});
108```
109
110## Dependency Injection System
111
112### Service Dependencies
113
114Commands declare service dependencies in metadata:
115
116```typescript
117export class CreateUserCommand {
118  static readonly metadata: CommandMetadata = {
119    name: 'CreateUserCommand',
120    dependencies: {
121      services: ['IDatabaseService', 'IEmailService'],
122      commands: [],
123      external: ['bcryptjs']
124    }
125  };
126  
127  constructor(
128    public input?: CreateUserInput,
129    private logger?: ICommandLogger,
130    private services?: Record<string, any>  // Services injected here
131  ) {
132    // Access injected services
133    this.databaseService = services?.['IDatabaseService'];
134    this.emailService = services?.['IEmailService'];
135  }
136}
137```
138
139### Command Dependencies
140
141Commands can depend on other commands for composition:
142
143```typescript
144export class UserRegistrationWorkflow {
145  static readonly metadata: CommandMetadata = {
146    name: 'UserRegistrationWorkflow',
147    dependencies: {
148      services: ['IDatabaseService'],
149      commands: ['user/CreateUserCommand', 'email/SendWelcomeEmailCommand']
150    }
151  };
152  
153  constructor(
154    public input?: WorkflowInput,
155    private logger?: ICommandLogger,
156    private services?: Record<string, any>,
157    private commands?: Record<string, any>  // Commands injected here
158  ) {
159    this.createUserCommand = commands?.['user/CreateUserCommand'];
160    this.sendEmailCommand = commands?.['email/SendWelcomeEmailCommand'];
161  }
162}
163```
164
165### Dependency Resolution Rules
166
1671. **Service Dependencies**: Resolved through ServiceRegistry
1682. **Command Dependencies**: Lazily loaded and instantiated
1693. **Circular Dependency Protection**: Maximum depth of 10 levels
1704. **Failure Handling**: Dependency resolution failures throw descriptive errors
171
172## Discovery Methods
173
174### Category-Based Discovery
175
176Find commands by functional category:
177
178```typescript
179const commandRegistry = serviceRegistry.getCommandRegistry();
180
181// Find all user-related commands
182const userCommands = commandRegistry.findByCategory('user');
183console.log(userCommands.map(cmd => cmd.name));
184// ['CreateUserCommand', 'UpdateUserCommand', 'DeleteUserCommand']
185```
186
187### Dependency-Based Discovery
188
189Find commands that depend on specific services:
190
191```typescript
192// Find commands that use the database service
193const databaseCommands = commandRegistry.findByDependency('IDatabaseService');
194
195// Impact analysis: which commands are affected by service changes?
196const affectedCommands = commandRegistry.findByDependency('IEmailService');
197```
198
199### Data Flow Discovery
200
201Find commands by input/output types:
202
203```typescript
204// Find commands that produce UserOutput
205const userProducers = commandRegistry.findByDataFlow(undefined, 'UserOutput');
206
207// Find commands that consume UserInput
208const userConsumers = commandRegistry.findByDataFlow('UserInput', undefined);
209
210// Find exact input/output match
211const exactMatch = commandRegistry.findByDataFlow('UserInput', 'UserOutput');
212```
213
214## Workflow Composition
215
216### Contract-Based Chaining
217
218The registry enables automatic workflow construction through contract matching:
219
220```typescript
221// Find commands that can consume CreateUserCommand output
222const nextCommands = commandRegistry.findNextCommands('CreateUserCommand');
223
224// Find commands that can provide input to SendEmailCommand
225const previousCommands = commandRegistry.findPreviousCommands('SendEmailCommand');
226
227// Find alternative implementations with same contracts
228const alternatives = commandRegistry.findAlternativeCommands('CreateUserCommand');
229```
230
231### Workflow Chain Discovery
232
233Build complete workflows from start to end contracts:
234
235```typescript
236// Find all possible workflows from UserInput to EmailSentOutput
237const workflows = commandRegistry.findWorkflowChains('UserInput', 'EmailSentOutput');
238
239workflows.forEach(workflow => {
240  console.log(`Workflow (${workflow.complexity} steps):`);
241  workflow.commands.forEach(cmd => console.log(`  - ${cmd.name}`));
242  console.log(`Estimated duration: ${workflow.estimatedDuration}ms`);
243});
244```
245
246### Contract Analysis
247
248Get comprehensive contract analysis:
249
250```typescript
251const analysis = commandRegistry.getContractAnalysis();
252
253console.log('Registry Analysis:');
254console.log(`Total Commands: ${analysis.totalCommands}`);
255console.log(`Total Contracts: ${analysis.totalContracts}`);
256console.log(`Orphaned Contracts: ${analysis.orphanedContracts}`);
257console.log(`Fully Connected: ${analysis.fullyConnectedContracts}`);
258```
259
260## Command Loading Strategies
261
262### Lazy Loading
263
264Commands are loaded on-demand to optimize startup performance:
265
266```typescript
267// Command modules are imported only when first requested
268const command = await commandRegistry.get(SomeCommand, input);
269// This triggers: import(`../../commands/${category}/${commandName}`)
270```
271
272### Registration vs Dynamic Loading
273
274**Explicit Registration (Optional):**
275```typescript
276commandRegistry.registerCommand(CreateUserCommand);
277```
278
279**Dynamic Loading (Automatic):**
280```typescript
281// No registration needed - commands are discovered by file structure
282const command = await commandRegistry.get(CreateUserCommand, input);
283```
284
285### File Structure Requirements
286
287Commands must follow the expected file structure:
288
289```
290src/commands/
291├── user/
292│   ├── CreateUserCommand.ts
293│   ├── UpdateUserCommand.ts
294│   └── DeleteUserCommand.ts
295├── email/
296│   ├── SendEmailCommand.ts
297│   └── SendBulkEmailCommand.ts
298└── cache/
299    ├── SetCacheCommand.ts
300    └── GetCacheCommand.ts
301```
302
303## Error Handling
304
305### Dependency Resolution Errors
306
307```typescript
308try {
309  const command = await commandRegistry.get(CreateUserCommand, input);
310} catch (error) {
311  // Handles:
312  // - Missing service dependencies
313  // - Circular command dependencies
314  // - Command not found
315  // - Invalid metadata
316}
317```
318
319### Command Creation Errors
320
321```typescript
322try {
323  const command = await commandRegistry.createCommandByName(
324    "user/CreateUserCommand", 
325    input
326  );
327} catch (error) {
328  // Handles:
329  // - Invalid command name format
330  // - Module not found
331  // - Constructor not found
332  // - Metadata validation errors
333}
334```
335
336## Best Practices
337
338### 1. Command Metadata
339
340Always provide comprehensive metadata:
341
342```typescript
343export class ExampleCommand {
344  static readonly metadata: CommandMetadata = {
345    name: 'ExampleCommand',
346    description: 'Clear, specific description of what this command does',
347    category: 'domain-area',
348    inputType: 'ExampleInput',
349    outputType: 'ExampleOutput',
350    errorType: 'ExampleError',
351    version: '1.0.0',
352    contractVersion: '1.0',
353    dependencies: {
354      services: ['IRequiredService'],
355      commands: ['category/DependentCommand'],
356      external: ['external-package']
357    },
358    dataFlow: {
359      inputs: ['field1', 'field2'],
360      outputs: ['result1', 'result2'],
361      sideEffects: ['database-write', 'email-send']
362    },
363    performance: {
364      expectedDuration: '100ms',
365      scaling: 'linear'
366    }
367  };
368}
369```
370
371### 2. Dependency Declaration
372
373Use specific service interface names:
374
375```typescript
376// ✅ CORRECT - Interface references
377dependencies: {
378  services: ['IDatabaseService', 'ICacheService', 'IEmailService']
379}
380
381// ❌ INCORRECT - Concrete class or technology references
382dependencies: {
383  services: ['DatabaseService', 'SQL Server', 'Redis Cache']
384}
385```
386
387### 3. Registry Access
388
389Always access through ServiceRegistry:
390
391```typescript
392// ✅ CORRECT - Access through ServiceRegistry
393const serviceRegistry = ServiceRegistry.getInstance();
394const commandRegistry = serviceRegistry.getCommandRegistry();
395
396// ❌ INCORRECT - Direct access bypasses ServiceRegistry
397const commandRegistry = CommandRegistry.getInstance();
398```
399
400### 4. Error Handling
401
402Implement comprehensive error handling:
403
404```typescript
405try {
406  const command = await commandRegistry.get(CreateUserCommand, input);
407  const result = await command.execute();
408  return result;
409} catch (error) {
410  logger.error('Command execution failed', error);
411  throw new ServiceError('User creation failed', 'COMMAND_ERROR', { 
412    commandName: 'CreateUserCommand',
413    originalError: error 
414  });
415}
416```
417
418### 5. Performance Optimization
419
420Use appropriate loading strategies:
421
422```typescript
423// For known commands - type-safe and efficient
424const command = await commandRegistry.get(CreateUserCommand, input);
425
426// For dynamic discovery - flexible but less type-safe
427const command = await commandRegistry.createCommandByName(
428  "user/CreateUserCommand", 
429  input
430);
431```
432
433## AI Integration Patterns
434
435### Command Discovery
436
437AI agents can use metadata for intelligent command selection:
438
439```typescript
440function findOptimalCommand(requirements: CommandRequirements): CommandMetadata {
441  const candidates = commandRegistry.findByCategory(requirements.category);
442  
443  // AI analyzes metadata to select best command
444  return candidates.find(cmd => 
445    cmd.performance?.expectedDuration === requirements.performanceLevel &&
446    cmd.dependencies?.services?.every(service => 
447      serviceRegistry.isAvailable(service)
448    )
449  );
450}
451```
452
453### Workflow Orchestration
454
455AI can automatically compose workflows:
456
457```typescript
458function buildOptimalWorkflow(startContract: string, endContract: string): WorkflowChain {
459  const workflows = commandRegistry.findWorkflowChains(startContract, endContract);
460  
461  // AI selects optimal workflow based on complexity, duration, and reliability
462  return workflows.sort((a, b) => {
463    const aScore = calculateWorkflowScore(a);
464    const bScore = calculateWorkflowScore(b);
465    return bScore - aScore;
466  })[0];
467}
468```
469
470### Dynamic Adaptation
471
472AI can substitute commands based on runtime conditions:
473
474```typescript
475async function executeWithFallback(primaryCommand: string, input: any): Promise<any> {
476  const alternatives = commandRegistry.findAlternativeCommands(primaryCommand);
477  
478  for (const alternative of [primaryCommand, ...alternatives]) {
479    try {
480      const command = await commandRegistry.createCommandByName(alternative, input);
481      return await command.execute();
482    } catch (error) {
483      logger.warn(`Command ${alternative} failed, trying alternative`);
484    }
485  }
486  
487  throw new Error('All command alternatives failed');
488}
489```
490
491## Advanced Features
492
493### Contract Validation
494
495The registry provides contract validation capabilities:
496
497```typescript
498// Validate workflow compatibility
499const isCompatible = commandRegistry.validateWorkflowChain([
500  'user/CreateUserCommand',
501  'email/SendWelcomeEmailCommand'
502]);
503
504// Check contract completeness
505const analysis = commandRegistry.getContractAnalysis();
506const orphanedContracts = analysis.orphanedContracts;
507```
508
509### Performance Monitoring
510
511Track command execution metrics:
512
513```typescript
514const command = await commandRegistry.get(CreateUserCommand, input);
515const startTime = Date.now();
516
517try {
518  const result = await command.execute();
519  const duration = Date.now() - startTime;
520  
521  // Log performance metrics
522  logger.info(`Command executed successfully`, {
523    commandName: command.getMetadata().name,
524    duration,
525    expectedDuration: command.getMetadata().performance?.expectedDuration
526  });
527  
528  return result;
529} catch (error) {
530  logger.error(`Command failed after ${Date.now() - startTime}ms`, error);
531  throw error;
532}
533```
534
535## Integration with UCM
536
537When uploaded to UCM, the CommandRegistry becomes a discoverable service that other projects can use:
538
539```typescript
540// UCM Service Metadata
541const ucmMetadata: ServiceMetadata = {
542  name: 'CommandRegistry',
543  displayName: 'Command Registry Service',
544  description: 'Central registry for discovering and instantiating commands with automatic dependency injection',
545  contract: 'CommandRegistry',
546  version: '1.0.0',
547  contractVersion: '1.0',
548  features: [
549    'Dynamic command discovery',
550    'Automatic dependency injection',
551    'Contract-based workflow composition',
552    'Metadata-driven AI orchestration'
553  ],
554  limitations: [
555    'Requires consistent file structure',
556    'Depends on ServiceRegistry for dependency resolution'
557  ],
558  requirements: ['ServiceRegistry', 'BaseService', 'ILogger'],
559  recommendations: ['Micro-block architecture projects', 'AI-collaborative development']
560};
561```
562
563This pattern enables AI agents to understand and compose functionality automatically through rich metadata and explicit contracts, making it a foundational component for AI-collaborative development.

Metadata

Path
utaba/main/patterns/micro-block/command-registry-pattern.md
Namespace
utaba/main/patterns/micro-block
Author
utaba
Category
patterns
Technology
typescript
Contract Version
1.0.0
MIME Type
text/markdown
Published
18-Jul-2025
Last Updated
18-Jul-2025