✨ Características: - 45 ejercicios universitarios (Basic → Advanced) - Renderizado LaTeX profesional - IA generativa (Z.ai/DashScope) - Docker 9 servicios - Tests 123/123 pasando - Seguridad enterprise (JWT, XSS, Rate limiting) 🐳 Infraestructura: - Next.js 14 + Node.js 20 - PostgreSQL 15 + Redis 7 - Docker Compose completo - Nginx + SSL ready 📚 Documentación: - 5 informes técnicos completos - README profesional - Scripts de deployment automatizados Estado: Producción lista ✅
518 lines
12 KiB
Markdown
518 lines
12 KiB
Markdown
# Backend Professionalization Plan - Math Platform
|
|
|
|
## Executive Summary
|
|
|
|
This document outlines the complete migration plan to transform the Math Platform backend into an enterprise-grade system with strict TypeScript, clean architecture, and professional patterns.
|
|
|
|
## Current Status
|
|
|
|
### Errors Found (Initial)
|
|
- **Total TypeScript Errors**: 159
|
|
- **Categories**:
|
|
- TS6133: Unused variables/imports (~25)
|
|
- TS2345: Type undefined not assignable to string (~40)
|
|
- TS2375/TS2379: exactOptionalPropertyTypes issues (~60)
|
|
- TS2322: Type null/undefined assignment errors (~20)
|
|
- TS6196: Unused type declarations (~10)
|
|
- Other misc errors (~4)
|
|
|
|
### Errors Fixed in This Session
|
|
1. ✓ Fixed `jwt.sign` type issues in auth.service.ts
|
|
2. ✓ Fixed `topicId` null handling in exercise.service.ts
|
|
3. ✓ Removed unused ValidationError import
|
|
4. ✓ Removed unused adminChatId variable
|
|
5. ✓ Removed unused getTelegramAdminChatId import
|
|
6. ✓ Fixed module.controller.ts imports
|
|
|
|
### Remaining: ~150 errors
|
|
|
|
## Architecture Implemented
|
|
|
|
### 1. Configuration System ✓
|
|
**Location**: `src/config/index.ts`
|
|
|
|
**Features**:
|
|
- Zod schema validation for all environment variables
|
|
- Type-safe config object
|
|
- Computed properties (isDevelopment, isProduction, etc.)
|
|
- Centralized defaults and constants
|
|
|
|
**Usage**:
|
|
```typescript
|
|
import { config } from './config';
|
|
|
|
// Type-safe access
|
|
const port = config.PORT;
|
|
const isDev = config.isDevelopment;
|
|
```
|
|
|
|
### 2. Enterprise Error System ✓
|
|
**Location**: `src/core/errors/index.ts`
|
|
|
|
**Features**:
|
|
- Hierarchical error classes (AppError base)
|
|
- Error codes enum for frontend mapping
|
|
- HTTP status code mapping
|
|
- Structured logging support
|
|
- Prisma error mapping
|
|
- Production vs development responses
|
|
|
|
**Error Types**:
|
|
- ValidationError (400)
|
|
- AuthenticationError (401)
|
|
- AuthorizationError (403)
|
|
- NotFoundError (404)
|
|
- ConflictError (409)
|
|
- RateLimitError (429)
|
|
- DatabaseError (500)
|
|
- ExternalServiceError (502)
|
|
- ServiceUnavailableError (503)
|
|
|
|
### 3. Core Types ✓
|
|
**Location**: `src/core/types/index.ts`
|
|
|
|
**Includes**:
|
|
- API response types (ApiSuccessResponse, ApiErrorResponse)
|
|
- Pagination types (PaginatedResult, PaginationMeta)
|
|
- Service types (ServiceResult, ServiceContext)
|
|
- Repository types (QueryOptions, RepositoryOptions)
|
|
- Event types (DomainEvent, EventHandler)
|
|
- Utility types (Nullable, Optional, DeepPartial)
|
|
|
|
### 4. Error Middleware ✓
|
|
**Location**: `src/shared/middleware/error.middleware.ts`
|
|
|
|
**Features**:
|
|
- Global error handling
|
|
- Automatic Prisma error mapping
|
|
- Structured logging with correlation IDs
|
|
- Production-safe error messages
|
|
- Async handler wrapper
|
|
|
|
### 5. Rate Limiting ✓
|
|
**Location**: `src/shared/middleware/rate-limit.middleware.ts`
|
|
|
|
**Features**:
|
|
- Redis-backed store
|
|
- Multiple limiter configurations
|
|
- Custom key generation (user-based or IP-based)
|
|
- Standard HTTP headers
|
|
- Professional error responses
|
|
|
|
**Limiters**:
|
|
- standardRateLimiter: 100 req/15min
|
|
- authRateLimiter: 5 req/15min
|
|
- exerciseRateLimiter: 30 req/5min
|
|
- aiRateLimiter: 20 req/min
|
|
- adminRateLimiter: 300 req/15min
|
|
|
|
### 6. Dependency Injection Container (In Progress)
|
|
**Location**: `src/infrastructure/di/container.ts`
|
|
|
|
**Dependencies to install**:
|
|
```bash
|
|
npm install tsyringe reflect-metadata
|
|
```
|
|
|
|
**Update tsconfig.json**:
|
|
```json
|
|
{
|
|
"compilerOptions": {
|
|
"experimentalDecorators": true,
|
|
"emitDecoratorMetadata": true
|
|
}
|
|
}
|
|
```
|
|
|
|
### 7. Repository Pattern (In Progress)
|
|
**Location**: `src/repositories/`
|
|
|
|
**Structure**:
|
|
```
|
|
repositories/
|
|
├── interfaces/
|
|
│ ├── exercise.repository.interface.ts
|
|
│ ├── user.repository.interface.ts
|
|
│ └── ...
|
|
├── exercise.repository.ts
|
|
├── user.repository.ts
|
|
└── ...
|
|
```
|
|
|
|
**Features**:
|
|
- Interface-based design
|
|
- Prisma implementation
|
|
- Caching support hooks
|
|
- Query optimization
|
|
- Transaction support
|
|
|
|
### 8. Service Layer Refactor (TODO)
|
|
|
|
**Pattern**:
|
|
```typescript
|
|
export interface IExerciseService {
|
|
create(data: CreateExerciseDTO): Promise<Exercise>;
|
|
findById(id: string): Promise<Exercise | null>;
|
|
update(id: string, data: UpdateExerciseDTO): Promise<Exercise>;
|
|
delete(id: string): Promise<void>;
|
|
}
|
|
|
|
@injectable()
|
|
export class ExerciseService implements IExerciseService {
|
|
constructor(
|
|
@inject(TOKENS.ExerciseRepository)
|
|
private readonly repository: IExerciseRepository,
|
|
@inject(TOKENS.Logger)
|
|
private readonly logger: Logger,
|
|
@inject(TOKENS.CacheService)
|
|
private readonly cache: CacheService
|
|
) {}
|
|
}
|
|
```
|
|
|
|
## Migration Plan
|
|
|
|
### Phase 1: Fix TypeScript Errors (Priority: High)
|
|
|
|
#### 1.1 Fix exactOptionalPropertyTypes Issues
|
|
|
|
**Pattern**: Replace `prop?: Type` with `prop: Type | undefined`
|
|
|
|
**Example**:
|
|
```typescript
|
|
// Before
|
|
interface Options {
|
|
userId?: string;
|
|
moduleId?: string;
|
|
}
|
|
|
|
// After
|
|
interface Options {
|
|
userId: string | undefined;
|
|
moduleId: string | undefined;
|
|
}
|
|
```
|
|
|
|
**Files to fix** (40+ occurrences):
|
|
- exercise.controller.ts (7 errors)
|
|
- module.controller.ts (7 errors)
|
|
- progress.controller.ts (3 errors)
|
|
- ranking.controller.ts (3 errors)
|
|
- user.controller.ts (3 errors)
|
|
- All *service.ts files
|
|
|
|
#### 1.2 Fix undefined to string assignments
|
|
|
|
**Pattern**: Add null checks before function calls
|
|
|
|
**Example**:
|
|
```typescript
|
|
// Before
|
|
const id = req.params.id; // string | undefined
|
|
await service.findById(id); // Error
|
|
|
|
// After
|
|
const id = req.params.id;
|
|
if (!id) {
|
|
throw new ValidationError('ID is required');
|
|
}
|
|
await service.findById(id); // OK
|
|
```
|
|
|
|
#### 1.3 Fix Prisma type issues
|
|
|
|
**Pattern**: Handle null types from Prisma
|
|
|
|
**Example**:
|
|
```typescript
|
|
// Before
|
|
topicId: string; // But Prisma returns string | null
|
|
|
|
// After
|
|
topicId: string | null;
|
|
```
|
|
|
|
#### 1.4 Remove unused imports and variables
|
|
|
|
**Pattern**: Clean up imports and use `_` prefix for unused params
|
|
|
|
**Example**:
|
|
```typescript
|
|
// Before
|
|
import { ValidationError } from '../types'; // Unused
|
|
|
|
const handler = (req, res, next) => { // req unused
|
|
res.json({ data });
|
|
};
|
|
|
|
// After
|
|
const handler = (_req, res, _next) => {
|
|
res.json({ data });
|
|
};
|
|
```
|
|
|
|
### Phase 2: Implement Repository Pattern (Priority: High)
|
|
|
|
#### 2.1 Create Repository Interfaces
|
|
|
|
For each entity (User, Exercise, Module, Progress, Ranking):
|
|
- Define interface in `src/repositories/interfaces/`
|
|
- Specify all CRUD operations
|
|
- Include query options and filters
|
|
|
|
#### 2.2 Implement Prisma Repositories
|
|
|
|
- Create implementation in `src/repositories/`
|
|
- Use dependency injection
|
|
- Add caching hooks
|
|
- Handle errors with AppError
|
|
|
|
#### 2.3 Migrate Services
|
|
|
|
- Update services to use repositories
|
|
- Add proper typing
|
|
- Implement logging
|
|
|
|
### Phase 3: Implement DI Container (Priority: Medium)
|
|
|
|
#### 3.1 Setup tsyringe
|
|
|
|
```typescript
|
|
// In server.ts
|
|
import 'reflect-metadata';
|
|
import { container } from './infrastructure/di/container';
|
|
|
|
// Register dependencies
|
|
container.register(TOKENS.PrismaClient, { useValue: prisma });
|
|
container.register(TOKENS.ExerciseRepository, { useClass: ExerciseRepository });
|
|
```
|
|
|
|
#### 3.2 Decorate Services
|
|
|
|
```typescript
|
|
@injectable()
|
|
export class ExerciseService {
|
|
constructor(
|
|
@inject(TOKENS.ExerciseRepository)
|
|
private readonly repository: IExerciseRepository
|
|
) {}
|
|
}
|
|
```
|
|
|
|
### Phase 4: Update Server Configuration (Priority: Medium)
|
|
|
|
#### 4.1 Replace Old Middleware
|
|
|
|
Update `src/server.ts`:
|
|
- Use new error middleware
|
|
- Use new rate limiters
|
|
- Add correlation ID middleware
|
|
- Use structured logging
|
|
|
|
#### 4.2 Add Health Checks
|
|
|
|
- Database health
|
|
- Redis health
|
|
- AI service health
|
|
|
|
### Phase 5: Testing (Priority: High)
|
|
|
|
#### 5.1 Unit Tests
|
|
|
|
Target: >80% coverage
|
|
- Service layer tests
|
|
- Repository tests
|
|
- Error handling tests
|
|
|
|
#### 5.2 Integration Tests
|
|
|
|
- API endpoint tests
|
|
- Database transaction tests
|
|
- Redis integration tests
|
|
|
|
#### 5.3 E2E Tests
|
|
|
|
- Full user flows
|
|
- Error scenarios
|
|
|
|
## Code Examples
|
|
|
|
### New Service Pattern
|
|
|
|
```typescript
|
|
// src/modules/exercise/exercise.service.ts
|
|
|
|
import { injectable, inject } from 'tsyringe';
|
|
import { TOKENS } from '../../infrastructure/di/container';
|
|
import { IExerciseRepository } from '../../repositories/interfaces/exercise.repository.interface';
|
|
import { AppError, NotFoundError, ValidationError } from '../../core/errors';
|
|
import type { CreateExerciseDTO, UpdateExerciseDTO } from './dtos';
|
|
import type { Exercise } from '@prisma/client';
|
|
|
|
export interface IExerciseService {
|
|
create(data: CreateExerciseDTO): Promise<Exercise>;
|
|
findById(id: string): Promise<Exercise | null>;
|
|
update(id: string, data: UpdateExerciseDTO): Promise<Exercise>;
|
|
delete(id: string): Promise<void>;
|
|
list(filters: ExerciseFilterOptions): Promise<PaginatedResult<Exercise>>;
|
|
}
|
|
|
|
@injectable()
|
|
export class ExerciseService implements IExerciseService {
|
|
constructor(
|
|
@inject(TOKENS.ExerciseRepository)
|
|
private readonly repository: IExerciseRepository,
|
|
@inject(TOKENS.Logger)
|
|
private readonly logger: Logger
|
|
) {}
|
|
|
|
async create(data: CreateExerciseDTO): Promise<Exercise> {
|
|
this.logger.info('Creating exercise', { title: data.statement });
|
|
|
|
try {
|
|
const exercise = await this.repository.create(data);
|
|
this.logger.info('Exercise created', { exerciseId: exercise.id });
|
|
return exercise;
|
|
} catch (error) {
|
|
this.logger.error('Failed to create exercise', { error, data });
|
|
throw new AppError('Failed to create exercise', ErrorCode.INTERNAL_ERROR);
|
|
}
|
|
}
|
|
|
|
async findById(id: string): Promise<Exercise | null> {
|
|
if (!id) {
|
|
throw new ValidationError('Exercise ID is required');
|
|
}
|
|
|
|
const exercise = await this.repository.findById(id);
|
|
|
|
if (!exercise) {
|
|
throw new NotFoundError('Exercise');
|
|
}
|
|
|
|
return exercise;
|
|
}
|
|
|
|
// ... other methods
|
|
}
|
|
```
|
|
|
|
### New Controller Pattern
|
|
|
|
```typescript
|
|
// src/modules/exercise/exercise.controller.ts
|
|
|
|
import { Request, Response } from 'express';
|
|
import { injectable, inject } from 'tsyringe';
|
|
import { TOKENS } from '../../infrastructure/di/container';
|
|
import { IExerciseService } from './exercise.service';
|
|
import { asyncHandler } from '../../shared/middleware/error.middleware';
|
|
|
|
@injectable()
|
|
export class ExerciseController {
|
|
constructor(
|
|
@inject(TOKENS.ExerciseService)
|
|
private readonly service: IExerciseService
|
|
) {}
|
|
|
|
getById = asyncHandler(async (req: Request, res: Response): Promise<void> => {
|
|
const id = req.params.id;
|
|
|
|
if (!id) {
|
|
throw new ValidationError('Exercise ID is required');
|
|
}
|
|
|
|
const exercise = await this.service.findById(id);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: exercise,
|
|
});
|
|
});
|
|
|
|
// ... other methods
|
|
}
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
```typescript
|
|
// In controllers - just throw
|
|
try {
|
|
await service.doSomething();
|
|
} catch (error) {
|
|
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
|
throw mapPrismaError(error, req.correlationId);
|
|
}
|
|
throw new AppError(
|
|
'Operation failed',
|
|
ErrorCode.INTERNAL_ERROR,
|
|
500,
|
|
false,
|
|
{ originalError: error },
|
|
req.correlationId
|
|
);
|
|
}
|
|
|
|
// Middleware handles the rest
|
|
```
|
|
|
|
## Commands
|
|
|
|
### Check TypeScript Errors
|
|
```bash
|
|
cd /home/ren/Documents/math2/backend
|
|
npm run type-check
|
|
```
|
|
|
|
### Fix Lint Issues
|
|
```bash
|
|
npm run lint:fix
|
|
```
|
|
|
|
### Run Tests
|
|
```bash
|
|
npm run test:coverage
|
|
```
|
|
|
|
### Build
|
|
```bash
|
|
npm run build
|
|
```
|
|
|
|
## Recommendations
|
|
|
|
1. **Enable strict mode in phases**: Fix errors in batches by directory
|
|
2. **Use type assertion sparingly**: Only for external library issues
|
|
3. **Add comprehensive tests**: Before refactoring, add tests for current behavior
|
|
4. **Use feature flags**: For gradual rollout of new architecture
|
|
5. **Document breaking changes**: Update API documentation
|
|
6. **Monitor error rates**: After deployment
|
|
|
|
## Timeline Estimate
|
|
|
|
- Phase 1 (TypeScript fixes): 2-3 days
|
|
- Phase 2 (Repositories): 3-4 days
|
|
- Phase 3 (DI setup): 1-2 days
|
|
- Phase 4 (Server update): 1-2 days
|
|
- Phase 5 (Testing): 3-4 days
|
|
|
|
**Total**: ~10-15 days for complete migration
|
|
|
|
## Next Steps
|
|
|
|
1. Continue fixing TypeScript errors using the patterns above
|
|
2. Create repository interfaces for all entities
|
|
3. Implement Prisma repositories
|
|
4. Refactor services one by one
|
|
5. Update server.ts with new middleware
|
|
6. Add comprehensive tests
|
|
7. Deploy and monitor
|
|
|
|
## Resources
|
|
|
|
- [TypeScript Strict Mode Guide](https://www.typescriptlang.org/tsconfig#strict)
|
|
- [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
|
|
- [tsyringe Documentation](https://github.com/microsoft/tsyringe)
|
|
- [Prisma Best Practices](https://www.prisma.io/docs/guides/best-practices)
|