Saltar al contenido principal

Arquitectura del Sistema

Esta página profundiza en cómo está construido FeedbackPulse SaaS. Comprender la arquitectura te ayuda a depurar problemas, extender la plataforma y tomar decisiones de despliegue informadas.


Stack Tecnológico

CapaTecnologíaPropósito
BackendLaravel 12 (PHP 8.4+)Framework de aplicación, enrutamiento, ORM, colas
Base de DatosMySQL 8.0+ / MariaDB 10.6+Almacenamiento persistente de datos
FrontendBlade + Alpine.js + Tailwind CSSUI renderizada en servidor con componentes reactivos
PagosStripe PHP SDK + PayPal REST APIFacturación por suscripción
IAOpenAI GPT APIAnálisis de sentimiento, etiquetado automático, sugerencias de respuesta
AutenticaciónLaravel SocialiteOAuth con Google y GitHub
CorreoLaravel Mail (SMTP)Correos transaccionales, resúmenes, informes
Tiempo RealServer-Sent Events (SSE)Transmisión en vivo de envíos
Tareas ProgramadasLaravel Scheduler (cron)Resúmenes, expiración de pruebas, retención de datos

Modelo Multi-Inquilino

FeedbackPulse utiliza un modelo de multi-inquilino de base de datos única y esquema compartido. Esto significa:

  • Una sola base de datos sirve a todos los inquilinos
  • Cada tabla con ámbito de inquilino tiene una columna tenant_id
  • Un scope global (TenantScope) filtra automáticamente todas las consultas al inquilino actual
  • Un trait (BelongsToTenant) rellena automáticamente tenant_id al crear y aplica el scope

Cómo Funciona la Resolución de Inquilinos

Cuando llega una solicitud, el middleware ResolveTenant determina el inquilino actual con la siguiente prioridad:

  1. Subdominioacme.yourdomain.com → busca el inquilino con el subdominio "acme"
  2. Dominio personalizadofeedback.acmecorp.com → busca el dominio verificado en la tabla tenant_domains
  3. Usuario autenticado — recurre al tenant_id del usuario con sesión iniciada

Para páginas públicas (como /wall/acme-corp), el inquilino se resuelve desde el slug de la URL directamente en el controlador (sin pasar por el middleware).

Aislamiento de Datos

+------------------------------------------+
| Base de Datos |
| |
| products (tenant_id = 1) -> Acme data |
| products (tenant_id = 2) -> TechCorp |
| products (tenant_id = 3) -> E-Commerce |
| |
| TenantScope garantiza que Acme SOLO vea |
| filas donde tenant_id = 1 |
+------------------------------------------+

Seguridad: Los controladores de cara al público usan withoutGlobalScopes() para omitir el scope del inquilino y luego filtran manualmente por ID de inquilino. Esto es intencional: las páginas públicas necesitan mostrar datos sin una sesión autenticada.


Estructura de Directorios

feedbackpulse-saas/
+-- app/
| +-- Console/Commands/ # 7 comandos artisan (resúmenes, retención, alertas)
| +-- Http/
| | +-- Controllers/
| | | +-- Admin/ # Controladores del panel de superadmin
| | | +-- Auth/ # Login, registro, 2FA, OAuth, suplantación
| | | +-- Customer/ # Portal del cliente
| | | +-- Public/ # Páginas públicas (muro, formulario, hoja de ruta, registro de cambios, hub)
| | | +-- Tenant/ # Controladores del panel del inquilino
| | | +-- Webhooks/ # Manejadores de webhooks de Stripe y PayPal
| | +-- Middleware/ # 15 middleware personalizados
| +-- Mail/ # 7 clases de correo
| +-- Models/ # 28 modelos Eloquent
| +-- Scopes/ # TenantScope (scope global de consultas)
| +-- Services/ # Lógica de negocio (IA, pagos, webhooks, etc.)
| +-- Traits/ # Trait BelongsToTenant
| +-- Providers/ # Proveedores de servicios
+-- config/ # 11 archivos de configuración de Laravel
+-- database/
| +-- migrations/ # Más de 35 archivos de migración
| +-- seeders/ # Seeders de datos de demostración
+-- public/ # Raíz web (index.php, assets, enlace simbólico de almacenamiento)
+-- resources/views/ # 86 plantillas Blade
| +-- admin/ # Vistas del superadmin
| +-- auth/ # Vistas de autenticación (login, registro, 2FA)
| +-- tenant/ # Vistas del panel del inquilino
| +-- public/ # Vistas de páginas públicas
| +-- emails/ # Plantillas de correo electrónico
| +-- layouts/ # Plantillas de diseño (admin, inquilino, invitado, instalación)
| +-- partials/ # Componentes compartidos (nav, meta, paleta de comandos)
| +-- install/ # Vistas del instalador web
| +-- legal/ # Páginas de privacidad, términos y cookies
| +-- errors/ # Páginas de error (403, 404, 419, 429, 500)
| +-- landing/ # Parciales de la página de inicio
+-- routes/
| +-- web.php # 416 líneas de rutas web
| +-- api.php # Rutas de la API v2
+-- storage/ # Subidas, caché, sesiones, registros
+-- bootstrap/ # Archivos de arranque del framework

