Saltar al contenido principal

🎨 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:

  1. Presentation: React Components
  2. Application: Server Actions, API Routes
  3. Domain: Modules (queries, actions)
  4. 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


Siguiente: Infrastructure