micro-block-pattern-summary.md
Summary of the micro-block architecture pattern for AI-native development
micro-block-pattern-summary.mdv1.0.09.8 KB
micro-block-pattern-summary.md(markdown)
1# Micro-block Architecture Summary
2
3*Essential patterns for AI developers working on this codebase*
4
5**Reference**: For complete details see [`utaba/main/patterns/micro-block/micro-block-pattern.md`](utaba/main/patterns/micro-block/micro-block-pattern.md)
6
7## Core Concept
8
9Business logic is implemented as **Commands** - self-contained units that declare their contracts and dependencies explicitly. Commands are discovered and instantiated via registries, enabling automatic dependency injection and composition.
10
11## Command Structure Template
12
13```typescript
14// All types in same file
15export interface MyCommandInput { /* explicit input contract */ }
16export interface MyCommandOutput { /* explicit output contract */ }
17export class MyCommandError extends BaseError { /* custom errors */ }
18
19export class MyCommand extends OutputCommand<MyCommandInput, MyCommandOutput, MyCommandError> {
20 // Service dependencies (injected via constructor)
21 private dbService?: IDatabaseService;
22
23 // Static metadata for discovery
24 static readonly metadata: CommandMetadata = {
25 name: "MyCommand",
26 description: "Brief description of what this command does",
27 category: "folder-name", // MUST match parent folder exactly
28 inputType: "MyCommandInput",
29 outputType: "MyCommandOutput",
30 errorType: "MyCommandError",
31 version: "1.0.0",
32 contractVersion: "1.0.0",
33 dependencies: {
34 services: ["IDatabaseService", "ICacheService"],
35 commands: ["category/SomeOtherCommand"],
36 external: ["youtube-api"]
37 }
38 };
39
40 constructor(
41 input?: MyCommandInput,
42 private logger?: any,
43 private services?: Record<string, any>,
44 private commands?: Record<string, any>
45 ) {
46 super();
47 if (input) {
48 this.setInput(input);
49 }
50
51 // Services injected via constructor based on metadata
52 this.dbService = services?.['IDatabaseService'];
53 if (!this.dbService) {
54 throw this.raiseError(new MyCommandError('IDatabaseService required', 'MISSING_SERVICE'));
55 }
56 }
57
58 validate(): void { /* input validation - throws errors */ }
59 async execute(): Promise<MyCommandOutput> { /* business logic */ }
60 getMetadata(): CommandMetadata { return MyCommand.metadata; }
61}
62```
63
64## Critical Rules
65
66### 1. Never Direct Instantiation
67```typescript
68// ❌ WRONG
69const command = new CreateUserCommand(input);
70
71// ✅ CORRECT
72const command = await commandRegistry.get<CreateUserCommand>(
73 CreateUserCommand, input, logger
74);
75```
76
77### 2. Category = Folder Name
78```
79src/commands/user/CreateUserCommand.ts → category: "user"
80src/commands/cache/SetCacheCommand.ts → category: "cache"
81```
82
83### 3. Required Metadata Fields
84```typescript
85static readonly metadata: CommandMetadata = {
86 name: "CommandName", // Exact class name
87 description: "What it does", // Brief description
88 category: "folder-name", // MUST match parent folder
89 inputType: "InputInterface", // Input contract type
90 outputType: "OutputInterface", // Output contract type
91 errorType: "ErrorClass", // Error class type
92 version: "1.0.0", // Implementation version
93 contractVersion: "1.0.0", // API contract version
94 dependencies: {
95 services: ["IServiceName"],
96 commands: ["category/CommandName"],
97 external: ["package-name"]
98 }
99};
100```
101
102### 4. Constructor Pattern
103```typescript
104constructor(
105 input?: InputType, // Optional input (use setInput later)
106 private logger?: any, // Optional logger injection
107 private services?: Record<string, any>, // Services injected based on metadata
108 private commands?: Record<string, any> // Commands injected based on metadata
109) {
110 super(); // Call parent constructor
111 if (input) {
112 this.setInput(input); // Set input using inherited method
113 }
114
115 // Validate injected dependencies
116 this.myService = services?.['IMyService'];
117 if (!this.myService) {
118 throw new MyCommandError('IMyService required', 'MISSING_SERVICE');
119 }
120}
121```
122
123### 5. Dependency Injection - Not Resolution
124```typescript
125// ✅ CORRECT - Dependencies injected via constructor
126this.dbService = services?.['IDatabaseService'];
127this.otherCommand = commands?.['category/OtherCommand'];
128
129// ❌ WRONG - Commands should NOT call ServiceRegistry directly
130const serviceRegistry = ServiceRegistry.getInstance();
131this.dbService = serviceRegistry.get<IDatabaseService>('IDatabaseService');
132```
133
134## Presentation Layer Integration
135
136Presentation layers (API routes, UI components) should be **lightweight orchestration layers** that delegate business logic to Services and Commands:
137
138```typescript
139// ✅ Service-First - Route → Service → Commands
140export async function handleUserRegistration(requestData: any): Promise<any> {
141 const securityService = serviceRegistry.get<ISecurityService>('ISecurityService');
142 const result = await securityService.registerUser(requestData, baseUrl, context);
143 return { success: true, message: result.message, user: result.user };
144}
145
146// ✅ Command-First - Route → Commands directly
147export async function handleWorkflow(requestData: any): Promise<any> {
148 const serviceRegistry = ServiceRegistry.getInstance();
149 const commandRegistry = serviceRegistry.getCommandRegistry();
150 const command = await commandRegistry.get<WorkflowCommand>(WorkflowCommand, requestData);
151 return { success: true, data: await command.execute() };
152}
153```
154
155**Use Service-First for:** Cross-cutting concerns, service-level coordination
156**Use Command-First for:** Workflows, isolated operations, direct business logic
157
158## Service Portability
159
160**CRITICAL: Services must be portable** - Never depend on project-specific configuration classes. Use service-specific config interfaces:
161
162```typescript
163// Service-specific configuration interface
164export interface MyServiceConfig {
165 connectionString: string;
166 timeout: number;
167 retryAttempts: number;
168}
169
170// Portable service implementation
171export class MyService extends BaseService implements IMyService {
172 constructor(
173 private config: MyServiceConfig, // Own config interface
174 private logger: ILogger, // Interface dependency
175 private otherService?: IOtherService
176 ) {
177 super();
178
179 // Validate required dependencies after super()
180 if (!this.config) {
181 throw new Error('MyServiceConfig is required for MyService');
182 }
183 if (!this.logger) {
184 throw new Error('ILogger is required for MyService');
185 }
186 // otherService is optional, no validation needed
187 }
188
189 // Implementation uses this.config, never project-specific config
190}
191```
192
193**ServiceRegistry** maps project config → service config interfaces, enabling portability.
194
195
196## Registry Access Patterns
197
198**Services accessing Commands:** Receive CommandRegistry via constructor injection:
199```typescript
200export class MonitorService extends BaseService {
201 constructor(
202 private config: MonitorServiceConfig,
203 private logger: ILogger,
204 private commandRegistry: CommandRegistry // ✅ Injected by ServiceRegistry
205 ) {
206 super();
207
208 // Validate required dependencies
209 if (!this.commandRegistry) {
210 throw new Error('CommandRegistry is required for MonitorService');
211 }
212 }
213
214 private async performAnalysis(): Promise<any> {
215 // ✅ CORRECT: Use injected CommandRegistry
216 const command = await this.commandRegistry.get<AnalysisCommand>(AnalysisCommand, input);
217 return await command.execute();
218 }
219}
220```
221
222**Commands accessing other Commands:** Use dependency injection via constructor:
223```typescript
224static readonly metadata: CommandMetadata = {
225 dependencies: {
226 commands: ['user/CreateUserCommand', 'email/SendEmailCommand']
227 }
228};
229
230constructor(input?: any, logger?: any, services?: any, commands?: any) {
231 // ✅ Commands injected via constructor
232 this.createUserCommand = commands?.['user/CreateUserCommand'];
233 this.sendEmailCommand = commands?.['email/SendEmailCommand'];
234}
235```
236
237### ❌ NEVER DO THIS:
238```typescript
239// WRONG: Commands should not call ServiceRegistry directly
240const serviceRegistry = ServiceRegistry.getInstance();
241this.dbService = serviceRegistry.get<IDatabaseService>('IDatabaseService');
242
243// WRONG: Direct command instantiation
244const command = new SomeCommand();
245```
246
247
248## Key Benefits for AI Development
249
2501. **Granular Focus**: Each command has single responsibility
2512. **Explicit Contracts**: Input/Output types make requirements clear
2523. **Dependency Clarity**: Metadata declares exactly what's needed
2534. **Composition Over Inheritance**: Build complex workflows from simple parts
2545. **Testability**: Mock dependencies easily via constructor injection
2556. **Discoverability**: Registry patterns enable automatic discovery
256
257## Common Patterns
258
259### Error Handling
260```typescript
261export class MyCommandError extends BaseError {
262 constructor(message: string, code?: string, details?: Record<string, any>) {
263 super(message, code, details);
264 }
265}
266```
267
268### Validation Pattern
269```typescript
270validate(): void {
271 if (!this.input) {
272 const error = new MyCommandError('Command input cannot be null', 'VALIDATION_ERROR');
273 error.setValidationError('input', 'input');
274 throw this.raiseError(error);
275 }
276
277 if (!this.input.requiredField) {
278 const error = new MyCommandError('Required field missing', 'VALIDATION_ERROR');
279 error.setValidationError('requiredField', 'input.requiredField');
280 throw this.raiseError(error);
281 }
282}
283```
284
285## Architecture Principles
286
287- **Self-Describing**: Metadata makes components discoverable
288- **Loosely Coupled**: Components interact only through contracts
289- **Composable**: Commands can orchestrate other commands via dependency injection
290- **Portable**: Commands work across different systems
291- **Cacheable**: Registry enables lazy loading and reuse
292- **AI-Friendly**: Granular, focused responsibilities
293
294*Source: [micro-block-pattern-summary.md](https://ucm.utaba.ai/browse/utaba/main/patterns/micro-block/micro-block-pattern-summary.md)*
Metadata
- Path
- utaba/main/patterns/micro-block/micro-block-pattern-summary.md
- Namespace
- utaba/main/patterns/micro-block
- Author
- utaba
- Category
- patterns
- Technology
- markdown
- Contract Version
- 1.0.0
- MIME Type
- text/markdown
- Published
- 23-Jul-2025
- Last Updated
- 23-Jul-2025