# IMPACTO DE CAMBIOS EN ENTITY **Version:** 1.0.0 **Fecha:** 2025-12-08 **Prioridad:** OBLIGATORIA - Consultar antes de modificar Entities **Sistema:** SIMCO + CAPVED --- ## PROPOSITO Documentar especificamente los impactos de modificar Entities de TypeORM, que son el puente entre la base de datos y la logica de negocio. --- ## 1. PRINCIPIO FUNDAMENTAL ``` ╔══════════════════════════════════════════════════════════════════════╗ ║ ENTITY ES REFLEJO DE DDL, NO AL REVES ║ ║ ║ ║ DDL (fuente) ──────▶ Entity (reflejo) ──────▶ DTO (contrato API) ║ ║ ║ ║ ⚠️ NUNCA crear Entity sin DDL existente ║ ║ ⚠️ NUNCA modificar Entity sin verificar DDL primero ║ ╚══════════════════════════════════════════════════════════════════════╝ ``` --- ## 2. TIPOS DE CAMBIOS EN ENTITY ### 2.1 Cambios de Columna | Tipo de Cambio | Impacto DDL | Impacto DTO | Impacto Service | Impacto Frontend | |----------------|-------------|-------------|-----------------|------------------| | Agregar campo | SI | SI | Posible | SI | | Eliminar campo | SI | SI | Verificar | SI | | Renombrar campo | SI | SI | SI | SI | | Cambiar tipo | SI | SI | Posible | SI | | Cambiar nullable | SI | SI | Posible | SI | | Agregar default | SI | Posible | NO | NO | ### 2.2 Cambios de Relacion | Tipo de Cambio | Impacto DDL | Impacto DTO | Impacto Service | Impacto Frontend | |----------------|-------------|-------------|-----------------|------------------| | Agregar ManyToOne | SI (FK) | SI | SI | SI | | Agregar OneToMany | NO | SI | SI | SI | | Agregar ManyToMany | SI (junction) | SI | SI | SI | | Eliminar relacion | SI | SI | SI | SI | | Cambiar cascade | NO | NO | Verificar | NO | ### 2.3 Cambios de Indice/Constraint | Tipo de Cambio | Impacto DDL | Impacto DTO | Impacto Service | Impacto Frontend | |----------------|-------------|-------------|-----------------|------------------| | Agregar indice | SI | NO | NO | NO | | Agregar unique | SI | NO | Manejar error | Manejar error | | Agregar check | SI | NO | Manejar error | Manejar error | --- ## 3. AGREGAR CAMPO A ENTITY ### Flujo Completo ``` ┌─────────────────────────────────────────────────────────────────────┐ │ PASO 1: DDL (Database-Agent) │ ├─────────────────────────────────────────────────────────────────────┤ │ ALTER TABLE auth.users ADD COLUMN phone VARCHAR(20); │ │ │ │ Validar: ./recreate-database.sh && psql -c "\d auth.users" │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ PASO 2: Entity (Backend-Agent) │ ├─────────────────────────────────────────────────────────────────────┤ │ @Column({ type: 'varchar', length: 20, nullable: true }) │ │ phone: string | null; │ │ │ │ Validar: npm run build │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ PASO 3: DTOs (Backend-Agent) │ ├─────────────────────────────────────────────────────────────────────┤ │ CreateUserDto: │ │ @IsOptional() │ │ @IsString() │ │ @MaxLength(20) │ │ phone?: string; │ │ │ │ UserResponseDto: │ │ @ApiProperty({ required: false }) │ │ phone: string | null; │ │ │ │ Validar: npm run build && npm run lint │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ PASO 4: Frontend (Frontend-Agent) │ ├─────────────────────────────────────────────────────────────────────┤ │ user.types.ts: │ │ interface User { │ │ phone: string | null; │ │ } │ │ │ │ user.schema.ts: │ │ phone: z.string().max(20).optional() │ │ │ │ Validar: npm run build && npm run typecheck │ └─────────────────────────────────────────────────────────────────────┘ ``` ### Checklist Agregar Campo ``` [ ] 1. DDL tiene la columna con tipo correcto [ ] 2. Entity @Column refleja DDL (tipo, length, nullable) [ ] 3. CreateDto tiene validaciones apropiadas [ ] 4. UpdateDto hereda de CreateDto (automatico con PartialType) [ ] 5. ResponseDto tiene el campo con ApiProperty [ ] 6. Frontend interface tiene el campo [ ] 7. Frontend Zod schema tiene validacion equivalente [ ] 8. Swagger documenta correctamente [ ] 9. Build pasa en backend [ ] 10. Build y typecheck pasan en frontend ``` --- ## 4. ELIMINAR CAMPO DE ENTITY ### ADVERTENCIA ``` ⚠️ ELIMINAR CAMPO ES OPERACION DESTRUCTIVA ANTES DE ELIMINAR: 1. Verificar que no hay datos importantes 2. Buscar TODAS las referencias en codigo 3. Considerar deprecar primero (nullable + ignorar) 4. Planear migracion de datos si necesario ``` ### Flujo de Eliminacion ```bash # PASO 0: Buscar todas las referencias grep -rn "fieldName" src/ grep -rn "fieldName" apps/ grep -rn "field_name" db/ # snake_case en DDL # PASO 1: Eliminar uso en Frontend primero (evita errores de tipo) # PASO 2: Eliminar uso en Service/Controller # PASO 3: Eliminar de DTOs # PASO 4: Eliminar de Entity # PASO 5: Eliminar de DDL (ultimo porque es irreversible) ``` ### Checklist Eliminar Campo ``` [ ] 1. Buscar referencias en backend: grep -rn "field" src/ [ ] 2. Buscar referencias en frontend: grep -rn "field" apps/ [ ] 3. Eliminar de componentes Frontend [ ] 4. Eliminar de types/interfaces Frontend [ ] 5. Eliminar de Zod schemas Frontend [ ] 6. Eliminar de Service (si hay logica) [ ] 7. Eliminar de ResponseDto [ ] 8. Eliminar de CreateDto [ ] 9. Eliminar de Entity [ ] 10. Eliminar de DDL (con migracion) [ ] 11. Build pasa en todos los proyectos ``` --- ## 5. AGREGAR RELACION A ENTITY ### 5.1 ManyToOne (FK hacia otra tabla) ```typescript // Entity: Order tiene un User @ManyToOne(() => UserEntity, { nullable: false }) @JoinColumn({ name: 'user_id' }) user: UserEntity; // Columna FK explicita (opcional pero recomendado) @Column({ type: 'uuid' }) userId: string; ``` **Impacto:** | Capa | Archivo | Cambio Requerido | |------|---------|------------------| | DDL | `XX-orders.sql` | `user_id UUID REFERENCES auth.users(id)` | | Entity | `order.entity.ts` | `@ManyToOne` + `@JoinColumn` | | CreateDto | `create-order.dto.ts` | `@IsUUID() userId: string;` | | ResponseDto | `order-response.dto.ts` | `userId: string;` o `user: UserResponseDto;` | | Service | `order.service.ts` | Decidir: cargar relacion o no | | Frontend | `order.types.ts` | `userId: string;` o `user: User;` | ### 5.2 OneToMany (Coleccion inversa) ```typescript // Entity: User tiene muchos Orders @OneToMany(() => OrderEntity, order => order.user) orders: OrderEntity[]; ``` **Impacto:** | Capa | Archivo | Cambio Requerido | |------|---------|------------------| | DDL | N/A | Sin cambio (FK esta en orders) | | Entity | `user.entity.ts` | `@OneToMany` | | ResponseDto | `user-response.dto.ts` | Decidir: incluir o no | | Service | `user.service.ts` | Decidir: cargar relacion o no | | Frontend | `user.types.ts` | `orders?: Order[];` | ### 5.3 ManyToMany (Tabla junction) ```typescript // Entity: User tiene muchos Roles @ManyToMany(() => RoleEntity) @JoinTable({ name: 'user_roles', joinColumn: { name: 'user_id' }, inverseJoinColumn: { name: 'role_id' }, }) roles: RoleEntity[]; ``` **Impacto:** | Capa | Archivo | Cambio Requerido | |------|---------|------------------| | DDL | `XX-user-roles.sql` | Crear tabla junction | | Entity | `user.entity.ts` | `@ManyToMany` + `@JoinTable` | | ResponseDto | `user-response.dto.ts` | `roles: RoleResponseDto[];` | | Service | `user.service.ts` | Logica para asignar/remover roles | | Controller | `user.controller.ts` | Endpoints para manejar roles | | Frontend | `user.types.ts` | `roles: Role[];` | | Frontend | `useUserRoles.ts` | Hook para manejar roles | ### Checklist Agregar Relacion ``` [ ] 1. DDL tiene FK o tabla junction [ ] 2. Entity tiene decorador de relacion correcto [ ] 3. Entity relacionada tiene relacion inversa (si necesario) [ ] 4. CreateDto acepta ID de relacion [ ] 5. ResponseDto decide: incluir objeto o solo ID [ ] 6. Service decide: eager loading o lazy [ ] 7. Frontend types reflejan estructura [ ] 8. Frontend tiene logica para manejar relacion ``` --- ## 6. CAMBIAR TIPO DE CAMPO ### Mapeo de Cambios Comunes | Cambio | DDL | Entity | DTO | Frontend | |--------|-----|--------|-----|----------| | `INT` → `BIGINT` | ALTER TYPE | `number` (sin cambio) | Sin cambio | Sin cambio | | `VARCHAR` → `TEXT` | ALTER TYPE | Sin cambio | Ajustar MaxLength | Ajustar max | | `INTEGER` → `DECIMAL` | ALTER TYPE | `number` → `string` | Ajustar validador | Ajustar tipo | | `BOOLEAN` → `ENUM` | ALTER TYPE | `boolean` → `enum` | Cambiar validador | Cambiar tipo | | `VARCHAR` → `UUID` | ALTER TYPE + datos | `string` (sin cambio) | Agregar @IsUUID | Agregar validacion | ### Ejemplo: INTEGER a DECIMAL ```sql -- DDL ALTER TABLE products ALTER COLUMN price TYPE DECIMAL(10,2); ``` ```typescript // Entity ANTES @Column({ type: 'integer' }) price: number; // Entity DESPUES @Column({ type: 'decimal', precision: 10, scale: 2 }) price: string; // String para precision ``` ```typescript // CreateDto ANTES @IsNumber() price: number; // CreateDto DESPUES @IsNumberString() @Matches(/^\d+(\.\d{1,2})?$/) price: string; ``` ```typescript // Frontend ANTES interface Product { price: number; } // Frontend DESPUES interface Product { price: string; } // Uso en componente const displayPrice = parseFloat(product.price).toFixed(2); ``` --- ## 7. PATRON DE MIGRACION SEGURA ### Para cambios que pueden romper ``` FASE 1: Agregar nuevo (sin eliminar viejo) ═══════════════════════════════════════════════════════════════ 1. Agregar nuevo campo/columna junto al existente 2. Actualizar codigo para escribir en ambos 3. Deploy backend FASE 2: Migrar datos ═══════════════════════════════════════════════════════════════ 1. Script para copiar datos de viejo a nuevo 2. Verificar integridad FASE 3: Cambiar lecturas ═══════════════════════════════════════════════════════════════ 1. Actualizar codigo para leer de nuevo 2. Deploy backend + frontend FASE 4: Eliminar viejo ═══════════════════════════════════════════════════════════════ 1. Eliminar campo viejo de codigo 2. Eliminar columna de DDL 3. Deploy final ``` --- ## 8. DECORADORES ENTITY Y SU IMPACTO ### @Column Options | Option | Impacto si cambia | Requiere DDL | |--------|-------------------|--------------| | `type` | Tipo TS, validacion DTO | SI | | `length` | Validacion DTO | SI | | `nullable` | Tipo TS (null), validacion | SI | | `default` | Posible logica Service | SI | | `unique` | Manejo error Service | SI | | `name` | Ninguno (mapeo interno) | SI | | `precision/scale` | Tipo TS, formateo FE | SI | ### @Index | Cambio | Impacto | |--------|---------| | Agregar | Solo performance, DDL | | Eliminar | Solo performance, DDL | | Cambiar columnas | Solo performance, DDL | ### Relation Options | Option | Impacto si cambia | |--------|-------------------| | `eager: true` | Queries cargan automatico | | `cascade: true` | Operaciones se propagan | | `onDelete` | Comportamiento al eliminar padre | | `nullable` | Validacion, tipo TS | --- ## 9. COMANDOS DE VERIFICACION ```bash # Ver diferencias entre DDL y Entity # (manual: comparar columnas) psql -d mydb -c "\d schema.table" cat src/modules/x/entities/x.entity.ts # Verificar que Entity compila npm run build # Verificar que no hay referencias rotas npm run lint # Verificar sincronizacion con frontend npm run typecheck --prefix apps/web # Buscar uso de campo especifico grep -rn "fieldName" src/ apps/ ``` --- ## 10. MATRIZ DE DECISION ``` ¿Que tipo de cambio en Entity? ┌─────────────────────┐ │ Agregar campo │──▶ DDL primero → Entity → DTO → Frontend └─────────────────────┘ ┌─────────────────────┐ │ Eliminar campo │──▶ Frontend primero → Service → DTO → Entity → DDL └─────────────────────┘ ┌─────────────────────┐ │ Cambiar tipo │──▶ DDL primero → Entity → DTO → Frontend └─────────────────────┘ ┌─────────────────────┐ │ Agregar relacion │──▶ DDL (FK) → Entity → DTO → Service → Frontend └─────────────────────┘ ┌─────────────────────┐ │ Renombrar campo │──▶ Migracion segura (agregar nuevo → migrar → eliminar viejo) └─────────────────────┘ ``` --- **Version:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Guia de Impacto