Skip to content

Arquitectura General - Portal PWA Multi-Tenant

Visión General

El Portal PWA de Clientes es un sistema multi-tenant por dominio donde cada empresa tiene su propio dominio web que identifica automáticamente al tenant, permitiendo a los clientes acceder a sus cuentas corrientes, consultar deudas y realizar pagos online.

Principios Arquitectónicos

1. Separación de Frontend y Backend

DECISIÓN CLAVE: Desarrollar frontend y backend de forma separada pero integrada.

Frontend PWA (Nuevo Proyecto)

  • Ubicación: Repositorio independiente
  • Tecnología: PWA con React
  • Propósito: Aplicación optimizada para clientes (mobile-first, instalable, ligera)
  • Independencia: Separada del ERP administrativo
  • Despliegue: Build estático servido por servidor web

Backend API (Extensión del Servidor Existente)

  • Ubicación: Servidor existente de Bautista (reutiliza infraestructura)
  • Nuevos endpoints: /portal/*
  • Reutilización: Modelos existentes (Cliente, CuentaCorriente, Servicios de recibos)
  • Beneficio: No duplica lógica de negocio

2. Multi-Tenancy (Multi-Inquilino) por Dominio

Patrón: Una sola aplicación para todos los clientes. El dominio identifica al tenant (inquilino).

https://ctacte.empresaA.com.ar → Tenant: EmpresaA (DB: empresa_a, Schema: public)
https://portal.clubXYZ.com     → Tenant: ClubXYZ (DB: club_xyz, Schema: public)
https://clientes.ferreteriaZ.ar → Tenant: FerreteriaZ (DB: ferreteria_z, Schema: public)

Ventajas:

  • ✅ Cada empresa tiene su propia identidad (dominio personalizado)
  • ✅ Branding completamente personalizable
  • ✅ Aislamiento de datos por tenant
  • ✅ Escalabilidad: agregar tenants sin cambiar código
  • ✅ Mantenimiento: una sola codebase

Diagrama de Arquitectura

┌─────────────────────────────────────────────────────────────┐
│                    CLIENTE (Navegador/PWA)                  │
│  https://ctacte.empresaA.com.ar o https://portal.clubXYZ.com│
└────────────────────────┬────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│              FRONTEND PWA (Proyecto Separado)               │
│  - PWA con React                                            │
│  - Cliente API HTTP                                         │
│  - Instalable como app móvil                                │
│  - Build estático                                           │
└────────────────────────┬────────────────────────────────────┘
                         │ Peticiones HTTP

┌─────────────────────────────────────────────────────────────┐
│           BACKEND API (Servidor Existente)                  │
│                                                             │
│  Middleware: Autenticación + Conexión multi-tenant         │
│  Controllers: Auth, CtaCte, Pagos, Cupones                 │
│  Services: Identificación, Consultas, Pagos, Cupones       │
│  Models: Nuevos (PortalClient, Payments, Cupones)          │
│         + Reutilizados (Cliente, CuentaCorriente)           │
└────────────────────────┬────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│           POSTGRESQL MULTI-TENANT                            │
│                                                             │
│  DB ini: Configuración global de tenants                    │
│  DB {tenant}: Datos del portal y clientes por tenant        │
└─────────────────────────────────────────────────────────────┘

Flujo de Datos

1. Resolución de Tenant

1. Cliente accede a un dominio (ej: ctacte.empresaA.com.ar)

2. Middleware de autenticación extrae el dominio

3. Busca en tabla tenant_domains para identificar el tenant

4. Obtiene configuración: tenant_id, database, branding

5. Middleware de conexión configura acceso a BD del tenant

6. Request procede con contexto de tenant inyectado

2. Autenticación de Cliente

1. Cliente ingresa DNI/CUIT

2. POST /portal/auth/identify-client

3. Servicio de identificación:
   - Busca en portal_clients por DNI
   - Si no existe, busca en tabla de clientes existente
   - Crea registro en portal_clients si es primera vez
   - Valida que no esté bloqueado

4. Genera session o token con datos del cliente

5. Frontend guarda credenciales

6. Peticiones subsiguientes incluyen autenticación

3. Consulta de Deudas

1. GET /portal/deudas?cliente_id=123

2. Middleware verifica autenticación

3. Controller de cuenta corriente recibe request

4. Service de cuenta corriente:
   - Usa modelo existente de CuentaCorriente
   - Obtiene movimientos sin pago
   - Enriquece con días de vencimiento

5. Retorna JSON con deudas pendientes

4. Pago Online

1. Cliente selecciona facturas → POST /portal/pagos/iniciar

2. Servicio de pagos:
   - Crea preferencia en gateway de pago
   - Guarda registro con estado "pending"

3. Frontend redirige al gateway de pago

4. Cliente completa el pago en el gateway

5. Gateway envía notificación al backend

6. Servicio de pagos procesa webhook:
   - Valida la notificación
   - Verifica unicidad
   - Si es aprobado: actualiza registro y genera recibo

7. Cliente retorna a página de éxito

Decisiones Arquitectónicas

¿Por qué frontend separado?

AspectoFrontend SeparadoFrontend Integrado en /public/
Optimización PWA✅ Optimizado específicamente para móviles❌ Comparte código con ERP admin
Tamaño de Bundle✅ Ligero (solo portal)❌ Pesado (incluye ERP)
Instalabilidad✅ PWA pura⚠️ Complejo
Mantenimiento✅ Código independiente❌ Acoplado al ERP
Despliegue✅ Build estático en Apache❌ Junto con ERP
Servidor✅ Sin Node.js en producción❌ Requiere infraestructura PHP

¿Por qué reutilizar backend?

AspectoReutilizar /server/Backend Nuevo
Lógica de negocio✅ Reutiliza CuentaCorriente, Recibos❌ Duplica código
Mantenimiento✅ Una sola fuente de verdad❌ Sincronizar cambios
Multi-tenant✅ Ya implementado❌ Re-implementar
Testing✅ Tests existentes❌ Crear desde cero

¿Por qué multi-tenant por dominio?

Alternativas evaluadas:

  1. Subdominios: empresaA.portal.bautista.com

    • ❌ Menos profesional
    • ❌ Branding limitado
  2. Parámetro en ruta: portal.bautista.com/empresaA

    • ❌ SEO pobre
    • ❌ URLs poco amigables
  3. Dominio completo: ctacte.empresaA.com.ar

    • ✅ Branding completo
    • ✅ Identidad propia
    • ✅ SSL por empresa
    • ✅ Escalable

Ventajas del Diseño

  1. Escalabilidad: Agregar nuevos tenants sin cambiar código
  2. Mantenimiento: Una base de código para todos los clientes
  3. Reutilización: Aprovecha infraestructura existente (ConnectionManager, modelos, servicios)
  4. Seguridad: Aislamiento multi-tenant por schema
  5. Rendimiento: Frontend PWA optimizado, backend con pooling de conexiones
  6. Experiencia de Usuario: Instalable como app nativa, capacidad offline
  7. Branding: Completamente personalizable por empresa

Limitaciones y Compromisos (Trade-offs)

  1. Complejidad DNS: Requiere configurar dominio por tenant
  2. SSL Wildcard: Necesita certificado wildcard o certificados individuales
  3. Incorporación: Proceso de alta de tenant requiere configuración manual (mitigado con scripts CLI)
  4. Caché: Service Worker debe cachear por dominio

Próximos Pasos

  1. Revisar database/schema.md para modelo de datos
  2. Leer backend/README.md para implementación del backend
  3. Consultar frontend/README.md para PWA
  4. Ver roadmap/phase-1-mvp.md para comenzar implementación