development-guidelines.md
Updated development guidelines with scope management guidance to prevent AI assistants from inventing requirements
development-guidelines.mdv1.1.113.6 KB
development-guidelines.md(markdown)
1# Development Guidelines for Micro-Block Architecture
2
3## Overview
4
5This document provides essential development guidelines and best practices for working with micro-block architecture. These guidelines ensure consistency, maintainability, and proper implementation of the architectural patterns.
6
7## Critical Core Principles
8
9### šØ ES MODULES ONLY šØ
10
11This project uses ES Modules exclusively. **NEVER use CommonJS**:
12
13- ā **FORBIDDEN**: `require()`, `module.exports`, `exports`
14- ā
**REQUIRED**: `import`, `export`, `export default`
15
16**Why ES Modules are mandatory:**
17- Modern JavaScript standard (not legacy CommonJS)
18- Better tree-shaking and dead code elimination
19- Static analysis for bundlers and tools
20- Top-level await support
21- Improved TypeScript integration
22- Future-proof architecture
23
24### Contract-First Development
25
26All components must define their contracts before implementation:
27
281. **Define Input/Output Interfaces** - Start with TypeScript interfaces
292. **Define Error Contracts** - Create specific error classes
303. **Define Metadata** - Specify dependencies and capabilities
314. **Implement Logic** - Write the actual implementation
325. **Test Contracts** - Verify interface compliance
33
34### šļø Database and Repository Standards
35
36**CRITICAL**: All database and repository development must follow the Code-First Naming Standards.
37
38š **Reference**: See `utaba/guidance/development/database-naming-standards.md` for complete database conventions.
39
40**Key Requirements:**
41- **camelCase for all database columns**: `id`, `email`, `firstName`, `createdAt`
42- **camelCase for all entity properties**: Direct mapping, no translation needed
43- **camelCase for all SQL parameters**: `@id`, `@email`, `@firstName`
44- **PascalCase for table names**: `Users`, `UserSessions`, `ApiKeys`
45
46**Why this matters:**
47- Eliminates mapping overhead between database and TypeScript
48- Reduces bugs from translation errors
49- Provides consistent naming for AI model code generation
50- Improves ORM compatibility and performance
51
52## Command Development Guidelines
53
54### Command Structure
55
56Every command must follow this structure:
57
58```typescript
59// 1. Input Contract
60interface CreateUserInput {
61 email: string;
62 password: string;
63 name: string;
64}
65
66// 2. Output Contract
67interface CreateUserOutput {
68 userId: string;
69 email: string;
70 createdAt: Date;
71}
72
73// 3. Error Contract
74class CreateUserError extends BaseError {
75 constructor(message: string, code?: string, details?: Record<string, any>) {
76 super(message, code, details);
77 this.name = "CreateUserError";
78 }
79}
80
81// 4. Command Implementation
82export class CreateUserCommand implements OutputCommand<CreateUserInput, CreateUserOutput, CreateUserError> {
83 static readonly metadata: CommandMetadata = {
84 name: 'CreateUserCommand',
85 description: 'Creates a new user account in the system',
86 category: 'user',
87 inputType: 'CreateUserInput',
88 outputType: 'CreateUserOutput',
89 errorType: 'CreateUserError',
90 version: '1.0.0',
91 contractVersion: '1.0',
92 dependencies: {
93 services: ['IDatabaseService'],
94 commands: [],
95 external: []
96 }
97 };
98
99 constructor(
100 public input?: CreateUserInput,
101 private logger?: ICommandLogger,
102 private services?: Record<string, any>
103 ) {
104 // Initialize services and validate dependencies
105 }
106
107 validate(): void {
108 // Implement validation with detailed error feedback
109 }
110
111 async execute(): Promise<CreateUserOutput> {
112 this.validate();
113 // Implement business logic
114 }
115
116 getMetadata(): CommandMetadata {
117 return CreateUserCommand.metadata;
118 }
119}
120```
121
122### Command Design Rules
123
1241. **Self-Contained**: All related types in one file
1252. **Static Metadata**: Accessible before instantiation
1263. **Interface Dependencies**: Use service interfaces, not concrete classes
1274. **Error-Throwing Validation**: Throw detailed errors, don't return booleans
1285. **Dependency Declaration**: Explicitly declare all dependencies in metadata
129
130## Service Development Guidelines
131
132### Service Portability
133
134**CRITICAL**: Services must be portable and reusable across projects:
135
136```typescript
137// ā
CORRECT: Portable service with own configuration interface
138export interface CacheServiceConfig {
139 maxMemoryMB: number;
140 defaultTtlSeconds: number;
141 evictionPolicy: 'LRU' | 'LFU' | 'FIFO';
142}
143
144export class MemoryCacheService extends BaseService implements ICacheService {
145 constructor(
146 private config: CacheServiceConfig, // Service-specific config
147 private logger: ILogger,
148 private metricsService?: IMetricsService
149 ) {
150 super();
151 }
152}
153
154// ā WRONG: Service coupled to project configuration
155export class BadCacheService extends BaseService {
156 constructor(
157 private appConfig: AppConfig, // Project-specific dependency
158 private logger: ILogger
159 ) {
160 super();
161 }
162}
163```
164
165### Service Design Rules
166
1671. **Own Configuration Interface**: Each service defines its config interface
1682. **Constructor Injection**: All dependencies via constructor
1693. **Rich Metadata**: Comprehensive metadata for AI-driven selection
1704. **Health Monitoring**: Implement isHealthy() method
1715. **Graceful Cleanup**: Implement destroy() method
172
173### Repository Services
174
175Repository services MUST follow the database naming standards:
176
177```typescript
178// ā
CORRECT: Entity matches database exactly (camelCase)
179interface User {
180 id: string; // Database column: id
181 email: string; // Database column: email
182 firstName: string; // Database column: firstName
183 createdAt: Date; // Database column: createdAt
184}
185
186// ā
CORRECT: SQL parameters match columns exactly
187async findByEmail(email: string): Promise<User | null> {
188 const query = `SELECT * FROM Users WHERE email = @email`;
189 return await this.db.queryOne<User>(query, { email });
190 // No mapping needed - result object matches interface exactly
191}
192```
193
194## ServiceRegistry Guidelines
195
196### Access Patterns
197
198**ā
CORRECT: Always use ServiceRegistry as primary access point**
199
200```typescript
201// Pattern 1: Constructor Injection (Frequent Users)
202export class JobManagerService extends BaseService {
203 constructor(
204 config: JobManagerConfig,
205 logger: ILogger,
206 commandRegistry: CommandRegistry // Injected by ServiceRegistry
207 ) {
208 super();
209 }
210}
211
212// Pattern 2: Runtime Access (Occasional Users)
213export class MonitorService extends BaseService {
214 private async processCommand(): Promise<any> {
215 const serviceRegistry = ServiceRegistry.getInstance();
216 const commandRegistry = serviceRegistry.getCommandRegistry();
217
218 const command = await commandRegistry.get(SomeCommand, input);
219 return await command.execute();
220 }
221}
222```
223
224**ā WRONG: Direct registry access**
225
226```typescript
227// NEVER DO THIS
228const commandRegistry = CommandRegistry.getInstance();
229const command = new SomeCommand();
230```
231
232## API Route Development Rules
233
234### API Route Responsibilities
235
236API routes are thin orchestration layers, NOT business logic containers.
237
238**ā NEVER Do in API Routes:**
239- Direct database calls
240- Repository method calls
241- Business logic or data transformation
242- Complex filtering or calculations
243
244**ā
ALWAYS Do in API Routes:**
245- Extract request parameters
246- Call Commands via CommandRegistry
247- Map Command errors to HTTP status codes
248- Return Command results directly
249
250### API Route Template
251
252```typescript
253export async function POST(request: NextRequest, { params }: { params: { id: string } }) {
254 try {
255 const session = await auth();
256 const { id } = await params;
257 const body = await request.json();
258
259 const serviceRegistry = ServiceRegistry.getInstance();
260 const commandRegistry = serviceRegistry.getCommandRegistry();
261
262 const { CreateResourceCommand } = await import('@/commands/resource/CreateResourceCommand');
263 const command = await commandRegistry.get(CreateResourceCommand, {
264 userId: session.user.id,
265 resourceId: id,
266 ...body
267 });
268
269 const result = await command.execute();
270 return NextResponse.json(result);
271 } catch (error) {
272 return handleApiError(error);
273 }
274}
275```
276## Implement ONLY what is requested
277
2781. Only implement exactly what's requested
2792. Do Not add "helpful" features or anticipate needs
2803. Stop and ask before adding anything not explicitly mentioned
281
282## Testing Guidelines
283
284### Command Testing
285
286```typescript
287describe('CreateUserCommand', () => {
288 let mockDbService: jest.Mocked<IDatabaseService>;
289
290 beforeEach(() => {
291 mockDbService = createMockDatabaseService();
292 });
293
294 it('should create user with valid input', async () => {
295 const command = new CreateUserCommand(
296 { email: 'test@example.com', password: 'password123', name: 'Test User' },
297 mockLogger,
298 { IDatabaseService: mockDbService }
299 );
300
301 const result = await command.execute();
302 expect(result.userId).toBeDefined();
303 expect(result.email).toBe('test@example.com');
304 });
305
306 it('should throw validation error for invalid email', () => {
307 const command = new CreateUserCommand(
308 { email: 'invalid-email', password: 'password123', name: 'Test User' },
309 mockLogger,
310 { IDatabaseService: mockDbService }
311 );
312
313 expect(() => command.validate()).toThrow(CreateUserError);
314 });
315});
316```
317
318### Service Testing
319
320```typescript
321describe('CacheService', () => {
322 it('should implement ICacheService contract', () => {
323 const config: CacheServiceConfig = {
324 maxMemoryMB: 100,
325 defaultTtlSeconds: 3600,
326 evictionPolicy: 'LRU'
327 };
328
329 const service = new MemoryCacheService(config, mockLogger);
330 expect(service).toImplementInterface('ICacheService');
331 });
332});
333```
334
335## Error Handling Guidelines
336
337### Error Design Patterns
338
3391. **Extend BaseError**: All command errors extend BaseError
3402. **Specific Error Classes**: One error class per command
3413. **Validation Metadata**: Include field-level validation info
3424. **Standard Error Codes**: Use consistent error code patterns
3435. **User-Friendly Messages**: Write for end users, not developers
344
345### Error Handling Template
346
347```typescript
348validate(): void {
349 if (!this.input?.email) {
350 const error = new CreateUserError(
351 'Email field is required and cannot be empty',
352 'VALIDATION_ERROR'
353 );
354 error.setValidationError('email', 'input.email');
355 throw this.raiseError(error);
356 }
357}
358```
359
360## Code Organization Guidelines
361
362### File Structure
363
364```
365src/
366āāā commands/
367ā āāā {category}/
368ā āāā {CommandName}.ts # All command-related types in one file
369āāā services/
370ā āāā interfaces/
371ā ā āāā I{ServiceName}.ts # Service contracts
372ā āāā implementations/
373ā āāā {ServiceName}.ts # Service implementations
374āāā core/
375ā āāā interfaces/
376ā āāā registry/
377ā āāā logging/
378āāā types/
379 āāā {domain}.ts # Shared domain types
380```
381
382### Naming Conventions
383
3841. **Commands**: `{Verb}{Noun}Command` (e.g., `CreateUserCommand`)
3852. **Services**: `{Purpose}Service` (e.g., `DatabaseService`)
3863. **Interfaces**: `I{ServiceName}` (e.g., `IDatabaseService`)
3874. **Errors**: `{CommandName}Error` (e.g., `CreateUserError`)
3885. **Types**: `{Purpose}{Input|Output|Config}` (e.g., `CreateUserInput`)
3896. **Database Entities**: Follow camelCase exactly matching database columns
3907. **Repository Methods**: camelCase (e.g., `findByEmail`, `createUser`)
391
392## Performance Guidelines
393
394### Lazy Loading
395
3961. **Commands**: Loaded on-demand via CommandRegistry
3972. **Services**: Created once and cached by ServiceRegistry
3983. **Modules**: Use dynamic imports for command loading
3994. **Resources**: Load only when needed
400
401### Optimization Patterns
402
4031. **Service Caching**: ServiceRegistry caches service instances
4042. **Command Pooling**: CommandRegistry manages command instances
4053. **Dependency Resolution**: Resolve dependencies once per command execution
4064. **Memory Management**: Implement proper cleanup in destroy() methods
4075. **Database Efficiency**: No mapping overhead with camelCase naming consistency
408
409## Security Guidelines
410
411### Input Validation
412
4131. **Always Validate**: Every command validates its input
4142. **Sanitize Data**: Clean input data before processing
4153. **Type Safety**: Use TypeScript for compile-time safety
4164. **Error Handling**: Don't expose internal details in error messages
417
418### Service Security
419
4201. **Interface Boundaries**: Use interfaces to enforce contracts
4212. **Dependency Injection**: Control service access through registry
4223. **Configuration**: Keep sensitive config separate from business logic
4234. **Logging**: Log security-relevant events
424
425## Best Practices Summary
426
427### Do's
428
4291. ā
Use ES Modules exclusively
4302. ā
Define contracts before implementation
4313. ā
Access services through ServiceRegistry
4324. ā
Keep commands self-contained
4335. ā
Use error-throwing validation
4346. ā
Implement comprehensive metadata
4357. ā
Test contracts and behaviors
4368. ā
Follow naming conventions
4379. ā
Use TypeScript strictly
43810. ā
Document dependencies explicitly
43911. ā
Follow database naming standards (camelCase columns, entities, parameters)
44012. ā
Reference database-naming-standards.md for all database work
441
442### Don'ts
443
4441. ā Never use CommonJS syntax
4452. ā Never access CommandRegistry directly
4463. ā Never put business logic in API routes
4474. ā Never couple services to project configuration
4485. ā Never instantiate commands directly
4496. ā Never return boolean from validation
4507. ā Never skip error handling
4518. ā Never ignore TypeScript errors
4529. ā Never create circular dependencies
45310. ā Never bypass the registry system
45411. ā Never use inconsistent naming between database and TypeScript entities
45512. ā Never create mapping layers between database results and entity objects
456
457These guidelines ensure consistent, maintainable, and scalable development within the micro-block architecture framework.
Metadata
- Path
- utaba/main/guidance/development/development-guidelines.md
- Namespace
- utaba/main/guidance/development
- Author
- utaba
- Category
- guidance
- Contract Version
- 1.1.1
- MIME Type
- text/markdown
- Published
- 26-Jul-2025
- Last Updated
- 26-Jul-2025