Roles de Usuario

FeedbackPulse tiene cuatro roles de usuario, almacenados en la columna users.role:

RolAccesoURL de Inicio de Sesión
superadminControl total de la plataforma (/admin/*)/login
tenant_adminControl total del inquilino (/dashboard, /settings/*)/login
tenant_staffAcceso limitado al inquilino (sin facturación, sin eliminar)/login
customerSolo portal del cliente (/customer/dashboard)/customer/login

Jerarquía de Roles

superadmin
+-- Puede suplantar a cualquier tenant_admin
+-- tenant_admin
+-- Puede invitar a tenant_staff
+-- Puede gestionar facturación, configuración y equipo
+-- tenant_staff
+-- Puede gestionar envíos y campañas
+-- customer
+-- Puede ver su propio feedback

Ciclo de Vida de una Solicitud

Esto es lo que ocurre cuando una solicitud llega a FeedbackPulse:

Solicitud del Navegador
|
v
public/index.php
|
v
Laravel Kernel (pila de middleware)
|
+-- EnsureInstalled -> Redirige a /install si no está configurado
+-- SecurityHeaders -> Agrega HSTS, CSP, X-Frame-Options
+-- VerifyCsrfToken -> Verifica token CSRF (excepto webhooks)
+-- ResolveTenant -> Determina el inquilino actual
+-- Authenticate -> Verifica si el usuario ha iniciado sesión
+-- EnsureTenantAccess -> Verifica que el usuario pertenezca al inquilino
+-- EnsureTwoFactorVerified -> Comprueba 2FA si está habilitado
+-- CheckPlanLimit -> Aplica los límites de características del plan
|
v
Controlador (procesa la solicitud)
|
v
Vista Blade (renderiza HTML)
|
v
Respuesta -> Navegador

Arquitectura de Base de Datos

Tablas Principales

TablaFilas PorPropósito
tenantsPlataformaCuentas multi-inquilino
usersPlataformaTodas las cuentas de usuario (todos los roles)
plansPlataformaPlanes de suscripción
platform_settingsPlataformaConfiguración global clave-valor
productsInquilinoProductos de feedback
feedback_campaignsInquilinoConfiguraciones del formulario de feedback
feedback_submissionsInquilinoEnvíos individuales de feedback
feedback_tagsInquilinoEtiquetas (muchos a muchos con envíos)
roadmap_itemsInquilinoElementos kanban de la hoja de ruta
roadmap_votesInquilinoVotos anónimos en elementos de la hoja de ruta
feature_requestsInquilinoSugerencias de funcionalidades de la comunidad
changelog_entriesInquilinoNotas de versión del producto
team_membersInquilinoRegistros de miembros del equipo
team_invitationsInquilinoInvitaciones pendientes
api_keysInquilinoClaves de acceso a la API
audit_logsPlataformaRegistro de auditoría de acciones
notificationsPlataformaNotificaciones en la aplicación
payment_eventsPlataformaEventos de webhook de Stripe/PayPal
webhook_logsInquilinoRegistros de entrega de webhooks salientes
data_deletion_requestsInquilinoSolicitudes de eliminación GDPR
landing_pagesPlataformaDatos del constructor de páginas de inicio
tenant_domainsInquilinoMapeos de dominios personalizados
referral_codesInquilinoCódigos de referido
referral_conversionsPlataformaSeguimiento de conversiones de referido
cron_logsPlataformaRegistros de ejecución de tareas programadas

Índices Clave

Todas las tablas con ámbito de inquilino están indexadas en (tenant_id, created_at) para un rendimiento óptimo de las consultas. La tabla feedback_submissions tiene índices adicionales en status, product_id, campaign_id y sentiment_label.


Arquitectura de Seguridad

CapaProtección
TransporteHTTPS forzado, cabeceras HSTS
AutenticaciónBcrypt (12 rondas), 2FA opcional (TOTP)
AutorizaciónMiddleware basado en roles + clases de políticas
CSRFTokens CSRF de Laravel en todos los formularios
XSSEscape automático de Blade ({{ }})
Inyección SQLConsultas parametrizadas de Eloquent
Limitación de VelocidadThrottling por ruta (5-120 req/min)
Seguridad de APIClaves de API con hash SHA256, límites por inquilino
Seguridad de WebhooksVerificación de firma HMAC (Stripe), protección SSRF
DatosConfiguración sensible cifrada en reposo
SesionesCifradas, solo HTTP, cookies seguras
CabecerasCSP, X-Frame-Options, X-Content-Type-Options

Próximos Pasos