Arquitectura del Sistema
Informacion del Documento
| Campo | Valor |
|---|---|
| Version | 1.1 |
| Fecha | Marzo 2026 |
| Estado | Publicado |
| Proyecto | Nvito Platform |
Tabla de Contenidos
- Arquitectura de Alto Nivel (C4 Context)
- Arquitectura de Contenedores (C4 Container)
- Flujo de Request HTTP
- Multi-Tenancy
- Comunicacion entre Servicios
- Patrones Arquitectonicos
1. Arquitectura de Alto Nivel (C4 Context)
La plataforma Nvito esta compuesta por cuatro aplicaciones principales que trabajan en conjunto para ofrecer la experiencia completa de gestion de eventos sociales. Cada aplicacion tiene un proposito especifico y se comunica con servicios externos especializados.
1.1 Aplicaciones del Sistema
| Aplicacion | Tecnologia | Proposito |
|---|---|---|
| nvito-api | NestJS | API REST centralizada con toda la logica de negocio, autenticacion y datos |
| nvito-admin | Next.js 16 | Panel de administracion para anfitriones: gestion de eventos, invitados, invitaciones y configuracion |
| nvito-invitations | Next.js 16 | Micrositios publicos de invitaciones renderizados con SSG/ISR para maximo rendimiento |
| nvito-client | React Native / Expo | App movil nativa (iOS/Android) para interaccion en tiempo real: check-in QR, galeria, audio guestbook, push notifications |
1.2 Servicios Externos
| Servicio | Proposito |
|---|---|
| Clerk | Autenticacion, gestion 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 | Descripcion |
|---|---|
| Anfitrion (Host) | Crea eventos y gestiona invitaciones a traves de nvito-admin. El dia del evento, usa nvito-client para check-in y monitoreo en vivo |
| Invitado (Guest) | Visualiza la invitacion publica via nvito-invitations y confirma RSVP. Usa nvito-client para QR pass, galeria 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 dia del evento |
1.4 Diagrama C4 de Contexto
2. Arquitectura de Contenedores (C4 Container)
Cada aplicacion tiene una estructura interna compuesta por capas y componentes especializados. A continuacion se detalla la composicion interna de cada contenedor.
2.1 nvito-api (NestJS) - Componentes Internos
El backend esta 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 accion (events:create:organization) |
| Controllers | 56 controladores REST organizados por dominio (incluye mobile controllers y external-invitation controller) |
| Services | Logica de negocio, validaciones, orquestacion de operaciones |
| Prisma ORM | Acceso a datos con queries tipados y migraciones |
| AuditInterceptor | Registra operaciones de modificacion 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 unica (SRP) |
| Modulos de Dominio | 39 modulos: 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 validacion Zod y ActionResult tipado |
| React Query | Cache del lado del cliente, invalidacion automatica, optimistic updates |
| React Compiler | Memoizacion automatica de componentes y hooks (habilitado en next.config.ts) |
| Componentes UI | 41+ componentes Shadcn/UI + componentes custom con accesibilidad (ARIA) |
| Clerk Provider | Contexto de autenticacion, proteccion de rutas, session management |
| Validacion Zod | 16 esquemas de validacion 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 | Navegacion 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 invalidacion automatica |
| AuthContext | Estado de autenticacion 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 codigos 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
Aplicacion minimalista con solo 7 dependencias de produccion. 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 | Logica principal en route handlers (/i/[slug], /preview/[token], /api/revalidate) |
| ISR (Revalidation) | Regeneracion 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, validacion SSRF, rate limiting, CSP headers |
2.5 Diagrama C4 de Contenedores
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 ejecucion es critico para la seguridad y el aislamiento de datos.
3.1 Cadena de Ejecucion
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 - Validacion 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 publicas, 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 minimo 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 logica de negocio, validaciones de dominio y operaciones
- Prisma - Ejecuta queries contra PostgreSQL con RLS activo (filtrado automatico por tenant)
- AuditInterceptor - Tras una respuesta exitosa en rutas criticas (POST/PATCH/PUT/DELETE), registra la accion en
audit_logde forma asincrona - Response - Se envia la respuesta al cliente con compresion gzip
3.2 Diagrama de Secuencia del Request
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 multiples instancias del API
4. Multi-Tenancy
Nvito implementa multi-tenancy a nivel de base de datos utilizando PostgreSQL Row Level Security (RLS). Cada organizacion es un tenant aislado, y el mecanismo garantiza que un tenant nunca pueda acceder a los datos de otro.
4.1 Concepto General
| Aspecto | Implementacion |
|---|---|
| Estrategia | Row Level Security (RLS) - una BD, un schema, multiples 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 transaccion |
| Enforcement | Politicas RLS en PostgreSQL que filtran automaticamente 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 transaccion (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 esta autenticado y tiene una organizacion 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 automaticamente por RLS
4.4 Diagrama de Secuencia Multi-Tenancy
4.5 Garantias de Aislamiento
| Garantia | Mecanismo |
|---|---|
| Aislamiento de datos | RLS filtra todas las queries automaticamente por organization_id |
| Prevencion de cross-tenant | No es posible acceder a datos de otra organizacion, ni siquiera con queries manuales desde el codigo |
| 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 transaccion actual |
| Sin SQL injection | Tagged templates de Prisma previenen inyeccion en el set_config |
5. Comunicacion entre Servicios
La comunicacion entre las cuatro aplicaciones y los servicios externos sigue patrones especificos segun el tipo de interaccion.
5.1 Admin a API (Sincrono)
| Aspecto | Detalle |
|---|---|
| Protocolo | HTTPS REST |
| Autenticacion | Bearer token (JWT emitido por Clerk) |
| Prefijo | /v1/ (versionado de API) |
| Formato | JSON |
| Cache cliente | React Query con invalidacion automatica |
| CORS | Origenes configurados explicitamente, sin wildcard en produccion |
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 |
| Autenticacion | Sin autenticacion (rutas @Public()) para RSVP y visualizacion |
| Server-side | Fetch de metadata en getStaticProps / generateStaticParams |
| Datos | Metadata de invitacion, datos del evento, configuracion de RSVP |
5.3 App Movil a API (Sincrono - JWT Movil)
| Aspecto | Detalle |
|---|---|
| Protocolo | HTTPS REST |
| Autenticacion | 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 invalidacion automatica |
| Push | Registro de Expo Push Token para notificaciones |
El flujo tipico es:
- El usuario se autentica con codigo 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 automaticamente
5.4 API a Invitations (Webhook ISR)
Cuando un anfitrion modifica una invitacion en nvito-admin, el API notifica a nvito-invitations para que regenere la pagina estatica:
| Aspecto | Detalle |
|---|---|
| Mecanismo | Webhook HTTP POST a la ruta de revalidacion de Next.js |
| Trigger | Publicacion/despublicacion de invitacion (internas y externas), cambio de estado |
| Efecto | ISR regenera la pagina estatica de la invitacion 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 | Sincronizacion de usuarios: creacion, actualizacion, eliminacion |
| Twilio | /webhooks/twilio | Status de entrega de mensajes WhatsApp |
| Stripe | /webhooks/stripe | Eventos de pago: checkout completado, pago fallido, reembolsos |
Estas 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 publicas se sirven desde el edge de la CDN:
| Aspecto | Detalle |
|---|---|
| Build-time | SSG genera HTML/CSS/JS estatico por invitacion |
| Runtime | CDN sirve el contenido pre-compilado con latencia minima |
| Revalidacion | ISR regenera paginas 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 multiples 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 aplicacion tiene su propio pipeline de build, test y deploy
- Versionado independiente: Las versiones de cada aplicacion 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 | Logica 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 unica que interactua con PostgreSQL
6.3 CQRS-Lite (Command Query Responsibility Segregation)
Nvito aplica una version 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 | Creacion de recursos | POST /v1/events |
| Command | PATCH / PUT | Actualizacion de recursos existentes | PATCH /v1/events/:id |
| Command | DELETE | Eliminacion 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 convencion 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 | Implementacion |
|---|---|
| Broker | Redis como backend de BullMQ |
| Produccion de jobs | Los services encolan jobs con datos del contexto actual |
| Consumo de jobs | Workers dedicados procesan jobs de forma asincrona |
| Reintentos | Configuracion por cola con backoff exponencial |
| Monitoreo | Dashboards de BullMQ para observar estado de colas |
Casos de uso tipicos:
- Envio de email de confirmacion tras RSVP
- Generacion de HTML compilado de invitacion
- Procesamiento de generacion 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 publicas usan ISR para combinar el rendimiento de paginas estaticas con la capacidad de actualizar contenido:
| Fase | Descripcion |
|---|---|
| Build-time | Se genera HTML estatico para cada invitacion publicada |
| Request-time | El CDN sirve la pagina pre-renderizada con latencia minima |
| Revalidacion | Cuando nvito-api detecta un cambio, envia un webhook a Next.js para regenerar solo la pagina 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)
- Actualizacion en tiempo casi-real cuando el anfitrion modifica la invitacion
6.6 Seguridad en Capas
La seguridad se implementa como multiples capas que un request debe atravesar:
| Capa | Componente | Nivel de Proteccion |
|---|---|---|
| 1. Red | Helmet + CORS | Headers de seguridad, proteccion de origenes |
| 2. Rate Limiting | UserThrottlerGuard | 100 req/min por usuario/IP via Redis |
| 3. Autenticacion | ClerkAuthGuard | Verificacion JWT, usuario activo, no eliminado |
| 4. Tenant | TenantMiddleware | Aislamiento de datos por organizacion (RLS) |
| 5. Autorizacion | RoleGuard + PermissionsGuard | RBAC con permisos granulares |
| 6. Validacion | ValidationPipe | DTOs con whitelist, forbidNonWhitelisted |
| 7. Auditoria | AuditInterceptor | Registro de todas las acciones de modificacion |
| 8. Base de datos | PostgreSQL RLS | Filtrado automatico por tenant a nivel de query |