micro-block-pattern.md
Micro-block architecture pattern for AI-native development
micro-block-pattern.mdv3.0.134.7 KB
micro-block-pattern.md(markdown)
1# Micro-Block Design Pattern
2
3## Overview
4
5The Micro-Block Design Pattern is a command-based pattern designed for AI-collaborative development. It decomposes complex systems into small, discrete, contract-driven building blocks that can be discovered, composed, and substituted automatically by AI agents.
6
7This pattern enables intelligent software development where AI can understand, select, and compose functionality through rich metadata and explicit contracts.
8
9**Note**: While the examples in this document are written in TypeScript for clarity and precision, the design pattern concepts are language-agnostic and can be implemented in any object-oriented programming language that supports interfaces, classes, and dependency injection (Java, C#, Python, Go, Rust, etc.).
10
11## Core Principles
12
131. **Contract-First Design**: All commands implement explicit Input, Output, and Error contracts
142. **Self-Describing Components**: Commands include rich metadata for AI understanding
153. **Loose Coupling**: Commands are isolated and interact only through well-defined interfaces
164. **Dynamic Discovery**: Registry enables automatic command discovery and composition
175. **Substitutability**: Commands with identical contracts are interchangeable
186. **Hierarchical Composition**: Commands can include other commands, enabling complex workflow orchestration
197. **Self-Contained Modularity**: Each command is a portable, independent unit
208. **Service Abstraction**: Commands depend on service interfaces, not concrete implementations
219. **Lazy Loading**: Commands are loaded on-demand, services are registered at startup
22
23## Design Components
24
25### Component Architecture
26
27```
28┌──────────────────────────────────────────────────────────────────┐
29│ Micro-Block Ecosystem │
30├──────────────────────────────────────────────────────────────────┤
31│ │
32│ ┌───────────────────────────────────┐ │
33│ │ Registry Layer │ │
34│ │ │ │
35│ │ ┌─────────────────────────────┐ │ │
36│ │ │ ServiceRegistry │ │ │
37│ │ │ • Service Management │ │ │
38│ │ │ • Configuration Mapping │ │ │
39│ │ │ • Primary Access Point │ │ │
40│ │ └─────────────────────────────┘ │ │
41│ │ │ │ │
42│ │ ▼ │ │
43│ ┌─────────────────┐│ ┌────────────────────────────┐ │ │
44│ │ Command Layer ││ │ CommandRegistry │ │ │
45│ │ │◄──│ • Command Discovery │ │ │
46│ │ ┌─────────────┐ ││ │ • Lazy Loading │ │ │
47│ │ │ Command │ ││ │ • Metadata Analysis │ │ │
48│ │ │ │ ││ │ • Dependency Injection │ │ │
49│ │ │ • Metadata │ ││ └────────────────────────────┘ │ │
50│ │ │ • Contracts │ │└──────────────────────────────────┐ │
51│ │ │ • Validation│ │ │ │
52│ │ │ • Execute │ │ │ │
53│ │ └─────────────┘ │ ┌─────────────────────────────┐│ │
54│ │ │ │ │ Service Layer ││ │
55│ │ ▼ │ │ ││ │
56│ │ ┌─────────────┐ │ │ ┌─────────────────────────┐││ │
57│ │ │Error/Output │ │ │ │ Service Interface │││ │
58│ │ │ Contracts │ │ │ │ • Abstract Capabilities│││ │
59│ │ └─────────────┘ │ │ │ • Contract Definition │││ │
60│ └─────────────────┘ │ └─────────────────────────┘││ │
61│ │ │ ││ │
62│ │ ▼ ││ │
63│ │ ┌─────────────────────────┐││ │
64│ │ │ Service Implementation│││ │
65│ │ │ • Portable Design │││ │
66│ │ │ • Configuration Interface││ │
67│ │ │ • Rich Metadata │││ │
68│ │ └─────────────────────────┘││ │
69│ └──────────────────────────────┘│ │
70└──────────────────────────────────────────────────────────────────┘
71
72 Data Flow: Registry ► Commands ► Services
73 Discovery: Metadata-driven component selection and composition
74```
75
76### Core Components
77
78- **Commands**: Self-contained units of business logic with explicit contracts
79- **Service Interfaces**: Abstract definitions of capabilities (data access, external APIs, etc.)
80- **Service Implementations**: Concrete implementations of service interfaces with rich metadata
81- **Command Registry**: Discovery mechanism for finding and composing commands with lazy loading
82- **Service Registry**: Configuration mapping and service management with dependency injection
83
84## Command Structure & Contracts
85
86### Base Command Interface
87
88All commands must implement the BaseCommand interface with error-throwing validation:
89
90```typescript
91interface BaseCommand {
92 /**
93 * Validate that the command can execute with current configuration.
94 * Throws detailed validation errors instead of returning boolean.
95 */
96 validate(): void;
97
98 /** Execute the command's business logic */
99 execute(): Promise<any>;
100
101 /** Get the static metadata for this command */
102 getMetadata(): CommandMetadata;
103}
104```
105
106### Command Metadata Structure
107
108Commands must include comprehensive metadata for AI-driven discovery:
109
110```typescript
111interface CommandMetadata {
112 name: string; // Unique command identifier
113 description: string; // Human-readable description
114 category: string; // Grouping category (user, email, cache, etc.)
115 inputType: string; // Input contract type name
116 outputType: string; // Output contract type name
117 errorType: string; // Error contract type name
118 version: string; // Command implementation version
119 contractVersion: string; // Input/output contract version
120
121 dependencies?: {
122 services?: string[]; // Service interface dependencies
123 commands?: string[]; // Command dependencies for composition
124 external?: string[]; // External package dependencies
125 };
126
127 dataFlow?: {
128 inputs: string[]; // Input data fields
129 outputs: string[]; // Output data fields
130 sideEffects: string[]; // Side effects performed (database-write, email-send)
131 };
132
133 performance?: {
134 expectedDuration: string; // Expected execution time (fast, medium, slow)
135 scaling: string; // Performance scaling characteristics
136 };
137}
138```
139
140### Contract Patterns
141
142#### Input/Output/Error Contracts
143
144Each command defines three explicit contracts:
145
146```typescript
147// Input Contract - What the command needs
148interface CreateUserInput {
149 email: string;
150 password: string;
151 name: string;
152}
153
154// Output Contract - What the command produces
155interface CreateUserOutput {
156 userId: string;
157 email: string;
158 createdAt: Date;
159 requiresVerification: boolean;
160}
161
162// Error Contract - What can go wrong
163class CreateUserError extends BaseError {
164 constructor(message: string, code?: string, context?: Record<string, any>) {
165 super(message, code, context);
166 this.name = "CreateUserError";
167 }
168}
169```
170
171#### Self-Contained Command Implementation
172
173Commands are self-contained with all related types in the same module:
174
175```typescript
176export class CreateUserCommand implements OutputCommand<CreateUserInput, CreateUserOutput, CreateUserError> {
177 // Static metadata accessible before instantiation
178 static readonly metadata: CommandMetadata = {
179 name: 'CreateUserCommand',
180 description: 'Creates a new user account with email verification',
181 category: 'user',
182 inputType: 'CreateUserInput',
183 outputType: 'CreateUserOutput',
184 errorType: 'CreateUserError',
185 version: '1.2.0',
186 contractVersion: '1.0',
187 dependencies: {
188 services: ['IDatabaseService', 'IEmailService'],
189 commands: [],
190 external: ['bcryptjs']
191 },
192 dataFlow: {
193 inputs: ['email', 'password', 'name'],
194 outputs: ['userId', 'email', 'createdAt', 'requiresVerification'],
195 sideEffects: ['database-write', 'email-send']
196 },
197 performance: {
198 expectedDuration: 'medium',
199 scaling: 'linear'
200 }
201 };
202
203 constructor(
204 public input?: CreateUserInput,
205 private logger?: ICommandLogger,
206 private services?: Record<string, any>
207 ) {
208 // Dependency injection of services
209 this.databaseService = services?.['IDatabaseService'];
210 this.emailService = services?.['IEmailService'];
211 }
212
213 validate(): void {
214 // Error-throwing validation with detailed feedback
215 if (!this.input) {
216 const error = new CreateUserError(
217 'Command input cannot be null or undefined',
218 'VALIDATION_ERROR'
219 );
220 error.setValidationError('input', 'input');
221 throw error;
222 }
223
224 if (!this.input.email || !this.input.email.includes('@')) {
225 const error = new CreateUserError(
226 'Valid email address is required',
227 'VALIDATION_ERROR'
228 );
229 error.setValidationError('email', 'input.email');
230 throw error;
231 }
232 }
233
234 async execute(): Promise<CreateUserOutput> {
235 this.validate(); // Throws detailed errors if invalid
236
237 // Business logic implementation
238 const hashedPassword = await this.hashPassword(this.input.password);
239 const user = await this.databaseService.createUser({
240 email: this.input.email,
241 passwordHash: hashedPassword,
242 name: this.input.name
243 });
244
245 await this.emailService.sendVerificationEmail(user.email, user.verificationToken);
246
247 return {
248 userId: user.id,
249 email: user.email,
250 createdAt: user.createdAt,
251 requiresVerification: true
252 };
253 }
254
255 getMetadata(): CommandMetadata {
256 return CreateUserCommand.metadata;
257 }
258}
259```
260
261### Validation Error Pattern
262
263Commands use error-throwing validation with detailed context:
264
265```typescript
266export class BaseError extends Error {
267 public readonly timestamp: Date;
268 public readonly code?: string;
269 public readonly context?: Record<string, any>;
270
271 // Validation error properties
272 public invalidInput: boolean = false;
273 public invalidInputName: string = '';
274 public invalidInputPath: string = '';
275
276 constructor(message: string, code?: string, context?: Record<string, any>) {
277 super(message);
278 this.name = this.constructor.name;
279 this.timestamp = new Date();
280 this.code = code;
281 this.context = context;
282 }
283
284 /**
285 * Mark this error as a validation error with specific input details
286 */
287 setValidationError(inputName: string, inputPath: string): this {
288 this.invalidInput = true;
289 this.invalidInputName = inputName;
290 this.invalidInputPath = inputPath;
291 return this;
292 }
293}
294```
295
296## Service Design & Portability
297
298### Service Portability Principle
299
300**CRITICAL**: Services must be portable across projects. They cannot depend on project-specific configuration classes. All dependencies must be injected through constructor parameters.
301
302**Why Portability Matters:**
303- **Cross-Project Reusability**: Services work in any project without modification
304- **Easier Testing**: Services can be tested in isolation with custom configurations
305- **Package Publishing**: Services can be published as reusable packages
306- **Configuration Independence**: Project config changes don't affect service code
307- **AI-Friendly Discovery**: Portable services with rich metadata enable intelligent selection
308
309### Service Configuration Pattern
310
311Every service defines its own configuration interface:
312
313```typescript
314// Service-specific configuration interface
315export interface CacheServiceConfig {
316 maxMemoryMB: number;
317 defaultTtlSeconds: number;
318 evictionPolicy: 'LRU' | 'LFU' | 'FIFO';
319 enableMetrics: boolean;
320}
321
322// Service interface with BaseService extension
323export interface ICacheService extends BaseService {
324 get<T>(key: string): T | null;
325 set<T>(key: string, value: T, ttlSeconds?: number): void;
326 delete(key: string): boolean;
327 flush(): void;
328 getStats(): CacheStats;
329}
330
331// Portable service implementation
332export class MemoryCacheService extends BaseService implements ICacheService {
333 constructor(
334 private config: CacheServiceConfig, // Own config interface
335 private logger: ILogger, // Interface dependency
336 private metricsService?: IMetricsService // Optional interface dependency
337 ) {
338 super();
339 }
340
341 // Implementation uses this.config, never project-specific config
342 async set<T>(key: string, value: T, ttlSeconds?: number): Promise<void> {
343 const ttl = ttlSeconds || this.config.defaultTtlSeconds;
344 // Use service-specific configuration
345 }
346}
347```
348
349### Service Metadata Pattern
350
351Services include comprehensive metadata for AI-driven selection:
352
353```typescript
354interface ServiceMetadata {
355 name: string; // Unique service identifier
356 displayName: string; // Human-readable name
357 description: string; // Detailed capability description
358 contract: string; // Interface name implemented
359 implementation: string; // Implementation class name
360 version: string; // Service version
361 contractVersion: string; // Interface version compatibility
362 features: string[]; // Key features and capabilities
363 limitations: string[]; // Known constraints
364 requirements: string[]; // External dependencies
365 recommendations: string[]; // Optimal use cases
366 dependencies: {
367 services: string[]; // Required service interfaces
368 };
369}
370```
371
372### Multiple Implementation Pattern
373
374Multiple services can implement the same contract with different characteristics:
375
376```typescript
377// Memory-based cache implementation
378export class MemoryCacheService extends BaseService implements ICacheService {
379 getMetadata(): ServiceMetadata {
380 return {
381 name: 'MemoryCacheService',
382 displayName: 'In-Memory Cache',
383 description: 'High-performance in-memory caching with TTL support',
384 contract: 'ICacheService',
385 implementation: 'MemoryCacheService',
386 version: '1.0.0',
387 contractVersion: '1.0',
388 features: ['In-memory storage', 'TTL expiration', 'Fast access'],
389 limitations: ['Memory-bound storage', 'Single-process scope'],
390 requirements: ['CacheServiceConfig', 'ILogger'],
391 recommendations: ['Development environments', 'Small datasets'],
392 dependencies: { services: ['IMetricsService'] }
393 };
394 }
395}
396
397// Redis-based cache implementation
398export class RedisCacheService extends BaseService implements ICacheService {
399 getMetadata(): ServiceMetadata {
400 return {
401 name: 'RedisCacheService',
402 displayName: 'Redis Distributed Cache',
403 description: 'Distributed caching with Redis backend and clustering',
404 contract: 'ICacheService',
405 implementation: 'RedisCacheService',
406 version: '1.0.0',
407 contractVersion: '1.0',
408 features: ['Distributed caching', 'Data persistence', 'Clustering'],
409 limitations: ['Requires Redis server', 'Network latency'],
410 requirements: ['RedisCacheServiceConfig', 'ILogger', 'IRedisClient'],
411 recommendations: ['Production environments', 'Multi-instance applications'],
412 dependencies: { services: ['IRedisClient'] }
413 };
414 }
415}
416```
417
418## Registry & Discovery Patterns
419
420### ServiceRegistry for Configuration Mapping
421
422The ServiceRegistry enables service portability by mapping project-specific configuration into service-specific configuration interfaces:
423
424```typescript
425interface ServiceRegistry {
426 // Singleton access - Primary entry point
427 static getInstance(): ServiceRegistry;
428
429 // Service management with configuration mapping
430 register<T>(serviceName: string, factory: () => T): void;
431 get<T>(serviceName: string): T;
432
433 // Command registry access - THE ONLY CORRECT WAY
434 getCommandRegistry(): CommandRegistry;
435
436 // Configuration and logging
437 getLogger(serviceName: string): ILogger;
438 getConfig(): AppConfig;
439}
440```
441
442### CommandRegistry for Discovery
443
444The CommandRegistry provides sophisticated command discovery and composition:
445
446```typescript
447interface CommandRegistry {
448 // Lazy loading with automatic service injection
449 get<T extends BaseCommand>(
450 commandClass: CommandConstructor,
451 input?: any,
452 logger?: ICommandLogger
453 ): Promise<T>;
454
455 // Service integration (managed by ServiceRegistry)
456 setServiceResolver(resolver: (serviceName: string) => any): void;
457
458 // Metadata-driven discovery
459 findByCategory(category: string): CommandMetadata[];
460 findByDependency(serviceName: string): CommandMetadata[];
461 findByDataFlow(inputType?: string, outputType?: string): CommandMetadata[];
462
463 // Contract-based discovery for workflow composition
464 findNextCommands(producingCommandName: string): CommandMetadata[];
465 findPreviousCommands(consumingCommandName: string): CommandMetadata[];
466 findAlternativeCommands(commandName: string): CommandMetadata[];
467
468 // Workflow composition
469 findWorkflowChains(startContract: string, endContract: string): WorkflowChain[];
470 getContractAnalysis(): ContractAnalysis;
471}
472```
473
474### Registry Access Patterns
475
476**CRITICAL**: There is only ONE correct way to access commands:
477
478#### Pattern 1: Constructor Injection (Frequent Command Users)
479```typescript
480// Services that frequently use commands receive CommandRegistry via constructor
481export class WorkflowManagerService extends BaseService {
482 constructor(
483 config: WorkflowManagerConfig,
484 logger: ILogger,
485 commandRegistry: CommandRegistry // ✅ Injected by ServiceRegistry
486 ) {
487 super();
488 this.commandRegistry = commandRegistry;
489 }
490
491 async processWorkflow(): Promise<void> {
492 // CommandRegistry readily available
493 const command = await this.commandRegistry.get(ProcessStepCommand, input);
494 await command.execute();
495 }
496}
497```
498
499#### Pattern 2: Runtime Access (Occasional Command Users)
500```typescript
501// Services that need commands receive CommandRegistry via constructor
502export class MonitorService extends BaseService {
503 constructor(
504 private config: MonitorServiceConfig,
505 private logger: ILogger,
506 private commandRegistry: CommandRegistry // ✅ Injected by ServiceRegistry
507 ) {
508 super();
509 }
510
511 private async analyzeMetrics(): Promise<any> {
512 // ✅ CORRECT: Use injected CommandRegistry
513 const command = await this.commandRegistry.get(AnalyzeMetricsCommand, input);
514 return await command.execute();
515 }
516}
517```
518
519#### ❌ NEVER DO THIS:
520```typescript
521// WRONG: Commands should not access ServiceRegistry directly
522const serviceRegistry = ServiceRegistry.getInstance();
523const commandRegistry = serviceRegistry.getCommandRegistry();
524
525// WRONG: Direct command instantiation bypasses registry
526const command = new SomeCommand();
527
528// WRONG: Commands calling registry during execution
529const command = await this.commandRegistry.get(SomeCommand, input);
530```
531
532## Dependency Injection Patterns
533
534### Service Dependencies
535
536Commands specify dependencies using service interface names in metadata:
537
538```typescript
539// ✅ CORRECT - Interface references
540static readonly metadata: CommandMetadata = {
541 dependencies: {
542 services: [
543 'IDatabaseService', // Database operations
544 'ICacheService', // Caching layer
545 'IEmailService' // Email notifications
546 ]
547 }
548};
549
550// ❌ INCORRECT - Concrete class or technology references
551static readonly metadata: CommandMetadata = {
552 dependencies: {
553 services: [
554 'DatabaseService', // Don't reference concrete classes
555 'SQL Server', // Don't use technology names
556 'Redis Cache' // Don't reference specific implementations
557 ]
558 }
559};
560```
561
562### Command Dependencies
563
564Commands that use other commands declare them in metadata:
565
566```typescript
567export class WorkflowCommand {
568 static readonly metadata: CommandMetadata = {
569 name: 'UserRegistrationWorkflow',
570 dependencies: {
571 services: ['IDatabaseService'],
572 commands: ['user/CreateUserCommand', 'email/SendWelcomeEmailCommand']
573 }
574 };
575
576 constructor(
577 public input?: WorkflowInput,
578 private logger?: ICommandLogger,
579 private services?: Record<string, any>,
580 private commands?: Record<string, any> // ✅ Commands injected here
581 ) {
582 // Resolve command dependencies
583 this.createUserCommand = commands?.['user/CreateUserCommand'];
584 this.sendEmailCommand = commands?.['email/SendWelcomeEmailCommand'];
585 }
586
587 async execute(): Promise<WorkflowOutput> {
588 // Use injected commands
589 const user = await this.createUserCommand.execute();
590 await this.sendEmailCommand.setInput({ userId: user.userId }).execute();
591 return { success: true, userId: user.userId };
592 }
593}
594```
595
596## Error Handling & Validation
597
598### Error Contract Design
599
600All command errors extend BaseError with validation capabilities:
601
602```typescript
603export class CreateUserError extends BaseError {
604 constructor(message: string, code?: string, context?: Record<string, any>) {
605 super(message, code, context);
606 this.name = "CreateUserError";
607 }
608}
609```
610
611### Validation Pattern
612
613Commands use error-throwing validation with detailed feedback:
614
615```typescript
616validate(): void {
617 if (!this.input?.email) {
618 const error = new CreateUserError(
619 'Email field is required and cannot be empty',
620 'VALIDATION_ERROR'
621 );
622 error.setValidationError('email', 'input.email');
623 throw error;
624 }
625
626 if (this.input.password.length < 8) {
627 const error = new CreateUserError(
628 'Password must be at least 8 characters long',
629 'VALIDATION_ERROR'
630 );
631 error.setValidationError('password', 'input.password');
632 throw error;
633 }
634}
635```
636
637## Workflow Composition
638
639### Sequential Workflows
640
641Commands can be composed into sequential workflows:
642
643```typescript
644export class VideoProcessingWorkflow {
645 async execute(input: VideoInput): Promise<VideoOutput> {
646 const serviceRegistry = ServiceRegistry.getInstance();
647 const commandRegistry = serviceRegistry.getCommandRegistry();
648
649 // Step 1: Extract metadata
650 const metadataCommand = await commandRegistry.get(
651 ExtractMetadataCommand,
652 { videoUrl: input.url }
653 );
654 const metadata = await metadataCommand.execute();
655
656 // Step 2: Process audio (using output from step 1)
657 const audioCommand = await commandRegistry.get(
658 ProcessAudioCommand,
659 { videoId: metadata.videoId }
660 );
661 const audio = await audioCommand.execute();
662
663 // Step 3: Generate summary (using outputs from steps 1 & 2)
664 const summaryCommand = await commandRegistry.get(
665 GenerateSummaryCommand,
666 { title: metadata.title, transcript: audio.transcript }
667 );
668 const summary = await summaryCommand.execute();
669
670 return { summary: summary.text, metadata };
671 }
672}
673```
674
675### Parallel Workflows
676
677Commands can execute in parallel for performance:
678
679```typescript
680export class ParallelProcessingWorkflow {
681 async execute(input: ProcessingInput): Promise<ProcessingOutput> {
682 // Execute multiple commands in parallel
683 const [validation, enrichment, analysis] = await Promise.all([
684 this.validateData(input.data),
685 this.enrichData(input.data),
686 this.analyzeData(input.data)
687 ]);
688
689 return this.combineResults(validation, enrichment, analysis);
690 }
691}
692```
693
694## AI-Driven Selection
695
696### Service Selection by AI
697
698AI agents use service metadata to select optimal implementations:
699
700```typescript
701function selectOptimalCacheService(useCase: string): string {
702 const serviceRegistry = ServiceRegistry.getInstance();
703
704 // Find all cache implementations
705 const cacheServices = serviceRegistry.findServicesByContract('ICacheService');
706
707 // AI analyzes metadata to choose optimal implementation
708 // Based on: features, limitations, recommendations, performance
709 for (const service of cacheServices) {
710 const metadata = service.getMetadata();
711 if (metadata.recommendations.includes(useCase)) {
712 return metadata.name;
713 }
714 }
715
716 return cacheServices[0].name; // Fallback
717}
718```
719
720### Command Discovery by AI
721
722AI agents discover commands through metadata analysis:
723
724```typescript
725function findOptimalWorkflow(startType: string, endType: string): CommandMetadata[] {
726 const serviceRegistry = ServiceRegistry.getInstance();
727 const commandRegistry = serviceRegistry.getCommandRegistry();
728
729 // Find possible workflow chains
730 const workflows = commandRegistry.findWorkflowChains(startType, endType);
731
732 // AI selects optimal based on performance, complexity, dependencies
733 return workflows
734 .sort((a, b) => a.complexity - b.complexity)
735 .map(w => w.commands)[0];
736}
737```
738
739### Contract Analysis
740
741The registry provides contract analysis for intelligent composition:
742
743```typescript
744interface ContractAnalysis {
745 availableInputTypes: string[];
746 availableOutputTypes: string[];
747 possibleChains: WorkflowChain[];
748 orphanedCommands: CommandMetadata[];
749 circularDependencies: string[];
750}
751```
752
753## Design Best Practices
754
755### Command Design Principles
756
7571. **Static Metadata**: Always provide static metadata accessible before instantiation
7582. **Self-Contained**: Keep all command-related types in the same module
7593. **Interface Dependencies**: Use service interfaces, never concrete implementations
7604. **Contract Naming**: Use domain-focused names, not platform-specific ones
7615. **Error Handling**: Extend BaseError for all command-specific errors
7626. **Validation**: Use error-throwing validation with detailed context
763
764### Service Design Principles
765
7661. **Rich Metadata**: Provide comprehensive metadata for AI-driven selection
7672. **Clear Recommendations**: Specify optimal use cases and environments
7683. **Honest Limitations**: Document constraints and boundaries clearly
7694. **Version Compatibility**: Track both service and contract versions
7705. **Feature Clarity**: List specific capabilities, not vague descriptions
7716. **Portability**: Never depend on project-specific configuration classes
772
773### Registry Integration Principles
774
7751. **Single Access Path**: Commands and services access dependencies through constructor injection
7762. **Configuration Mapping**: ServiceRegistry maps project config to service-specific config interfaces
7773. **Category Organization**: Group commands by functional categories
7784. **Dependency Analysis**: Use registry to validate service availability
7795. **Workflow Discovery**: Leverage registry for automatic workflow construction
780
781## Presentation Layer Integration
782
783### Lightweight Implementation Principle
784
785Presentation layers (API routes, web page handlers, UI components) should act as thin orchestration layers that delegate business logic to Services and Commands. These components focus on:
786
787- **Request/Response Handling**: Accept input data and format responses
788- **Input Validation**: Basic format and presence validation
789- **Service Orchestration**: Delegate business operations to services
790- **Error Translation**: Convert service/command errors to appropriate presentation formats
791- **Cross-cutting Concerns**: Handle logging, monitoring, security, caching through service layer
792
793### Service-First vs Command-First Usage
794
795The micro-block pattern supports two valid orchestration approaches:
796
797#### Service-First Approach
798```typescript
799// Presentation Layer → Service → Commands
800export async function handleUserRegistration(requestData: any): Promise<any> {
801 // Lightweight orchestration layer
802 const securityService = serviceRegistry.get<ISecurityService>('ISecurityService');
803 const result = await securityService.registerUser(
804 requestData,
805 baseUrl,
806 requestContext
807 );
808
809 return {
810 success: true,
811 message: result.message,
812 user: result.user
813 };
814}
815```
816
817**When to use Service-First:**
818- Cross-cutting concerns (security, caching, monitoring)
819- Operations requiring service-level coordination
820- Business operations needing multiple service interactions
821
822#### Command-First Approach
823```typescript
824// Presentation Layer → Commands directly
825export async function handleSimpleOperation(requestData: any): Promise<any> {
826 const serviceRegistry = ServiceRegistry.getInstance();
827 const commandRegistry = serviceRegistry.getCommandRegistry();
828
829 const command = await commandRegistry.get<SimpleCommand>(
830 SimpleCommand,
831 requestData
832 );
833 const result = await command.execute();
834
835 return {
836 success: true,
837 data: result
838 };
839}
840```
841
842**When to use Command-First:**
843- Workflow commands
844- Self-contained business operations
845- Direct data retrieval/manipulation
846- Operations with no cross-cutting concerns
847
848### Service Orchestration Patterns
849
850Services provide valuable abstraction for:
851
852#### Cross-Cutting Concerns
853```typescript
854export class SecurityService extends BaseService {
855 async registerUser(userData: any, baseUrl: string, context: SecurityContext): Promise<any> {
856 // Rate limiting (cross-cutting concern)
857 await this.checkRateLimit(context.ip, 'user-registration');
858
859 // Business logic delegation to commands
860 const createUserCommand = await this.commandRegistry.get<CreateUserCommand>(
861 CreateUserCommand, userData
862 );
863 const userResult = await createUserCommand.execute();
864
865 // Email sending (cross-cutting concern)
866 const emailCommand = await this.commandRegistry.get<SendVerificationEmailCommand>(
867 SendVerificationEmailCommand,
868 { email: userResult.email, baseUrl }
869 );
870 await emailCommand.execute();
871
872 // Monitoring (cross-cutting concern)
873 this.monitorService.logUserEvent('user-registered', userResult.userId, context);
874
875 return userResult;
876 }
877}
878```
879
880#### Workflow Coordination
881```typescript
882export class WorkflowService extends BaseService {
883 async processComplexWorkflow(input: WorkflowInput): Promise<WorkflowOutput> {
884 // Sequential command execution with error handling
885 const step1Result = await this.executeCommand(ValidateDataCommand, input.data);
886 const step2Result = await this.executeCommand(TransformDataCommand, step1Result);
887 const step3Result = await this.executeCommand(PersistDataCommand, step2Result);
888
889 // Parallel command execution
890 const [notification, audit] = await Promise.all([
891 this.executeCommand(SendNotificationCommand, step3Result),
892 this.executeCommand(CreateAuditLogCommand, { workflow: 'complex', result: step3Result })
893 ]);
894
895 return {
896 success: true,
897 dataId: step3Result.id,
898 notificationSent: notification.sent,
899 auditCreated: audit.created
900 };
901 }
902
903 private async executeCommand<T>(CommandClass: any, input: any): Promise<T> {
904 const command = await this.commandRegistry.get<T>(CommandClass, input);
905 return await command.execute();
906 }
907}
908```
909
910### Error Handling in Presentation Layers
911
912```typescript
913export async function handleRequest(requestData: any): Promise<any> {
914 try {
915 const result = await businessService.performOperation(requestData);
916 return {
917 success: true,
918 data: result
919 };
920 } catch (error: any) {
921 // Translate service/command errors to presentation format
922 if (error.invalidInput) {
923 return {
924 success: false,
925 error: 'VALIDATION_ERROR',
926 message: error.message,
927 field: error.invalidInputName
928 };
929 }
930
931 if (error.code === 'RATE_LIMIT_EXCEEDED') {
932 return {
933 success: false,
934 error: 'RATE_LIMITED',
935 message: 'Too many requests, please try again later',
936 retryAfter: error.context?.retryAfter
937 };
938 }
939
940 // Generic error fallback
941 return {
942 success: false,
943 error: 'INTERNAL_ERROR',
944 message: 'An unexpected error occurred'
945 };
946 }
947}
948```
949
950## Implementation Guidelines
951
952The Micro-Block Design Pattern is technology-agnostic and can be implemented in various languages and frameworks. The key is maintaining the core principles:
953
954### Essential Elements
955
956- **Contract-first design** with explicit Input/Output/Error interfaces
957- **Rich metadata** for AI-driven discovery and composition
958- **Dependency injection** using service interfaces
959- **Registry-based discovery** for commands and services
960- **Self-contained modularity** with clear boundaries
961- **Error-throwing validation** with detailed context
962- **Service portability** through configuration interfaces
963
964### Implementation Considerations
965
966- **Technology Stack**: Pattern works with any OOP language with interfaces/abstract classes
967- **Serialization**: Metadata should be serializable for cross-process discovery
968- **Performance**: Lazy loading and caching for production performance
969- **Testing**: Mock services through registry for clean unit testing
970- **Documentation**: Rich metadata serves as living documentation
971
972This design pattern creates a robust foundation for AI-collaborative development where components are discoverable, composable, and properly abstracted through well-defined contracts. The comprehensive metadata system enables AI to make informed decisions about component selection and composition while maintaining clear dependency tracking and automatic registration.
Metadata
- Path
- utaba/main/patterns/micro-block/micro-block-pattern.md
- Namespace
- utaba/main/patterns/micro-block
- Author
- utaba
- Category
- patterns
- Contract Version
- 3.0.1
- MIME Type
- text/markdown
- Published
- 22-Jul-2025
- Last Updated
- 22-Jul-2025