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
# 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)
// 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)
// 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)
// 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
-- DDL
ALTER TABLE products ALTER COLUMN price TYPE DECIMAL(10,2);
// Entity ANTES
@Column({ type: 'integer' })
price: number;
// Entity DESPUES
@Column({ type: 'decimal', precision: 10, scale: 2 })
price: string; // String para precision
// CreateDto ANTES
@IsNumber()
price: number;
// CreateDto DESPUES
@IsNumberString()
@Matches(/^\d+(\.\d{1,2})?$/)
price: string;
// 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
# 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