validation-patterns.md
Comprehensive validation patterns for micro-block commands with detailed error feedback
validation-patterns.mdv1.0.09.1 KB
validation-patterns.md(markdown)
1# Validation Patterns for Micro-Block Architecture
2
3## Overview
4
5This document defines validation patterns for micro-block commands that provide detailed error feedback instead of simple boolean validation.
6
7## Core Validation Principles
8
91. **Error-Throwing Validation**: Commands throw detailed errors instead of returning boolean validation results
102. **Specific Error Messages**: Each validation error provides clear, actionable feedback
113. **Field-Level Validation**: Errors identify the specific field and path that failed validation
124. **Validation Error Metadata**: Errors include structured metadata for automated processing
13
14## Validation Interface
15
16### Base Command Validation
17
18```typescript
19export interface BaseCommand {
20 /**
21 * Validate that the command can execute with current configuration.
22 * Throws detailed validation errors instead of returning boolean.
23 * Use raiseError() method to throw errors with validation details.
24 */
25 validate(): void;
26 execute(): Promise<any>;
27 getMetadata(): CommandMetadata;
28}
29```
30
31## Error Handling Pattern
32
33### BaseError with Validation Support
34
35```typescript
36export class BaseError extends Error {
37 public readonly timestamp: Date;
38 public readonly code?: string;
39 public readonly details?: Record<string, any>;
40
41 // Validation error properties
42 public invalidInput: boolean = false;
43 public invalidInputName: string = '';
44 public invalidInputPath: string = '';
45
46 constructor(message: string, code?: string, details?: Record<string, any>) {
47 super(message);
48 this.name = this.constructor.name;
49 this.timestamp = new Date();
50 this.code = code;
51 this.details = details;
52
53 Object.setPrototypeOf(this, new.target.prototype);
54 if (Error.captureStackTrace) {
55 Error.captureStackTrace(this, this.constructor);
56 }
57 }
58
59 /**
60 * Mark this error as a validation error with specific input details
61 */
62 setValidationError(inputName: string, inputPath: string): this {
63 this.invalidInput = true;
64 this.invalidInputName = inputName;
65 this.invalidInputPath = inputPath;
66 return this;
67 }
68}
69```
70
71### Command-Specific Error Classes
72
73```typescript
74export class CreateUserError extends BaseError {
75 constructor(message: string, code?: string, details?: Record<string, any>) {
76 super(message, code, details);
77 Object.setPrototypeOf(this, CreateUserError.prototype);
78 this.name = "CreateUserError";
79 }
80}
81```
82
83## Validation Implementation Patterns
84
85### Input Validation Pattern
86
87```typescript
88validate(): void {
89 // Check for null/undefined input
90 if (!this.input) {
91 const error = new CreateUserError(
92 'Command input cannot be null or undefined',
93 'VALIDATION_ERROR'
94 );
95 error.setValidationError('input', 'input');
96 throw this.raiseError(error);
97 }
98
99 // Check required fields
100 if (!this.input.email) {
101 const error = new CreateUserError(
102 'Email field is required and cannot be empty',
103 'VALIDATION_ERROR'
104 );
105 error.setValidationError('email', 'input.email');
106 throw this.raiseError(error);
107 }
108
109 // Check field format
110 if (!this.isValidEmail(this.input.email)) {
111 const error = new CreateUserError(
112 'Email must be a valid email address format',
113 'VALIDATION_ERROR'
114 );
115 error.setValidationError('email', 'input.email');
116 throw this.raiseError(error);
117 }
118
119 // Check field length
120 if (this.input.password && this.input.password.length < 8) {
121 const error = new CreateUserError(
122 'Password must be at least 8 characters long',
123 'VALIDATION_ERROR'
124 );
125 error.setValidationError('password', 'input.password');
126 throw this.raiseError(error);
127 }
128}
129```
130
131### Service Dependency Validation
132
133```typescript
134validate(): void {
135 // Validate service dependencies
136 if (!this.databaseService) {
137 const error = new CreateUserError(
138 'IDatabaseService is required for user creation',
139 'MISSING_SERVICE'
140 );
141 error.setValidationError('databaseService', 'services.IDatabaseService');
142 throw this.raiseError(error);
143 }
144
145 // Validate service health
146 if (!this.databaseService.isHealthy()) {
147 const error = new CreateUserError(
148 'Database service is not healthy and cannot process requests',
149 'SERVICE_UNHEALTHY'
150 );
151 error.setValidationError('databaseService', 'services.IDatabaseService');
152 throw this.raiseError(error);
153 }
154}
155```
156
157### Complex Object Validation
158
159```typescript
160validate(): void {
161 // Validate nested objects
162 if (this.input.profile) {
163 if (!this.input.profile.firstName) {
164 const error = new CreateUserError(
165 'Profile first name is required when profile is provided',
166 'VALIDATION_ERROR'
167 );
168 error.setValidationError('firstName', 'input.profile.firstName');
169 throw this.raiseError(error);
170 }
171
172 if (!this.input.profile.lastName) {
173 const error = new CreateUserError(
174 'Profile last name is required when profile is provided',
175 'VALIDATION_ERROR'
176 );
177 error.setValidationError('lastName', 'input.profile.lastName');
178 throw this.raiseError(error);
179 }
180 }
181
182 // Validate arrays
183 if (this.input.tags && this.input.tags.length === 0) {
184 const error = new CreateUserError(
185 'Tags array cannot be empty when provided',
186 'VALIDATION_ERROR'
187 );
188 error.setValidationError('tags', 'input.tags');
189 throw this.raiseError(error);
190 }
191}
192```
193
194## Validation Helper Methods
195
196### Command Base Class Helpers
197
198```typescript
199export abstract class BaseCommand {
200 protected raiseError(error: BaseError): BaseError {
201 this.logger?.error(`Validation failed: ${error.message}`, {
202 command: this.getMetadata().name,
203 errorCode: error.code,
204 invalidInput: error.invalidInput,
205 invalidInputName: error.invalidInputName,
206 invalidInputPath: error.invalidInputPath
207 });
208 return error;
209 }
210
211 protected isValidEmail(email: string): boolean {
212 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
213 return emailRegex.test(email);
214 }
215
216 protected isValidUrl(url: string): boolean {
217 try {
218 new URL(url);
219 return true;
220 } catch {
221 return false;
222 }
223 }
224}
225```
226
227## Error Code Standards
228
229### Validation Error Codes
230
231- `VALIDATION_ERROR` - General input validation failure
232- `MISSING_SERVICE` - Required service dependency not provided
233- `SERVICE_UNHEALTHY` - Service dependency is not healthy
234- `INVALID_FORMAT` - Field format validation failure
235- `INVALID_LENGTH` - Field length validation failure
236- `INVALID_RANGE` - Field value outside acceptable range
237- `MISSING_REQUIRED_FIELD` - Required field not provided
238- `INVALID_COMBINATION` - Invalid combination of field values
239
240## Testing Validation
241
242### Validation Test Pattern
243
244```typescript
245describe('CreateUserCommand Validation', () => {
246 it('should throw validation error for missing email', async () => {
247 const command = new CreateUserCommand({
248 password: 'password123',
249 name: 'Test User'
250 // email missing
251 });
252
253 expect(() => command.validate()).toThrow(CreateUserError);
254
255 try {
256 command.validate();
257 } catch (error) {
258 expect(error.invalidInput).toBe(true);
259 expect(error.invalidInputName).toBe('email');
260 expect(error.invalidInputPath).toBe('input.email');
261 expect(error.code).toBe('VALIDATION_ERROR');
262 }
263 });
264
265 it('should throw validation error for invalid email format', async () => {
266 const command = new CreateUserCommand({
267 email: 'invalid-email',
268 password: 'password123',
269 name: 'Test User'
270 });
271
272 expect(() => command.validate()).toThrow(CreateUserError);
273
274 try {
275 command.validate();
276 } catch (error) {
277 expect(error.invalidInput).toBe(true);
278 expect(error.invalidInputName).toBe('email');
279 expect(error.invalidInputPath).toBe('input.email');
280 expect(error.message).toContain('valid email address format');
281 }
282 });
283});
284```
285
286## Best Practices
287
288### Validation Design
2891. **Fail Fast**: Validate input before any business logic execution
2902. **Specific Messages**: Provide clear, actionable error messages
2913. **Field Identification**: Always identify the specific field that failed
2924. **Consistent Codes**: Use standardized error codes across commands
2935. **Logging Integration**: Log validation failures for debugging
294
295### Error Handling
2961. **Structured Errors**: Use BaseError with validation metadata
2972. **Error Propagation**: Let validation errors bubble up with full context
2983. **User-Friendly Messages**: Write error messages for end users, not developers
2994. **Localization Ready**: Design error messages for easy localization
3005. **Recovery Guidance**: When possible, suggest how to fix the validation error
301
302### Testing Strategy
3031. **Comprehensive Coverage**: Test all validation scenarios
3042. **Error Metadata**: Verify error metadata is set correctly
3053. **Edge Cases**: Test boundary conditions and edge cases
3064. **Service Dependencies**: Test validation with missing/unhealthy services
3075. **Integration Testing**: Test validation in full command execution flow
308
309This validation pattern ensures that commands provide rich, actionable feedback when validation fails, enabling better error handling and user experience in AI-collaborative development scenarios.
Metadata
- Path
- utaba/main/patterns/micro-block/validation-patterns.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