1. Arquitectura de Alto Nivel (C4 Context)
La plataforma Nvito está compuestá por cuatro aplicaciones principales que trabajan en conjunto para ofrecer la experiencia completa de gestión de eventos sociales. Cada aplicación tiene un proposito específico y se comúnica con servicios externos especializados.
1.1 Aplicaciones del Sistema
| Aplicación | Tecnologia | Proposito |
|---|---|---|
| nvito-api | NestJS | API REST centralizada con toda la lógica de negocio, autenticación y datos |
| nvito-admin | Next.js 16 | Panel de administración para anfitriones: gestión de eventos, invitados, invitaciones y configuración |
| nvito-invitations | Next.js 16 | Micrositios públicos de invitaciones renderizados con SSG/ISR para máximo rendimiento |
| nvito-client | React Native / Expo | App movil nativa (iOS/Android) para interacción en tiempo real: check-in QR, galería, audio guestbook, push notifications |
1.2 Servicios Externos
| Servicio | Proposito |
|---|---|
| Clerk | Autenticación, gestión de identidad y JWT |
| PostgreSQL | Base de datos principal con Row Level Security (RLS) |
| Redis | Cache, rate limiting, almacenamiento de sesiones y broker de colas |
| R2 / MinIO | Almacenamiento de objetos (imagenes, audio, archivos) |
| SMTP (Resend) | Envio de correos electronicos transaccionales y notificaciones |
| Twilio | Envio de mensajes por WhatsApp para notificaciones a invitados |
1.3 Actores del Sistema
| Actor | Descripción |
|---|---|
| Anfitrion (Host) | Crea eventos y gestióna invitaciones a traves de nvito-admin. El día del evento, usa nvito-client para check-in y monitoreo en vivo |
| Invitado (Guest) | Visualiza la invitación pública via nvito-invitations y confirma RSVP. Usa nvito-client para QR pass, galería y audio guestbook |
| Super Administrador | Operador de la plataforma con acceso global completo a todos los tenants |
| Platform Admin | Administrador de plataforma con lectura cross-org y escritura en orgs asignadas. Accede al panel admin con restricciones (no puede eliminar usuarios/orgs ni gestionar SA/PA) |
| Staff del Evento | Personal que realiza check-in de invitados el día del evento |
1.4 Diagrama C4 de Contexto
C4 Context — Sistema Nvito
Aplicaciones
Sitios Estaticos
Aplicaciones
Servicios Externos
2. Arquitectura de Contenedores (C4 Container)
Cada aplicación tiene una estructura interna compuestá por capas y componentes especializados. A continuacion se detalla la composición interna de cada contenedor.
2.1 nvito-api (NestJS) - Componentes Internos
El backend está construido con NestJS y sigue una arquitectura modular por capas:
| Capa / Componente | Responsabilidad |
|---|---|
| Guards Globales | ClerkAuthGuard (JWT), UserThrottlerGuard (rate limit), RoleGuard (RBAC) |
| TenantMiddleware | Establece contexto de tenant via SET LOCAL app.current_tenant_id. Soporta X-Organization-Id para Platform Admins |
| AdminPanelGuard | Permite acceso al panel admin para Super Admins y Platform Admins |
| PermissionsGuard | Valida permisos granulares por acción (events:create:organization) |
| Controllers | 56 controladores REST organizados por dominio (incluye mobile controllers y external-invitation controller) |
| Services | Lógica de negocio, validaciones, orquestacion de operaciones |
| Prisma ORM | Acceso a datos con queries tipados y migraciones |
| AuditInterceptor | Registra operaciones de modificación en audit_log de forma asincrona |
| Queue Processors | Workers Bull para tareas asincronas (email, whatsapp, push notifications) |
| Sub-Services | 56 sub-servicios en directorios services/ con responsabilidad única (SRP) |
| Modulos de Dominio | 39 módulos: Events, Guests, Invitations, RSVP, Locations, Tables, QR Passes, AI, Templates, Payments, Mobile, Clients, etc. |
Modulos registrados en AppModule (39 de dominio + 5 de infraestructura = 44 total):
- Core:
AuthModule,OrganizationsModule,UsersModule,RolesModule,AdminModule,PackagesModule,HealthModule - Eventos:
EventsModule,EventTypesModule,EventServicesModule,ContractsModule - Invitaciones:
InvitationsModule,TemplatesModule,HtmlGenerationModule - Invitados:
GuestsModule,GuestGroupsModule,RsvpModule,TablesModule - Clientes:
ClientsModule - Features:
LocationsModule,ItineraryModule,QRPassesModule,GiftRegistryModule,AccommodationModule,BackgroundMusicModule,MusicModule - Media:
MediaModule,StorageModule,CustomDomainModule - IA:
AIGenerationModule - Comunicaciones:
NotificationsModule,NotificationChannelsModule,CommunicationsModule,QueueModule,SchedulerModule - Pagos:
PaymentsModule - Movil:
MobileModule,MobileAuthModule - Analitica:
AnalyticsModule - Infraestructura:
PrismaModule,CommonModule,ActivityLogModule,PermissionsModule,ConfigModule
2.2 nvito-admin (Next.js 16) - Componentes Internos
| Capa / Componente | Responsabilidad |
|---|---|
| App Router | Enrutamiento basado en carpetas de Next.js 16 con layouts anidados |
| API Client | Cliente HTTP centralizado con interceptores para Bearer token (Clerk) y response validation (Zod) |
| Server Actions | 17 archivos de server actions con validación Zod y ActionResult tipado |
| React Query | Cache del lado del cliente, invalidación automática, optimistic updates |
| React Compiler | Memoizacion automática de componentes y hooks (habilitado en next.config.ts) |
| Componentes UI | 41+ componentes Shadcn/UI + componentes custom con accesibilidad (ARIA) |
| Clerk Provider | Contexto de autenticación, protección de rutas, session management |
| Validación Zod | 16 esquemas de validación para formularios y server actions |
| CSP Headers | Content Security Policy configurado para Clerk, Mapbox, CDN |
2.3 nvito-client (React Native / Expo) - Componentes Internos
| Capa / Componente | Responsabilidad |
|---|---|
| Expo Router | Navegación file-based con layouts anidados y grupos de rutas por rol |
| API Client | Fetch wrapper con interceptores para Bearer token (JWT movil) |
| React Query | Cache del lado del cliente con invalidación automática |
| AuthContext | Estado de autenticación con useReducer, tokens en SecureStore |
| MobileAuthGuard | Guard en (app)/_layout.tsx que redirige a login si no autenticado |
| Push Notifications | Registro de Expo Push Token y handlers de notificaciones |
| QR Scanner | expo-camera CameraView para escaneo de códigos QR en check-in |
| Media Uploads | Upload de fotos y audio via presigned URLs a R2/MinIO |
| NativeWind | Tailwind CSS compilado a StyleSheet nativo para estilos |
2.4 nvito-invitations (Next.js 16) - Componentes Internos
Aplicación mínimalista con solo 7 dependencias de producción. Agnostica al origen: renderiza invitaciones de forma identica sin importar si fueron creadas con el motor de templates de Nvito (INTERNAL) o subidas como HTML externo (EXTERNAL).
| Capa / Componente | Responsabilidad |
|---|---|
| Route Handlers | Lógica principal en route handlers (/i/[slug], /preview/[token], /api/revalidate) |
| ISR (Revalidation) | Regeneración incremental cuando el contenido cambia via webhook |
| Servicios SOLID | 4 servicios con responsabilidad unica: invitation-fetcher, html-processor, cdn-validator, analytics-script |
| DOM Utilities | font-injector (Google Fonts) y html-sanitizer (DOMPurify whitelist) |
| InvitationRenderer | Client component que renderiza HTML sanitizado con tracking de analytics |
| Analytics Tracker | Registro de visitas, interacciones y metricas de engagement (Web Vitals) |
| Seguridad | Timing-safe secrets, escape XSS context-aware, validación SSRF, rate limiting, CSP headers |
2.5 Diagrama C4 de Contenedores
C4 Containers — Aplicaciones e Infraestructura
Frontend Apps
Backend
Infraestructura
Servicios Externos
3. Flujo de Request HTTP
Cada peticion HTTP que llega a nvito-api atraviesa una cadena de middleware, guards e interceptors antes de llegar al controlador. El orden de ejecución es critico para la seguridad y el aislamiento de datos.
3.1 Cadena de Ejecución
El flujo completo de un request autenticado es el siguiente:
- HTTP Request - La peticion llega al servidor NestJS (puerto configurable, default 3000)
- Helmet + CORS + Compression - Middleware de seguridad y rendimiento configurados en
main.ts - ValidationPipe - Validación global de DTOs con
whitelistyforbidNonWhitelisted - ClerkAuthGuard - Verifica el JWT de Clerk, busca el usuario en la BD, resuelve roles desde
user_roles, valida que el usuario este activo y no eliminado - UserThrottlerGuard - Rate limiting por usuario autenticado (100 req/min) o por IP para rutas públicas, almacenado en Redis
- TenantMiddleware - Extrae el
organizationIddel usuario autenticado y ejecutaSET LOCAL app.current_tenant_iden PostgreSQL para activar RLS - RoleGuard - Verifica que el rol del usuario (
owner,admin,member,super_admin) cumple con el mínimo requerido por la ruta - PermissionsGuard - Valida permisos granulares definidos con
@RequirePermission('resource:action:scope') - Controller - Recibe la peticion validada, extrae parametros y delega al servicio
- Service - Ejecuta la lógica de negocio, validaciones de dominio y operaciones
- Prisma - Ejecuta queries contra PostgreSQL con RLS activo (filtrado automático por tenant)
- AuditInterceptor - Tras una respuesta exitosa en rutas criticas (POST/PATCH/PUT/DELETE), registra la acción en
audit_logde forma asincrona - Response - Se envia la respuesta al cliente con compresion gzip
3.2 Diagrama de Secuencia del Request
Flujo de Request HTTP Autenticado
3.3 Notas sobre el Flujo
- Orden de guards: El orden en
AppModuleimporta.ClerkAuthGuardva primero para quereq.usereste disponible paraUserThrottlerGuard - Rutas excluidas de TenantMiddleware:
webhooks/*yhealthno pasan por el middleware de tenant - Auditoria asincrona: El
AuditInterceptorno bloquea la respuesta HTTP; el registro enaudit_logse ejecuta en background - Rate limiting con Redis: El
RedisThrottlerStorageusa Redis compartido para funcionar correctamente con múltiples instancias del API
4. Multi-Tenancy
Nvito implementa multi-tenancy a nivel de base de datos utilizando PostgreSQL Row Level Security (RLS). Cada organización es un tenant aislado, y el mecanismo garantiza que un tenant nunca pueda acceder a los datos de otro.
4.1 Concepto General
| Aspecto | Implementación |
|---|---|
| Estrategia | Row Level Security (RLS) - una BD, un schema, múltiples tenants |
| Boundary del tenant | La entidad Organization es la frontera del tenant |
| Columna de filtrado | organizationId (UUID) presente en todas las tablas principales |
| Variable de sesion | app.current_tenant_id configurada via SET LOCAL por transacción |
| Enforcement | Politicas RLS en PostgreSQL que filtran automáticamente por organization_id |
4.2 Como Funciona el TenantMiddleware
El TenantMiddleware en /nvito-api/src/database/tenant.middleware.ts es el componente central del sistema multi-tenant:
// Extracto simplificado del TenantMiddleware
async use(req: Request, res: Response, next: NextFunction) {
const user = req.user as AuthenticatedUser;
if (user && user.organizationId) {
// SECURITY: Usar $executeRaw con tagged template para prevenir SQL injection
await this.prisma
.$executeRaw`SELECT set_config('app.current_tenant_id', ${user.organizationId}, true)`;
}
next();
}
Puntos clave:
- Usa
set_configcon el tercer parametrotruepara que sea local a la transacción (SET LOCAL) - El
organizationIdya fue resuelto y validado porClerkAuthGuarddesde la tablauser_roles - Previene SQL injection usando tagged templates de Prisma (
$executeRaw) - Solo se aplica si el usuario está autenticado y tiene una organización asignada
- Las rutas de webhooks y health estan excluidas
4.3 Politica RLS en PostgreSQL
-- Ejemplo de politica RLS aplicada a la tabla events
CREATE POLICY tenant_isolation_policy ON events
USING (organization_id = current_setting('app.current_tenant_id')::uuid);
-- Cuando el usuario ejecuta:
SELECT * FROM events WHERE status = 'published';
-- PostgreSQL realmente ejecuta (RLS transparente):
SELECT * FROM events
WHERE status = 'published'
AND organization_id = 'uuid-de-la-org'; -- Anadido automáticamente por RLS
4.4 Diagrama de Secuencia Multi-Tenancy
Multi-Tenancy con Row Level Security
4.5 Garantias de Aislamiento
| Garantia | Mecanismo |
|---|---|
| Aislamiento de datos | RLS filtra todas las queries automáticamente por organization_id |
| Prevencion de cross-tenant | No es posible acceder a datos de otra organización, ni siquiera con queries manuales desde el código |
| Super Admin | Los super admins tienen organizationId = null, lo que les permite operar sin restriccion de RLS cuando es necesario |
| Platform Admin | Los platform admins tienen lectura cross-org (sin RLS). Para escritura, el TenantMiddleware usa activeOrganizationId del header X-Organization-Id para establecer el contexto de la org donde operan |
| Transaccional | SET LOCAL asegura que el contexto del tenant se limita a la transacción actual |
| Sin SQL injection | Tagged templates de Prisma previenen inyección en el set_config |
5. Comunicación entre Servicios
La comunicación entre las cuatro aplicaciones y los servicios externos sigue patrones específicos según el tipo de interacción.
5.1 Admin a API (Sincrono)
| Aspecto | Detalle |
|---|---|
| Protocolo | HTTPS REST |
| Autenticación | Bearer token (JWT emitido por Clerk) |
| Prefijo | /v1/ (versionado de API) |
| Formato | JSON |
| Cache cliente | React Query con invalidación automática |
| CORS | Origenes configurados explicitamente, sin wildcard en producción |
El flujo tipico es:
nvito-adminobtiene el JWT del usuario autenticado via Clerk Provider- El API Client adjunta el token como
Authorization: Bearer <jwt> nvito-apivalida el token, resuelve el usuario y aplica el contexto de tenant- La respuesta se cachea en React Query con keys basadas en el recurso
5.2 Invitations a API (Sincrono - Parcialmente Publico)
| Aspecto | Detalle |
|---|---|
| Protocolo | HTTPS REST |
| Autenticación | Sin autenticación (rutas @Public()) para RSVP y visualización |
| Server-side | Fetch de metadata en getStaticProps / generateStaticParams |
| Datos | Metadata de invitación, datos del evento, configuración de RSVP |
5.3 App Movil a API (Sincrono - JWT Movil)
| Aspecto | Detalle |
|---|---|
| Protocolo | HTTPS REST |
| Autenticación | Bearer token (JWT firmado con MOBILE_JWT_SECRET, independiente de Clerk) |
| Prefijo | /v1/mobile/ (endpoints dedicados para la app) |
| Guard | MobileAuthGuard (valida JWT movil, extrae rol HOST/GUEST) |
| Formato | JSON |
| Cache cliente | React Query con invalidación automática |
| Push | Registro de Expo Push Token para notificaciones |
El flujo tipico es:
- El usuario se autentica con código de evento + PIN (host) o access token (guest)
nvito-apigenera un JWT de 15 min + refresh token de 30 dias- Los tokens se almacenan en
expo-secure-store(almacenamiento encriptado nativo) - El API Client inyecta el Bearer token en cada request
- Si recibe 401, intenta renovar el JWT con el refresh token automáticamente
5.4 API a Invitations (Webhook ISR)
Cuando un anfitrion modifica una invitación en nvito-admin, el API notifica a nvito-invitations para que regenere la página estática:
| Aspecto | Detalle |
|---|---|
| Mecanismo | Webhook HTTP POST a la ruta de revalidación de Next.js |
| Trigger | Publicación/despublicación de invitación (internas y externas), cambio de estado |
| Efecto | ISR regenera la página estática de la invitación afectada |
| Seguridad | Token secreto compartido para validar el webhook |
5.5 API a Queue (Asincrono)
Las tareas que no requieren respuesta inmediata se procesan a traves de colas BullMQ:
| Cola / Job | Processor | Proposito |
|---|---|---|
EmailProcessor | Envio de correos electronicos via SMTP (nodemailer) | |
WhatsAppProcessor | Envio de mensajes WhatsApp via Twilio | |
| push | PushProcessor | Notificaciones push via Expo Push API a dispositivos moviles |
5.6 Webhooks Entrantes
| Origen | Endpoint | Proposito |
|---|---|---|
| Clerk | /webhooks/clerk | Sincronización de usuarios: creación, actualización, eliminación |
| Twilio | /webhooks/twilio | Status de entrega de mensajes WhatsApp |
| Stripe | /webhooks/stripe | Eventos de pago: checkout completado, pago fallido, reembolsos |
Estás rutas estan excluidas del TenantMiddleware y utilizan verificacion de firma propia del servicio externo (Svix para Clerk, Twilio signature, Stripe signature).
5.7 Invitations a CDN
Las invitaciones públicas se sirven desde el edge de la CDN:
| Aspecto | Detalle |
|---|---|
| Build-time | SSG genera HTML/CSS/JS estático por invitación |
| Runtime | CDN sirve el contenido pre-compilado con latencia mínima |
| Revalidación | ISR regenera páginas individuales cuando cambia el contenido |
| Assets | Imagenes y audio se cargan desde R2/MinIO via URLs firmadas |
6. Patrones Arquitectonicos
6.1 Multi-Repo
Nvito utiliza una estrategia de múltiples repositorios independientes:
| Repositorio | Tecnologia | Despliegue Independiente |
|---|---|---|
nvito-api | NestJS | Si |
nvito-admin | Next.js 16 | Si |
nvito-invitations | Next.js 16 | Si |
nvito-client | React Native / Expo | Si (Expo Go / TestFlight) |
Ventajas de este enfoque:
- CI/CD independiente: Cada aplicación tiene su propio pipeline de build, test y deploy
- Versiónado independiente: Las versiones de cada aplicación avanzan a su propio ritmo
- Equipos autonomos: Diferentes desarrolladores pueden trabajar en cada repositorio sin conflictos
- Deployment aislado: Un deploy de nvito-admin no afecta a nvito-api ni a nvito-invitations
- Tecnologias especializadas: Cada repo puede tener sus propias dependencias y configuraciones
6.2 Arquitectura por Capas (Layered Architecture)
El backend sigue una arquitectura estrictamente por capas con dependencias unidireccionales:
Controller --> Service --> Repository (Prisma)
| Capa | Responsabilidad | Ejemplo |
|---|---|---|
| Controller | Recibir HTTP, validar DTOs, delegar al servicio | EventsController.create() |
| Service | Lógica de negocio, validaciones de dominio, orquestacion | EventsService.createEvent() |
| Repository | Acceso a datos via Prisma ORM, queries a PostgreSQL | prisma.event.create() |
Reglas:
- Los controllers nunca acceden directamente a Prisma
- Los services pueden inyectar otros services para orquestacion
- La capa de datos (Prisma) es la única que interactua con PostgreSQL
6.3 CQRS-Lite (Command Query Responsibility Segregation)
Nvito aplica una versión simplificada de CQRS donde las operaciones se separan por semantica HTTP:
| Tipo | Metodo HTTP | Semantica | Ejemplo |
|---|---|---|---|
| Query | GET | Lectura de datos, sin efectos secundarios | GET /v1/events |
| Command | POST | Creación de recursos | POST /v1/events |
| Command | PATCH / PUT | Actualización de recursos existentes | PATCH /v1/events/:id |
| Command | DELETE | Eliminación de recursos | DELETE /v1/events/:id |
Diferencias con CQRS completo:
- No hay modelos de lectura y escritura separados
- No hay event store ni event sourcing
- La separacion es a nivel de convención HTTP, no de infraestructura
- El
AuditInterceptorsolo registra Commands (POST/PATCH/PUT/DELETE), no Queries (GET)
6.4 Event-Driven para Tareas Asincronas (BullMQ)
Las operaciones que no requieren respuesta inmediata se delegan a colas de trabajo:
Service --> QueueModule.add(jobName, payload) --> Redis (BullMQ) --> Worker/Processor
| Caracteristica | Implementación |
|---|---|
| Broker | Redis como backend de BullMQ |
| Producción de jobs | Los services encolan jobs con datos del contexto actual |
| Consumo de jobs | Workers dedicados procesan jobs de forma asincrona |
| Reintentos | Configuración por cola con backoff exponencial |
| Monitoreo | Dashboards de BullMQ para observar estado de colas |
Casos de uso tipicos:
- Envio de email de confirmación tras RSVP
- Generación de HTML compilado de invitación
- Procesamiento de generación de contenido con IA
- Envio de recordatorios programados
- Procesamiento batch de metricas de analytics
6.5 ISR (Incremental Static Regeneration) para Paginas Publicas
Las invitaciones públicas usan ISR para combinar el rendimiento de páginas estáticas con la capacidad de actualizar contenido:
| Fase | Descripción |
|---|---|
| Build-time | Se genera HTML estático para cada invitación publicada |
| Request-time | El CDN sirve la página pre-renderizada con latencia mínima |
| Revalidación | Cuando nvito-api detecta un cambio, envia un webhook a Next.js para regenerar solo la página afectada |
| Fallback | Para invitaciones nuevas que no existian en build-time, se genera on-demand y se cachea |
Beneficios:
- Tiempo de carga < 1 segundo (servido desde CDN edge)
- Core Web Vitals optimizados (LCP, FID, CLS)
- Costo de infraestructura bajo (no requiere servidor activo para cada request)
- Actualización en tiempo casi-real cuando el anfitrion modifica la invitación
6.6 Seguridad en Capas
La seguridad se implementa como múltiples capas que un request debe atravesar:
| Capa | Componente | Nivel de Protección |
|---|---|---|
| 1. Red | Helmet + CORS | Headers de seguridad, protección de origenes |
| 2. Rate Limiting | UserThrottlerGuard | 100 req/min por usuario/IP via Redis |
| 3. Autenticación | ClerkAuthGuard | Verificacion JWT, usuario activo, no eliminado |
| 4. Tenant | TenantMiddleware | Aislamiento de datos por organización (RLS) |
| 5. Autorización | RoleGuard + PermissionsGuard | RBAC con permisos granulares |
| 6. Validación | ValidationPipe | DTOs con whitelist, forbidNonWhitelisted |
| 7. Auditoria | AuditInterceptor | Registro de todas las acciones de modificación |
| 8. Base de datos | PostgreSQL RLS | Filtrado automático por tenant a nivel de query |