micro-block-quick-reference.md

Ultra-concise quick reference for micro-block patterns with corrected workflow composition showing proper command dependency injection

micro-block-quick-reference.mdv1.1.06.3 KB
micro-block-quick-reference.md(markdown)
1# Micro-Block Quick Reference Card
2
3*Essential patterns for AI developers - minimal context version*
4
5## ๐Ÿ—๏ธ Command Template
6
7```typescript
8export interface MyInput { field: string; }
9export interface MyOutput { result: string; }
10export class MyError extends BaseError {}
11
12export class MyCommand extends OutputCommand<MyInput, MyOutput, MyError> {
13  private dbService: IDatabaseService;
14  
15  static readonly metadata: CommandMetadata = {
16    name: "MyCommand",
17    category: "folder-name",        // MUST match parent folder
18    inputType: "MyInput",
19    outputType: "MyOutput", 
20    errorType: "MyError",
21    version: "1.0.0",
22    contractVersion: "1.0.0",
23    dependencies: { 
24      services: ["IDatabaseService", "ICacheService"],
25      commands: ["user/CreateUserCommand"],
26      external: ["bcryptjs", "uuid"]
27    }
28  };
29
30  constructor(
31    input?: MyInput, 
32    private logger?: any,
33    private services?: Record<string, any>,
34    private commands?: Record<string, any>
35  ) {
36    super();
37    if (input) this.setInput(input);
38    
39    // Services injected via constructor based on metadata
40    this.dbService = services?.['IDatabaseService'];
41    if (!this.dbService) {
42      throw new MyError('IDatabaseService required', 'MISSING_SERVICE');
43    }
44  }
45
46  validate(): void { /* throw errors */ }
47  async execute(): Promise<MyOutput> { /* business logic */ }
48  getMetadata() { return MyCommand.metadata; }
49}
50```
51
52## ๐Ÿšจ Critical Rules
53
54### Command Access
55```typescript
56// โŒ NEVER
57const cmd = new MyCommand();
58
59// โœ… ALWAYS  
60const serviceRegistry = ServiceRegistry.getInstance();
61const commandRegistry = serviceRegistry.getCommandRegistry();
62const cmd = await commandRegistry.get(MyCommand, input);
63```
64
65### Service Dependencies
66```typescript
67// โœ… CORRECT: Services injected via constructor based on metadata
68constructor(
69  input?: MyInput,
70  logger?: any,
71  private services?: Record<string, any>  // Services injected here
72) {
73  this.dbService = services?.['IDatabaseService'];
74  this.cacheService = services?.['ICacheService'];
75  
76  // Validate required services
77  if (!this.dbService) {
78    throw new MyError('IDatabaseService required', 'MISSING_SERVICE');
79  }
80}
81
82// โŒ WRONG: Commands should NOT directly call ServiceRegistry
83const serviceRegistry = ServiceRegistry.getInstance();
84this.dbService = serviceRegistry.get<IDatabaseService>('IDatabaseService');
85```
86
87### Metadata Requirements
88- `category` = parent folder name exactly
89- Declare ALL dependencies in `metadata.dependencies`
90- Use interface names: `"IDatabaseService"` not `"DatabaseService"`
91- Services auto-injected via constructor based on declared dependencies
92- Commands get other commands via `commands` parameter
93
94## ๐Ÿ”ง Service Pattern
95
96```typescript
97// Service config interface
98export interface MyServiceConfig {
99  connectionString: string;
100  timeout: number;
101}
102
103// Service implementation
104export class MyService extends BaseService implements IMyService {
105  constructor(
106    private config: MyServiceConfig,  // Own config interface
107    private logger: ILogger
108  ) { super(); }
109  
110  getMetadata(): ServiceMetadata {
111    return {
112      name: 'MyService',
113      contract: 'IMyService',
114      version: '1.0.0',
115      features: ['Feature1', 'Feature2'],
116      limitations: ['Limitation1'],
117      recommendations: ['Use case 1', 'Use case 2'],
118      dependencies: {
119        services: ['ILogger', 'IMetricsService']
120      }
121    };
122  }
123}
124```
125
126## ๐Ÿ“ Registry Registration
127
128```typescript
129// In ServiceRegistry.initialize()
130this.register('IMyService', () => {
131  const serviceConfig: MyServiceConfig = {
132    connectionString: this.config.database.connectionString,
133    timeout: this.config.database.timeout
134  };
135  return new MyService(serviceConfig, this.getLogger('MyService'));
136});
137```
138
139## โšก Workflow Composition
140
141```typescript
142export class WorkflowCommand extends OutputCommand<WorkflowInput, WorkflowOutput, WorkflowError> {
143  private step1Command: Step1Command;
144  private step2Command: Step2Command;
145  
146  static readonly metadata: CommandMetadata = {
147    name: "WorkflowCommand",
148    category: "workflow",
149    inputType: "WorkflowInput",
150    outputType: "WorkflowOutput",
151    errorType: "WorkflowError",
152    version: "1.0.0",
153    contractVersion: "1.0.0",
154    dependencies: { 
155      services: ["ILogger"],
156      commands: ["user/Step1Command", "user/Step2Command"],  // Define command dependencies
157      external: []
158    }
159  };
160
161  constructor(
162    input?: WorkflowInput,
163    private logger?: any,
164    private services?: Record<string, any>,
165    private commands?: Record<string, any>  // Commands injected here
166  ) {
167    super();
168    if (input) this.setInput(input);
169    
170    // Verify injected commands
171    this.step1Command = commands?.['user/Step1Command'];
172    this.step2Command = commands?.['user/Step2Command'];
173    
174    if (!this.step1Command || !this.step2Command) {
175      throw new WorkflowError('Required commands not injected', 'MISSING_COMMANDS');
176    }
177  }
178
179  async execute(): Promise<WorkflowOutput> {
180    // Use injected commands - they're already instantiated and ready
181    const result1 = await this.step1Command.execute();
182    const result2 = await this.step2Command.execute();
183    
184    return { combinedResult: result1.data + result2.data };
185  }
186}
187```
188
189## ๐Ÿงช Testing Pattern
190
191```typescript
192describe('MyCommand', () => {
193  beforeEach(() => {
194    // Mock ServiceRegistry if needed
195    vi.mock('@/core/registry/ServiceRegistry', () => ({
196      ServiceRegistry: {
197        getInstance: () => ({ get: vi.fn().mockReturnValue(mockService) })
198      }
199    }));
200  });
201
202  it('validates input', () => {
203    const cmd = new MyCommand(validInput);
204    expect(() => cmd.validate()).not.toThrow();
205  });
206});
207```
208
209## ๐ŸŽฏ Key Principles
210
211- **Commands**: Single responsibility, explicit contracts
212- **Services**: Portable, own config interfaces
213- **Registry**: Single access point via ServiceRegistry
214- **Validation**: Throw errors with details
215- **Metadata**: Rich, discoverable information
216- **Composition**: Commands orchestrate other commands via dependency injection
217
218## โŒ Common Mistakes
219
220- Direct command instantiation: `new MyCommand()`
221- Commands calling ServiceRegistry directly
222- Commands requesting other commands from registry during execution
223- Missing category/folder alignment  
224- Concrete service references in dependencies
225- Missing metadata dependency declarations
226- Project-specific config in services

Metadata

Path
utaba/main/patterns/micro-block/micro-block-quick-reference.md
Namespace
utaba/main/patterns/micro-block
Author
utaba
Category
patterns
Technology
design-pattern
Contract Version
1.1.0
MIME Type
text/markdown
Published
18-Jul-2025
Last Updated
18-Jul-2025