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