--- id: "GUIA-CONVENCIONES" title: "Guía de Convenciones de Desarrollo" type: "Guide" status: "Active" project: "platform_marketing_content" version: "1.0.0" created_date: "2026-01-04" updated_date: "2026-01-04" --- # Guía de Convenciones de Desarrollo **Versión:** 1.0.0 **Fecha:** 2025-12-08 **Proyecto:** Platform Marketing Content --- ## Estructura del Proyecto ``` platform_marketing_content/ ├── apps/ │ ├── backend/ # API NestJS │ │ ├── src/ │ │ │ ├── modules/ # Módulos por dominio │ │ │ │ ├── auth/ │ │ │ │ ├── crm/ │ │ │ │ ├── projects/ │ │ │ │ ├── generation/ │ │ │ │ ├── assets/ │ │ │ │ ├── automation/ │ │ │ │ └── analytics/ │ │ │ ├── common/ # Utilidades compartidas │ │ │ ├── config/ # Configuración │ │ │ └── main.ts │ │ ├── test/ │ │ └── package.json │ │ │ ├── frontend/ # React + Vite │ │ ├── src/ │ │ │ ├── components/ # Componentes reutilizables │ │ │ ├── pages/ # Páginas/vistas │ │ │ ├── hooks/ # Custom hooks │ │ │ ├── services/ # API clients │ │ │ ├── stores/ # Estado global │ │ │ └── utils/ # Utilidades │ │ └── package.json │ │ │ └── comfyui/ # Workflows ComfyUI │ └── workflows/ │ ├── docs/ # Documentación ├── orchestration/ # Config SIMCO └── docker-compose.yml ``` --- ## Convenciones de Nomenclatura ### Backend (NestJS/TypeScript) ```typescript // Archivos: kebab-case user.controller.ts create-user.dto.ts user.service.ts user.entity.ts // Clases: PascalCase export class UserService {} export class CreateUserDto {} // Interfaces: PascalCase con prefijo I opcional export interface IUserRepository {} export interface User {} // Variables y funciones: camelCase const userId = '...'; function getUserById() {} // Constantes: UPPER_SNAKE_CASE const MAX_RETRY_ATTEMPTS = 3; const DEFAULT_PAGE_SIZE = 20; // Enums: PascalCase enum UserStatus { ACTIVE = 'active', SUSPENDED = 'suspended', } ``` ### Frontend (React/TypeScript) ```typescript // Componentes: PascalCase UserProfile.tsx AssetCard.tsx // Hooks: camelCase con prefijo use useAuth.ts useAssets.ts // Stores: camelCase authStore.ts assetsStore.ts // Utilidades: camelCase formatDate.ts validateEmail.ts ``` ### Base de Datos ```sql -- Tablas: snake_case, plural CREATE TABLE users ... CREATE TABLE generation_jobs ... -- Columnas: snake_case user_id created_at is_active -- Índices: idx_{tabla}_{columnas} idx_users_email idx_jobs_tenant_status -- Foreign keys: fk_{tabla}_{referencia} fk_users_tenant ``` --- ## Estructura de Módulos Backend ``` modules/crm/ ├── controllers/ │ ├── clients.controller.ts │ ├── brands.controller.ts │ └── products.controller.ts ├── services/ │ ├── clients.service.ts │ ├── brands.service.ts │ └── products.service.ts ├── entities/ │ ├── client.entity.ts │ ├── brand.entity.ts │ └── product.entity.ts ├── dto/ │ ├── create-client.dto.ts │ ├── update-client.dto.ts │ └── client-response.dto.ts ├── repositories/ │ └── clients.repository.ts ├── crm.module.ts └── index.ts ``` --- ## Patrones de Código ### Controllers ```typescript @Controller('crm/clients') @UseGuards(JwtAuthGuard) @ApiBearerAuth() @ApiTags('CRM - Clients') export class ClientsController { constructor(private readonly clientsService: ClientsService) {} @Post() @ApiOperation({ summary: 'Create a new client' }) @ApiResponse({ status: 201, type: ClientResponseDto }) async create( @CurrentTenant() tenantId: string, @Body() createClientDto: CreateClientDto, ): Promise { return this.clientsService.create(tenantId, createClientDto); } @Get() @ApiOperation({ summary: 'List all clients' }) async findAll( @CurrentTenant() tenantId: string, @Query() query: ListClientsQueryDto, ): Promise> { return this.clientsService.findAll(tenantId, query); } } ``` ### Services ```typescript @Injectable() export class ClientsService { constructor( private readonly clientsRepository: ClientsRepository, private readonly eventEmitter: EventEmitter2, ) {} async create(tenantId: string, dto: CreateClientDto): Promise { const client = await this.clientsRepository.create({ ...dto, tenantId, status: ClientStatus.PROSPECT, }); this.eventEmitter.emit('client.created', { client }); return client; } async findAll(tenantId: string, query: ListClientsQueryDto): Promise> { return this.clientsRepository.findAllPaginated(tenantId, query); } } ``` ### DTOs ```typescript export class CreateClientDto { @ApiProperty({ example: 'Acme Corp' }) @IsString() @MinLength(2) @MaxLength(255) name: string; @ApiPropertyOptional() @IsString() @IsOptional() legalName?: string; @ApiPropertyOptional({ enum: ClientSize }) @IsEnum(ClientSize) @IsOptional() size?: ClientSize; } ``` ### Entities ```typescript @Entity('clients', { schema: 'crm' }) export class Client extends BaseEntity { @PrimaryGeneratedColumn('uuid') id: string; @Column('uuid') tenantId: string; @Column({ length: 255 }) name: string; @Column({ type: 'enum', enum: ClientStatus, default: ClientStatus.PROSPECT }) status: ClientStatus; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; @OneToMany(() => Brand, (brand) => brand.client) brands: Brand[]; } ``` --- ## API Response Format ### Success Response ```json { "data": { ... }, "meta": { "timestamp": "2025-12-08T10:30:00Z" } } ``` ### Paginated Response ```json { "data": [...], "meta": { "total": 100, "page": 1, "limit": 20, "totalPages": 5 } } ``` ### Error Response ```json { "statusCode": 400, "message": "Validation failed", "errors": [ { "field": "email", "message": "Invalid email format" } ], "timestamp": "2025-12-08T10:30:00Z" } ``` --- ## Git Workflow ### Branches ``` main # Producción ├── develop # Desarrollo │ ├── feature/PMC-001-tenants │ ├── feature/PMC-002-crm │ ├── fix/login-validation │ └── refactor/auth-middleware ``` ### Commit Messages ``` feat(crm): add client creation endpoint fix(auth): resolve token refresh issue docs(api): update swagger documentation refactor(generation): extract queue service test(assets): add upload unit tests chore(deps): update dependencies ``` ### Pull Request Template ```markdown ## Description Brief description of changes ## Type of Change - [ ] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation ## Testing - [ ] Unit tests added/updated - [ ] Manual testing completed ## Checklist - [ ] Code follows conventions - [ ] Self-review completed - [ ] Documentation updated ``` --- ## Testing ### Unit Tests ```typescript describe('ClientsService', () => { let service: ClientsService; let repository: MockType; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [ ClientsService, { provide: ClientsRepository, useFactory: mockRepository }, ], }).compile(); service = module.get(ClientsService); repository = module.get(ClientsRepository); }); describe('create', () => { it('should create a client with prospect status', async () => { const dto = { name: 'Test Client' }; repository.create.mockResolvedValue({ id: '1', ...dto, status: 'prospect' }); const result = await service.create('tenant-1', dto); expect(result.status).toBe('prospect'); expect(repository.create).toHaveBeenCalledWith(expect.objectContaining({ tenantId: 'tenant-1', })); }); }); }); ``` ### E2E Tests ```typescript describe('Clients (e2e)', () => { let app: INestApplication; beforeAll(async () => { app = await createTestApp(); }); it('/crm/clients (POST)', () => { return request(app.getHttpServer()) .post('/crm/clients') .set('Authorization', `Bearer ${token}`) .send({ name: 'Test Client' }) .expect(201) .expect((res) => { expect(res.body.data.name).toBe('Test Client'); }); }); }); ``` --- ## Environment Variables ```bash # .env.example # App NODE_ENV=development PORT=3000 API_PREFIX=api/v1 # Database DATABASE_HOST=localhost DATABASE_PORT=5432 DATABASE_NAME=pmc DATABASE_USER=pmc_user DATABASE_PASSWORD=secret # Redis REDIS_HOST=localhost REDIS_PORT=6379 # JWT JWT_SECRET=your-secret-key JWT_EXPIRES_IN=1d # Storage S3_ENDPOINT=http://localhost:9000 S3_ACCESS_KEY=minioadmin S3_SECRET_KEY=minioadmin S3_BUCKET=pmc-assets # ComfyUI COMFYUI_URL=http://localhost:8188 COMFYUI_WEBHOOK_SECRET=webhook-secret # OpenAI (for text generation) OPENAI_API_KEY=sk-... ``` --- ## Referencias - [NestJS Documentation](https://docs.nestjs.com/) - [TypeORM Documentation](https://typeorm.io/) - [React Documentation](https://react.dev/) - [TailwindCSS](https://tailwindcss.com/) --- **Documento generado por:** Requirements-Analyst **Fecha:** 2025-12-08