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