Arquitectura General
Vista de alto nivel
Section titled “Vista de alto nivel”┌──────────────────────────────────────────────────────────┐│ USUARIO (Browser) │└─────────────────────────┬────────────────────────────────┘ │┌─────────────────────────▼────────────────────────────────┐│ COMPONENTES VUE ││ (AE* de ecom-components + componentes propios) │└─────────┬──────────────────────────────────┬─────────────┘ │ │┌─────────▼─────────┐ ┌──────────▼──────────┐│ COMPOSABLES │ │ PINIA STORES ││ (lógica de UI) │ │ (estado global) │└─────────┬─────────┘ └──────────┬──────────┘ │ │┌─────────▼──────────────────────────────────▼─────────────┐│ useServices() ││ (punto central de acceso a datos con fallback i18n) │└─────────────────────────┬────────────────────────────────┘ │┌─────────────────────────▼────────────────────────────────┐│ VERTICAL CONFIG (inspiria.ts) ││ (registra servicios, API URLs, colores, metadata) │└─────────────────────────┬────────────────────────────────┘ │┌─────────────────────────▼────────────────────────────────┐│ SERVICES (fetchApi → Strapi CMS) ││ + Stripe + Zoho + ElevenLabs │└──────────────────────────────────────────────────────────┘Principio clave: Inversión de dependencias
Section titled “Principio clave: Inversión de dependencias”Los componentes nunca importan servicios directamente. El flujo siempre es:
- El componente llama a
useServices('getChannels') useServicesbusca la funcióngetChannelsenvisibleVertical.services- La vertical (Inspiria) tiene registrada:
getChannels: channelsService.getChannels - El servicio usa
httpClientque conoce la URL base de la API según la vertical
Beneficio: Cambiar de vertical (marca) no requiere tocar un solo componente ni composable. Solo se cambia la configuración.
El sistema Multi-Vertical
Section titled “El sistema Multi-Vertical”Qué es una vertical
Section titled “Qué es una vertical”Una vertical es una marca/producto educativo independiente que comparte el mismo código base. Cada vertical define:
- Su dominio/subdominio propio
- Sus colores y branding
- Sus propias APIs y servicios
- Sus traducciones específicas
- Sus páginas exclusivas (algunas verticales no tienen “libros”, por ejemplo)
- Su Google Analytics y Search Console
- Su configuración de Zoho CRM
Estado actual
Section titled “Estado actual”Actualmente existe una sola vertical activa: Inspiria Dental. El sistema está diseñado y preparado para agregar más sin modificar componentes.
Cómo se determina qué vertical está activa
Section titled “Cómo se determina qué vertical está activa”| Entorno | Mecanismo | Ejemplo |
|---|---|---|
| Producción | Subdominio de la URL | inspiriadental.com → vertical con subdomain: 'inspiriadental' |
| Desarrollo | Puerto del servidor | :3000 → índice 0 del array, :3001 → índice 1, etc. |
Código relevante en app/core/utils.ts:
export function selectVertical(): Verticals { return isProduction() ? verticalSearchEngine() : selectVerticalForPort()}Y en app/core/config.ts:
export const VERTICAL_LIST = [INSPIRIA] // Array de todas las verticalesexport const visibleVertical = selectVertical() // La vertical activa para esta instanciaCómo agregar una nueva vertical
Section titled “Cómo agregar una nueva vertical”- Crear
app/constants/verticals/nuevaVertical.tsimplementando la interfazVerticals - Configurar: nombre, subdominio, colores, URLs de API, analytics
- Registrar las funciones de servicio en el objeto
services(puedes reutilizar servicios existentes o crear nuevos) - Agregar traducciones en
app/locales/nuevaVertical/{es,en,pt-BR}/ - Agregar al array
VERTICAL_LISTenapp/core/config.ts - Configurar subdominio en AWS CloudFront
- Crear el content-type correspondiente en Strapi
Flujo de datos: ejemplo completo
Section titled “Flujo de datos: ejemplo completo”Usuario abre /courses
Section titled “Usuario abre /courses”1. Nuxt Router detecta pages/courses/index.vue2. Middleware auth.global.ts → valida sesión JWT3. Middleware language.global.ts → aplica idioma (localStorage o browser)4. La página ejecuta: const { data, isPending } = useServices('getCourses')5. useServices internamente: a. Lee visibleVertical.services.getCourses → coursesService.getCourses b. Llama con locale actual: getCourses('es') c. coursesService usa httpClient.get({ resource: 'courses', params: QUERY }) d. httpClient construye: GET https://api.inspiriadental.com/api/courses?populate=*&locale=es e. Si datos vacíos → reintenta con fallback ('en') f. Merge profundo de ambas respuestas para rellenar campos sin traducir6. data reactivo se actualiza → componente re-renderizaUsuario compra un producto
Section titled “Usuario compra un producto”1. Click "Comprar" → useBuyFlow.startBuyFlow(type, products)2. ¿Está logueado? NO → guarda {type, products} en localStorage → redirige a /login Tras login → resumeBuyFlowAfterLogin(email) SÍ → continúa3. ¿Ya tiene el producto? → redirige al contenido4. ¿Es candidato a suscripción? → muestra popup AESubscriptionPopupContent5. useCheckout crea sesión Stripe con metadatos del producto6. Redirige al usuario a Stripe checkout URL7. Stripe redirige a /gracias tras pago exitoso8. Zoho CRM recibe webhook con datos del eventoInicialización de la app
Section titled “Inicialización de la app”Cuando la app arranca, el plugin 01.initApp.client.ts ejecuta (en orden):
applyFavicon()— pone el favicon de la vertical activaaddStandardSeo()— meta tags SEO baseaddVerticalColors()— inyecta CSS variables de colores de la vertical en:rootanalyticsInjection()— carga Google Analytics con el código de la verticalsearchConsoleInjection()— meta tag de verificaciónaddPrimaryColor()— aplica color primario como tema
En cada navegación de página:
documentLanguage()— actualiza<html lang="...">
Diagrama de dependencias entre capas
Section titled “Diagrama de dependencias entre capas”Componentes → nunca acceden directamente a: services, fetchApi, stores de auth → sí acceden a: useServices, composables propios, stores (read)
Composables → pueden usar: useServices, stores, otros composables → nunca acceden a: httpClient directamente
Stores → pueden usar: useServices (para fetch data) → nunca acceden a: httpClient directamente, otros stores (evitar circular)
Services → solo usan: httpClient, zohoClient → no tienen acceso a: stores, composables, componentes