🎨 Patrones de Diseño
Visión General
PuntoHack utiliza varios patrones de diseño para mantener el código organizado, mantenible y escalable.
Patrones Implementados
1. Singleton Pattern
Uso: Prisma Client
Implementación:
// src/core/db.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const db =
globalForPrisma.prisma ??
new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'],
});
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db;
Razón: Evitar múltiples instancias de Prisma Client en desarrollo.
2. Repository Pattern
Uso: Módulos de dominio
Implementación:
// modules/hackathons/queries.ts
export async function getHackathonById(id: string) {
return db.hackathon.findUnique({ where: { id } });
}
export async function listHackathons(filters: Filters) {
return db.hackathon.findMany({ where: filters });
}
Razón: Separar lógica de acceso a datos de la lógica de negocio.
3. Strategy Pattern
Uso: Sistema RBAC
Implementación:
// src/core/rbac.ts
export function hasRole(user: User, roles: Role[]): boolean {
return roles.includes(user.profile.role);
}
export function requireRole(user: User, roles: Role[]): void {
if (!hasRole(user, roles)) {
throw new Error('Unauthorized: Insufficient permissions');
}
}
Razón: Estrategia flexible para verificar permisos.
4. Factory Pattern
Uso: Generación de códigos de invitación
Implementación:
// modules/teams/actions.ts
function generateInviteCode(): string {
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // Sin 0, O, I, 1
let code = '';
for (let i = 0; i < 6; i++) {
code += chars.charAt(Math.floor(Math.random() * chars.length));
}
return code;
}
Razón: Centralizar lógica de generación.
5. Observer Pattern
Uso: Supabase Realtime
Implementación:
// src/lib/realtime/hooks/use-hackathons-realtime.ts
useEffect(() => {
const channel = supabase
.channel('hackathons-changes')
.on('postgres_changes', {
event: '*',
schema: 'public',
table: 'Hackathon',
}, (payload) => {
// Manejar cambios
})
.subscribe();
return () => {
supabase.removeChannel(channel);
};
}, []);
Razón: Notificar cambios en tiempo real.
6. Command Pattern
Uso: Server Actions
Implementación:
// modules/hackathons/actions.ts
export async function createHackathon(data: CreateHackathonInput) {
// 1. Auth
// 2. RBAC
// 3. Validate
// 4. Execute
// 5. Revalidate
// 6. Return
}
Razón: Encapsular operaciones como objetos.
7. Template Method Pattern
Uso: Estructura estándar de Server Actions
Implementación:
export async function standardAction(formData: FormData) {
try {
// 1. Auth check
const user = await getCurrentUser();
if (!user) throw new Error('Unauthorized');
// 2. RBAC check
requireRole(user, ['REQUIRED_ROLE']);
// 3. Validate
const validated = schema.parse(data);
// 4. Business logic
const result = await db.model.create({ data: validated });
// 5. Revalidate
revalidatePath('/path');
// 6. Return
return { success: true, data: result };
} catch (error) {
captureError(error, { context: 'actionName' });
return { success: false, error: error.message };
}
}
Razón: Estructura consistente para todas las acciones.
8. Facade Pattern
Uso: Core layer
Implementación:
// src/core/auth.ts
export async function getCurrentUser() {
// Lógica compleja de Clerk simplificada
const { userId } = auth();
if (!userId) return null;
const profile = await db.profile.findUnique({
where: { userId },
});
return profile ? { id: userId, profile } : null;
}
Razón: Simplificar interfaces complejas.
9. Decorator Pattern
Uso: Validaciones con Zod
Implementación:
// modules/hackathons/validations.ts
export const createHackathonSchema = z.object({
name: z.string().min(3),
slug: z.string().regex(/^[a-z0-9-]+$/),
}).refine(
(data) => data.registrationOpensAt < data.registrationClosesAt,
{ message: 'Invalid date order' }
);
Razón: Agregar validaciones sin modificar la estructura base.
10. Chain of Responsibility
Uso: Middleware de Next.js
Implementación:
// middleware.ts
export default clerkMiddleware((auth, req) => {
// 1. Verificar autenticación
// 2. Verificar rutas públicas
// 3. Verificar rutas protegidas
// 4. Redirigir si es necesario
});
Razón: Procesar requests en cadena.
Patrones de Arquitectura
Modular Architecture
Principio: Separar código por dominio de negocio
Ventajas:
- ✅ Código organizado
- ✅ Fácil de testear
- ✅ Reutilizable
- ✅ Escalable
Layered Architecture
Principio: Separar responsabilidades en capas
Capas:
- Presentation: React Components
- Application: Server Actions, API Routes
- Domain: Modules (queries, actions)
- Infrastructure: Core (auth, db, rbac)
Server Actions Pattern
Principio: Mutaciones directamente desde componentes
Ventajas:
- ✅ Menos código
- ✅ Type-safe
- ✅ Mejor performance
- ✅ Mejor UX
Próximos Pasos
- Module Structure - Estructura detallada
- System Architecture - Arquitectura completa
- Development Guide - Cómo implementar
Siguiente: Infrastructure