Sistema NEXUS v3.4 migrado con: Estructura principal: - core/orchestration: Sistema SIMCO + CAPVED (27 directivas, 28 perfiles) - core/catalog: Catalogo de funcionalidades reutilizables - shared/knowledge-base: Base de conocimiento compartida - devtools/scripts: Herramientas de desarrollo - control-plane/registries: Control de servicios y CI/CD - orchestration/: Configuracion de orquestacion de agentes Proyectos incluidos (11): - gamilit (submodule -> GitHub) - trading-platform (OrbiquanTIA) - erp-suite con 5 verticales: - erp-core, construccion, vidrio-templado - mecanicas-diesel, retail, clinicas - betting-analytics - inmobiliaria-analytics - platform_marketing_content - pos-micro, erp-basico Configuracion: - .gitignore completo para Node.js/Python/Docker - gamilit como submodule (git@github.com:rckrdmrd/gamilit-workspace.git) - Sistema de puertos estandarizado (3005-3199) Generated with NEXUS v3.4 Migration System EPIC-010: Configuracion Git y Repositorios
16 KiB
ANTIPATRONES: Lo que NUNCA Hacer
Versión: 1.0.0 Fecha: 2025-12-08 Prioridad: OBLIGATORIA - Consultar antes de implementar Sistema: SIMCO + CAPVED
PROPÓSITO
Documentar antipatrones comunes para que agentes y subagentes los eviten. Cada antipatrón incluye el problema, por qué es malo, y la solución correcta.
1. ANTIPATRONES DE DATABASE
DB-001: Crear tabla sin schema
-- ❌ INCORRECTO
CREATE TABLE users (
id UUID PRIMARY KEY
);
-- ✅ CORRECTO
CREATE TABLE auth.users (
id UUID PRIMARY KEY
);
Por qué es malo: Sin schema, la tabla va a public, mezclando dominios y dificultando permisos.
DB-002: Columna NOT NULL sin DEFAULT en tabla existente
-- ❌ INCORRECTO (en tabla con datos)
ALTER TABLE users ADD COLUMN status VARCHAR(20) NOT NULL;
-- ✅ CORRECTO
ALTER TABLE users ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT 'active';
Por qué es malo: Falla en INSERT para registros existentes que no tienen valor.
DB-003: Foreign Key sin ON DELETE
-- ❌ INCORRECTO
CREATE TABLE orders (
user_id UUID REFERENCES users(id)
);
-- ✅ CORRECTO
CREATE TABLE orders (
user_id UUID REFERENCES users(id) ON DELETE CASCADE
-- o ON DELETE SET NULL, ON DELETE RESTRICT según el caso
);
Por qué es malo: Al eliminar usuario, los orders quedan huérfanos o el DELETE falla sin explicación clara.
DB-004: Usar TEXT cuando debería ser ENUM
-- ❌ INCORRECTO
CREATE TABLE users (
status TEXT -- Permite cualquier valor
);
-- ✅ CORRECTO
CREATE TYPE user_status AS ENUM ('active', 'inactive', 'suspended');
CREATE TABLE users (
status user_status DEFAULT 'active'
);
-- O con CHECK constraint
CREATE TABLE users (
status VARCHAR(20) CHECK (status IN ('active', 'inactive', 'suspended'))
);
Por qué es malo: TEXT permite valores inválidos, causando bugs silenciosos.
DB-005: Índice faltante en columna de búsqueda frecuente
-- ❌ INCORRECTO
CREATE TABLE products (
sku VARCHAR(50) UNIQUE -- Sin índice adicional para búsquedas
);
-- ✅ CORRECTO
CREATE TABLE products (
sku VARCHAR(50) UNIQUE
);
CREATE INDEX idx_products_sku ON products(sku);
-- UNIQUE ya crea índice, pero para búsquedas parciales:
CREATE INDEX idx_products_sku_pattern ON products(sku varchar_pattern_ops);
Por qué es malo: Queries lentas en tablas grandes (full table scan).
DB-006: Guardar contraseñas en texto plano
-- ❌ INCORRECTO
CREATE TABLE users (
password VARCHAR(100) -- Almacena "mipassword123"
);
-- ✅ CORRECTO
CREATE TABLE users (
password_hash VARCHAR(255) -- Almacena hash bcrypt
);
-- Hashing se hace en backend, no en SQL
Por qué es malo: Vulnerabilidad de seguridad crítica.
2. ANTIPATRONES DE BACKEND
BE-001: Lógica de negocio en Controller
// ❌ INCORRECTO
@Controller('orders')
export class OrderController {
@Post()
async create(@Body() dto: CreateOrderDto) {
// Lógica de negocio en controller
const total = dto.items.reduce((sum, item) => sum + item.price * item.qty, 0);
const tax = total * 0.16;
const discount = dto.couponCode ? await this.calculateDiscount() : 0;
// ... más lógica
}
}
// ✅ CORRECTO
@Controller('orders')
export class OrderController {
@Post()
async create(@Body() dto: CreateOrderDto) {
return this.orderService.create(dto); // Delega a service
}
}
@Injectable()
export class OrderService {
async create(dto: CreateOrderDto) {
const total = this.calculateTotal(dto.items);
const tax = this.calculateTax(total);
// Lógica de negocio en service
}
}
Por qué es malo: Controller difícil de testear, lógica no reutilizable, violación de SRP.
BE-002: Query directo en Service sin Repository
// ❌ INCORRECTO
@Injectable()
export class UserService {
async findActive() {
// Query directo con QueryBuilder
return this.connection.query(`
SELECT * FROM users WHERE status = 'active'
`);
}
}
// ✅ CORRECTO
@Injectable()
export class UserService {
constructor(
@InjectRepository(UserEntity)
private readonly repository: Repository<UserEntity>,
) {}
async findActive() {
return this.repository.find({
where: { status: UserStatus.ACTIVE },
});
}
}
Por qué es malo: No tipado, vulnerable a SQL injection, difícil de mantener.
BE-003: Validación en Service en lugar de DTO
// ❌ INCORRECTO
@Injectable()
export class UserService {
async create(dto: any) {
if (!dto.email) throw new BadRequestException('Email required');
if (!dto.email.includes('@')) throw new BadRequestException('Invalid email');
if (dto.name.length < 2) throw new BadRequestException('Name too short');
// ... más validaciones manuales
}
}
// ✅ CORRECTO
export class CreateUserDto {
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@MinLength(2)
name: string;
}
// Service recibe DTO ya validado
Por qué es malo: Validación duplicada, inconsistente, no documentada en Swagger.
BE-004: Catch vacío o silencioso
// ❌ INCORRECTO
async processPayment() {
try {
await this.paymentGateway.charge();
} catch (e) {
// Silencioso - error perdido
}
}
// ❌ TAMBIÉN INCORRECTO
async processPayment() {
try {
await this.paymentGateway.charge();
} catch (e) {
console.log(e); // Solo log, sin manejar
}
}
// ✅ CORRECTO
async processPayment() {
try {
await this.paymentGateway.charge();
} catch (error) {
this.logger.error('Payment failed', { error, context: 'payment' });
throw new InternalServerErrorException('Error procesando pago');
}
}
Por qué es malo: Errores silenciosos causan bugs imposibles de debuggear.
BE-005: Entity no alineada con DDL
// DDL tiene:
// status VARCHAR(20) CHECK (status IN ('active', 'inactive'))
// ❌ INCORRECTO
@Column()
status: string; // No valida valores
// ✅ CORRECTO
enum UserStatus {
ACTIVE = 'active',
INACTIVE = 'inactive',
}
@Column({ type: 'varchar', length: 20 })
status: UserStatus;
Por qué es malo: Entity permite valores que BD rechaza, errores en runtime.
BE-006: Hardcodear configuración
// ❌ INCORRECTO
const API_URL = 'https://api.stripe.com/v1';
const DB_HOST = '192.168.1.100';
// ✅ CORRECTO
const API_URL = this.configService.get('STRIPE_API_URL');
const DB_HOST = this.configService.get('DB_HOST');
// O usar decorador
@Injectable()
export class PaymentService {
constructor(
@Inject('STRIPE_CONFIG')
private readonly config: StripeConfig,
) {}
}
Por qué es malo: Difícil cambiar entre ambientes, secretos expuestos en código.
BE-007: N+1 Query Problem
// ❌ INCORRECTO
async getOrdersWithItems() {
const orders = await this.orderRepository.find();
for (const order of orders) {
order.items = await this.itemRepository.find({
where: { orderId: order.id }
});
}
return orders;
}
// Resultado: 1 query para orders + N queries para items
// ✅ CORRECTO
async getOrdersWithItems() {
return this.orderRepository.find({
relations: ['items'], // JOIN en una query
});
}
// Resultado: 1 query con JOIN
Por qué es malo: Performance terrible en listas grandes (100 orders = 101 queries).
3. ANTIPATRONES DE FRONTEND
FE-001: Fetch directo en componente
// ❌ INCORRECTO
const UserProfile = () => {
const [user, setUser] = useState(null);
useEffect(() => {
fetch('/api/users/me')
.then(res => res.json())
.then(setUser);
}, []);
return <div>{user?.name}</div>;
};
// ✅ CORRECTO
// hooks/useUser.ts
const useUser = () => {
return useQuery({
queryKey: ['user', 'me'],
queryFn: () => userService.getMe(),
});
};
// components/UserProfile.tsx
const UserProfile = () => {
const { data: user, isLoading, error } = useUser();
if (isLoading) return <Loading />;
if (error) return <Error error={error} />;
return <div>{user.name}</div>;
};
Por qué es malo: No cachea, no maneja loading/error, lógica no reutilizable.
FE-002: Hardcodear URLs de API
// ❌ INCORRECTO
const response = await fetch('http://localhost:3000/api/users');
// ✅ CORRECTO
// config.ts
export const API_URL = import.meta.env.VITE_API_URL;
// services/api.ts
const api = axios.create({
baseURL: API_URL,
});
Por qué es malo: Rompe en producción, difícil cambiar backend.
FE-003: Estado global para todo
// ❌ INCORRECTO - Redux/Zustand para estado de formulario
const formStore = create((set) => ({
name: '',
email: '',
setName: (name) => set({ name }),
setEmail: (email) => set({ email }),
}));
// ✅ CORRECTO - Estado local para formularios
const UserForm = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
// O usar react-hook-form
};
// Store global SOLO para:
// - Auth state (usuario logueado)
// - UI state (tema, sidebar abierto)
// - Cache de server state (mejor usar React Query)
Por qué es malo: Complejidad innecesaria, renders innecesarios, difícil de seguir.
FE-004: Props drilling excesivo
// ❌ INCORRECTO
<App user={user} theme={theme}>
<Layout user={user} theme={theme}>
<Sidebar user={user} theme={theme}>
<UserMenu user={user} theme={theme}>
<Avatar user={user} /> // Finalmente se usa
</UserMenu>
</Sidebar>
</Layout>
</App>
// ✅ CORRECTO - Context para datos compartidos
const UserContext = createContext<User | null>(null);
const useUser = () => useContext(UserContext);
<UserProvider value={user}>
<App>
<Layout>
<Sidebar>
<UserMenu>
<Avatar /> // Usa useUser() internamente
</UserMenu>
</Sidebar>
</Layout>
</App>
</UserProvider>
Por qué es malo: Componentes intermedios reciben props que no usan, refactoring doloroso.
FE-005: useEffect para todo
// ❌ INCORRECTO
const ProductList = ({ categoryId }) => {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(`/api/products?category=${categoryId}`)
.then(res => res.json())
.then(data => {
setProducts(data);
setLoading(false);
});
}, [categoryId]);
// Race conditions, no cleanup, no error handling
};
// ✅ CORRECTO - React Query
const ProductList = ({ categoryId }) => {
const { data: products, isLoading } = useQuery({
queryKey: ['products', categoryId],
queryFn: () => productService.getByCategory(categoryId),
});
// Maneja cache, loading, error, race conditions automáticamente
};
Por qué es malo: Race conditions, memory leaks, re-inventar la rueda.
FE-006: Tipos no sincronizados con backend
// Backend DTO (actualizado)
class UserDto {
id: string;
email: string;
name: string;
phone: string; // ← NUEVO CAMPO
}
// ❌ INCORRECTO - Frontend desactualizado
interface User {
id: string;
email: string;
name: string;
// Falta phone → undefined en runtime
}
// ✅ CORRECTO - Mantener sincronizado
interface User {
id: string;
email: string;
name: string;
phone?: string; // Sincronizado con backend
}
Por qué es malo: Bugs silenciosos en runtime cuando backend cambia.
4. ANTIPATRONES DE ARQUITECTURA
ARCH-001: Importar de capa incorrecta
// ❌ INCORRECTO - Controller importa de otro módulo directamente
import { ProductEntity } from '../products/entities/product.entity';
// ✅ CORRECTO - Usar exports del módulo
import { ProductService } from '../products/product.module';
// O mejor: dependency injection
@Module({
imports: [ProductModule],
})
export class OrderModule {}
Por qué es malo: Acopla módulos, rompe encapsulamiento, dependencias circulares.
ARCH-002: Duplicar código entre módulos
// ❌ INCORRECTO - Misma función en dos módulos
// users/utils.ts
function formatDate(date: Date) { ... }
// orders/utils.ts
function formatDate(date: Date) { ... } // Duplicado
// ✅ CORRECTO - Shared utils
// shared/utils/date.utils.ts
export function formatDate(date: Date) { ... }
Por qué es malo: Cambios deben hacerse en múltiples lugares, inconsistencias.
ARCH-003: Circular dependencies
// ❌ INCORRECTO
// user.service.ts
import { OrderService } from '../orders/order.service';
// order.service.ts
import { UserService } from '../users/user.service';
// ✅ CORRECTO - Usar forwardRef o reestructurar
@Injectable()
export class UserService {
constructor(
@Inject(forwardRef(() => OrderService))
private orderService: OrderService,
) {}
}
// O mejor: Extraer lógica compartida a un tercer servicio
Por qué es malo: Errores en runtime, código difícil de entender.
5. ANTIPATRONES DE TESTING
TEST-001: Tests que dependen de orden
// ❌ INCORRECTO
describe('UserService', () => {
it('should create user', () => {
const user = service.create({ email: 'test@test.com' });
// Asume que este test corre primero
});
it('should find user', () => {
const user = service.findByEmail('test@test.com');
// Depende del test anterior
});
});
// ✅ CORRECTO - Tests independientes
describe('UserService', () => {
beforeEach(async () => {
// Setup limpio para cada test
await repository.clear();
});
it('should create user', () => { ... });
it('should find user', async () => {
// Crear datos necesarios en el test
await repository.save({ email: 'test@test.com' });
const user = await service.findByEmail('test@test.com');
});
});
TEST-002: Mock incorrecto
// ❌ INCORRECTO - Mock parcial/inconsistente
jest.mock('./user.service', () => ({
findOne: jest.fn().mockResolvedValue({ id: '1' }),
// Otros métodos no mockeados → undefined
}));
// ✅ CORRECTO - Mock completo
const mockUserService = {
findOne: jest.fn(),
findAll: jest.fn(),
create: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
};
beforeEach(() => {
jest.clearAllMocks();
});
6. CHECKLIST DE REVISIÓN
Antes de hacer commit, verificar que NO estás haciendo:
Database:
[ ] Tabla sin schema
[ ] NOT NULL sin DEFAULT en tabla existente
[ ] FK sin ON DELETE
[ ] TEXT donde debería ser ENUM
[ ] Índices faltantes
Backend:
[ ] Lógica en Controller
[ ] Queries directas sin Repository
[ ] Validación en Service
[ ] Catch vacío
[ ] Entity desalineada con DDL
[ ] Config hardcodeada
[ ] N+1 queries
Frontend:
[ ] Fetch en componente
[ ] URLs hardcodeadas
[ ] Store global para estado local
[ ] Props drilling excesivo
[ ] useEffect innecesario
[ ] Tipos desactualizados
Arquitectura:
[ ] Import de capa incorrecta
[ ] Código duplicado
[ ] Dependencias circulares
Testing:
[ ] Tests dependientes
[ ] Mocks incompletos
Versión: 1.0.0 | Sistema: SIMCO | Tipo: Guía de Antipatrones