Estándares y Reglas de Desarrollo
Este documento describe las reglas y estándares de desarrollo que deben seguirse en el proyecto UnoSportClub. Estas reglas garantizan la consistencia, calidad y mantenibilidad del código.
Principios Fundamentales
Convenciones de Nomenclatura
-
Código en inglés: Todo el código debe estar escrito en inglés
-
Funciones, atributos y variables: Usar
CamelCase -
Clases, Enums e Interfaces: Usar
PascalCase -
Rutas URL: Usar
kebab-case -
Constantes: Usar
UPPER_SNAKE_CASE -
Archivos y directorios: Preferir una sola palabra cuando sea posible (ej: "birthday" en lugar de "dateOfBirth")
Principios de Diseño
-
KISS (Keep It Simple, Stupid): Mantén el código simple y directo
-
Principio de Rubik: Divide problemas complejos en partes más pequeñas y simples
-
Una exportación por archivo: Cada archivo debe exportar un solo elemento principal
-
Sin líneas en blanco dentro de funciones: Mantén las funciones compactas
-
Sin comentarios explicativos: El código debe ser autoexplicativo; no uses comentarios para enseñar
TypeScript y Angular
TypeScript
Type Checking Estricto
Siempre habilita y respeta el type checking estricto. Esto ayuda a detectar errores tempranamente.
Preferir Inferencia de Tipos
Permite que TypeScript infiera los tipos cuando son obvios del contexto:
// ❌ INCORRECTO
let name: string = 'Angular';
// ✅ CORRECTO
let name = 'Angular';
Evitar any
No uses el tipo any a menos que sea absolutamente necesario. Si conoces la estructura de datos, usa la interfaz correspondiente. Para respuestas HTTP, usa HttpResponse o ErrorResponse, o la interfaz específica del endpoint según la documentación Swagger.
// ❌ INCORRECTO
const data: any = response;
// ✅ CORRECTO
interface UserResponse {
id: number;
name: string;
email: string;
}
const data: UserResponse = response;
Angular - Componentes Standalone
Componentes Standalone (Obligatorio)
Siempre usa componentes standalone. No necesitas especificar standalone: true explícitamente, está implícito por defecto.
// ✅ CORRECTO
@Component({
selector: 'app-user-card',
template: `<div>{{ user().name }}</div>`,
imports: [CommonModule]
})
export class UserCardComponent {
user = input.required<IUser>();
}
Signals para Gestión de Estado
Utiliza Angular Signals para la gestión de estado reactivo:
// ✅ CORRECTO
private count = signal(0);
private name = signal('');
private users = signal<IUser[]>([]);
// Computed signals
protected doubleCount = computed(() => this.count() * 2);
// Actualizar signals
increaseCount() {
this.count.update(c => c + 1);
// O
this.count.set(this.count() + 1);
}
// ❌ NUNCA usar mutate
this.users.mutate(arr => arr.push(newUser)); // ❌ INCORRECTO
this.users.update(arr => [...arr, newUser]); // ✅ CORRECTO
Input/Output Functions
Prefiere las funciones input() y output() sobre los decoradores @Input() y @Output():
// ❌ ANTIGUO
@Input() userId!: string;
@Output() userSelected = new EventEmitter<string>();
// ✅ NUEVO
import { input, output } from '@angular/core';
userId = input<string>('');
userSelected = output<string>();
Host Bindings
NO uses los decoradores @HostBinding y @HostListener. Coloca los host bindings dentro del objeto host del decorador:
// ✅ CORRECTO
@Component({
selector: 'app-button',
host: {
'[class.active]': 'isActive()',
'[class.disabled]': '!enabled()',
'(click)': 'onClick()'
}
})
export class ButtonComponent {
isActive = input(false);
enabled = input(true);
}
Control Flow Nativo
Usa el nuevo control flow nativo de Angular en lugar de las directivas estructurales:
<!-- ❌ ANTIGUO -->
<div *ngIf="isLoading">Loading...</div>
<div *ngFor="let user of users; trackBy: trackByUserId">
{{ user.name }}
</div>
<!-- ✅ NUEVO -->
@if (isLoading()) {
<div>Loading...</div>
}
@for (user of users(); track user.id) {
<div>{{ user.name }}</div>
}
Forms Reactivos
Prefiere Reactive Forms sobre Template-driven forms:
// ✅ CORRECTO
export class LoginComponent {
private fb = inject(FormBuilder);
loginForm = new FormGroup({
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormControl('', [Validators.required, Validators.minLength(8)])
});
}
Binding de Estilos y Clases
Usa bindings nativos en lugar de ngClass y ngStyle:
<!-- ✅ CORRECTO -->
<div
[class.active]="isActive()"
[class.disabled]="!enabled()"
[style.color]="color()"
[style.font-size.px]="fontSize()"
>
<!-- ❌ INCORRECTO -->
<div [ngClass]="{ active: isActive(), disabled: !enabled() }">
<div [ngStyle]="{ color: color(), fontSize: fontSize() }">
Services y Dependency Injection
Principios SOLID
Single Responsibility (Responsabilidad Única)
Cada clase y función debe tener una única razón clara para cambiar. Divide clases o funciones complejas en partes más pequeñas y enfocadas.
// ❌ INCORRECTO - Múltiples responsabilidades
export class UserComponent {
users: IUser[] = [];
async loadUsers() { /* ... */ }
async saveUser(user: IUser) { /* ... */ }
validateUserPermission() { /* ... */ }
sendNotification() { /* ... */ }
}
// ✅ CORRECTO - Separar responsabilidades
@Component({ /* ... */ })
export class UserListComponent {
users = toSignal(this.userService.getUsers());
}
@Injectable({ providedIn: 'root' })
export class UserService {
getUsers() {
return this.http.get<IUser[]>('/api/users');
}
}
Open/Closed Principle
Las clases deben estar abiertas para extensión pero cerradas para modificación:
// ✅ CORRECTO - Abierto a extensión
abstract class PaymentStrategy {
abstract process(amount: number): Promise<void>;
}
class CreditCardPayment extends PaymentStrategy {
async process(amount: number) { /* ... */ }
}
// Nueva estrategia sin modificar existentes
class PayPalPayment extends PaymentStrategy {
async process(amount: number) { /* ... */ }
}
Liskov Substitution
Los tipos derivados deben ser perfectamente sustituibles por sus tipos base:
// ✅ CORRECTO
abstract class Animal {
abstract makeSound(): string;
}
class Dog extends Animal {
makeSound() { return 'Woof'; }
}
class Cat extends Animal {
makeSound() { return 'Meow'; }
}
// Ambos pueden usarse de forma intercambiable
function makeAnimalSound(animal: Animal) {
console.log(animal.makeSound());
}
Interface Segregation
Prefiere interfaces pequeñas y específicas sobre una interfaz grande:
// ✅ CORRECTO
interface Readable {
read(): string;
}
interface Writable {
write(data: string): void;
}
interface FileHandler extends Readable, Writable {}
// ❌ INCORRECTO
interface FileOperations {
read(): string;
write(data: string): void;
delete(): void;
compress(): void;
encrypt(): void;
}
Dependency Inversion
Depende de abstracciones, no de implementaciones concretas:
// ✅ CORRECTO
interface IUserRepository {
findById(id: number): Promise<IUser>;
save(user: IUser): Promise<void>;
}
class FirebaseUserRepository implements IUserRepository {
async findById(id: number) { /* Firebase logic */ }
async save(user: IUser) { /* Firebase logic */ }
}
class UserService {
constructor(private repo: IUserRepository) {}
async getUser(id: number) {
return this.repo.findById(id);
}
}
Seguridad
OWASP Top 10
Sigue las recomendaciones del estándar OWASP Top 10:
-
Validación de Entrada: Valida y sanitiza todas las entradas del usuario, tanto del lado cliente como en el servidor
-
Protección contra Inyección: Usa consultas parametrizadas y validaciones estrictas
-
Autenticación y Autorización: Implementa controles robustos de autenticación y gestión de sesiones
-
Principio de Mínimo Privilegio: Concede solo los permisos necesarios
-
Protección IDOR: Restringe el acceso directo a objetos basados en referencias
-
Manejo de Errores: Gestiona correctamente los errores sin exponer detalles internos
-
Control de Acceso: Emplea controles estrictos sobre permisos y roles
-
HTTPS: Usa HTTPS en todas las comunicaciones
-
Almacenamiento Seguro: Almacena credenciales y datos sensibles de forma segura
-
Protección de APIs: Implementa autenticación, autorización y rate limiting
Angular Security
-
Sanitización HTML: Siempre sanitiza HTML no confiable usando la sanitización de Angular, especialmente con
[innerHTML] -
No exponer secretos: No expongas API keys, secrets o configuración sensible en el frontend
-
HttpClient: Usa HttpClient para todas las peticiones HTTP
-
Route Guards: Aplica guards de ruta para autenticación y autorización
-
CSP: Usa una política de seguridad de contenido estricta en producción
-
Validación del servidor: Nunca confíes solo en la validación del cliente; siempre valida en el servidor
Express.js Security
-
Orden de Middleware: Body parsers, middleware personalizado, rutas, manejadores de errores
-
Async/Await: Usa async/await con manejo adecuado de errores
-
Validación: Implementa validación de requests usando express-validator
-
Middleware de Autenticación: Usa middleware para autenticación y autorización
-
Códigos HTTP: Usa códigos de estado HTTP apropiados
Calidad de Código
Verificación Antes de Completar
IMPORTANTE: NO DAR POR TERMINADO SIN VERIFICAR ERRORES
-
✅ SIEMPRE ejecutar
read_lintsen los archivos modificados -
✅ SIEMPRE verificar que no haya warnings de componentes no usados
-
✅ SIEMPRE verificar que no haya errores de TypeScript
-
✅ SIEMPRE verificar que los imports sean correctos
-
✅ SIEMPRE verificar que los templates usen los componentes importados
No Delays en el Código
IMPORTANTE: NO HAY DELAY EN EL CÓDIGO
-
❌ NUNCA usar
setTimeout(),setInterval(),timer(), o cualquier método de delay artificial -
❌ NUNCA deshabilitar botones con estados de carga artificiales
-
❌ NUNCA usar signals de
loadingpara crear estados de espera artificiales
Filosofía: * Si algo va a tardar, tardará naturalmente (por ejemplo, una petición HTTP) * Si algo va antes o después, hay que observar al elemento correcto cuando cambie * Los ganadores no esperan a que las cosas sucedan, reaccionan cuando suceden
// ❌ INCORRECTO - Delay artificial
setTimeout(() => {
this.router.navigate(['/']);
}, 1000);
// ✅ CORRECTO - Reaccionar cuando el observable emite
this.authService.login(email, password).subscribe({
next: () => {
this.router.navigate(['/']); // Navega inmediatamente cuando el login es exitoso
}
});
Checklist de Code Review
Antes de enviar código, verifica:
-
✅ No hay
console.log()en código de producción -
✅
console.error()yconsole.warn()están permitidos cuando son necesarios -
✅ No hay comentarios TODO o FIXME sin referencia a issue
-
✅ Todas las funciones tienen comentarios JSDoc
-
✅ Todos los tipos están correctamente definidos (no
any) -
✅ Hay manejo de errores para todas las operaciones asíncronas
-
✅ No hay valores hardcodeados o números mágicos
-
✅ No hay código duplicado (principio DRY)
-
✅ El código es modular y testeable
Testing
Framework de Testing
-
Angular: Usa Karma y Jasmine (estándar de Angular)
-
Firebase Functions: Usa Jest
-
NO improvises: No uses frameworks fuera del estándar sin justificación
Express.js
Orden de Middleware
// ✅ CORRECTO - Orden específico
const app = express();
// 1. Body parsers
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 2. Custom middleware
app.use(corsMiddleware);
app.use(authMiddleware);
// 3. Routes
app.use('/api/users', userRoutes);
app.use('/api/products', productRoutes);
// 4. Error handler (ÚLTIMO)
app.use(errorHandlerMiddleware);