Documento interno de arquitectura

quipuSAAS - Arquitectura Inicial

Plataforma Web SaaS multi-tenant para logistica, inventario inteligente, trazabilidad, abastecimiento, operaciones comerciales, almacenes, despacho, portal B2B, caja y control financiero operativo. Este documento define la base tecnica antes de desarrollar logica de negocio.

Backend: Node.js + Express.js Frontend: EJS SSR Base de datos: PostgreSQL Cache y colas: Redis ready Auth: JWT corto + refresh rotation PWA + Push Notifications Template UI: PHOENIX No PHP

Resumen ejecutivo

Modelo Modular monolith empresarial con limites claros por dominio y preparado para extraer servicios si el crecimiento lo exige.
Tenancy Base de datos compartida con aislamiento obligatorio por tenant_id en todas las tablas operativas.
Logistica Motor configurable por producto para lotes, series, FIFO, variantes, empaque, ubicaciones y trazabilidad.
Escalabilidad PostgreSQL transaccional, Redis desacoplado, workers preparados, cache inteligente y observabilidad desde el inicio.

1. Principios utilizados

Multi-tenancy por diseno

Toda entidad operativa pertenece a un tenant_id. El frontend nunca envia ni decide el tenant.

Modular monolith

Cada dominio vive en un modulo aislado con rutas, controladores, casos de uso, servicios, repositorios, DTOs, validadores, eventos y tests.

Clean Architecture adaptada

El dominio no depende de Express, PostgreSQL, Redis ni EJS. Las dependencias externas viven en infraestructura.

Service Layer

Los casos de uso coordinan reglas, transacciones, eventos y repositorios. Los controladores se mantienen delgados.

Repository Pattern

La persistencia se encapsula en repositorios multi-tenant con filtros, constraints y transacciones consistentes.

APIs versionadas

La API nace bajo /api/v1 para proteger integraciones, apps, portal B2B y clientes futuros.

SSR rapido con EJS

Las vistas se renderizan en servidor con layouts PHOENIX, assets optimizados y reglas de seguridad SSR.

Seguridad por capas

Helmet, CORS, CSRF, RBAC, validacion, auditoria, JWT corto, refresh rotation y manejo centralizado de errores.

Eventos preparados

Los procesos pesados se disparan por eventos y workers. El request no debe cargar trabajo de reportes, push, PDF o recosteo.

2. Mapa de capas

La separacion mantiene el dominio limpio y evita que decisiones de framework, base de datos o proveedores externos contaminen la logica central.

Entrada
Express routes Controllers EJS SSR REST /api/v1 PWA
Middlewares
requestId auth tenantContext subscription RBAC audit
Aplicacion
usecases services DTOs validators transactions
Dominio
entities policies strategies domain events Money TaxEngine
Infraestructura
PostgreSQL Redis Queues Logger Web Push Storage

3. Stack obligatorio

Area Tecnologia Rol arquitectonico
Backend Node.js + Express.js API REST versionada, SSR, middlewares, workers y orquestacion de modulos.
Frontend EJS SSR + Template PHOENIX Interfaz administrativa rapida, segura y optimizada para operaciones empresariales.
Base de datos PostgreSQL Fuente transaccional principal, constraints, indices, transacciones, JSONB controlado y particionamiento futuro.
Cache / colas / sesiones Redis Cache, rate limiting, locks, revocacion JWT, pub/sub, colas y workers preparados.
Autenticacion JWT corto + refresh tokens Rotacion segura, revocacion de sesiones, deteccion de reuse y cookies seguras.
PWA Manifest + Service Worker Offline basico, assets cacheados, actualizaciones seguras y push notifications.

4. Arbol visual de carpetas y archivos

Esta estructura esta pensada para crecer durante anos sin perder claridad. Las carpetas se agrupan por responsabilidad, no por tipo tecnico global, excepto en infraestructura y shared donde conviene centralizar adaptadores y utilidades.

  • quipuSAAS/
    • package.json dependencias, scripts y metadata
    • .env.example variables requeridas sin secretos reales
    • README.md guia inicial del proyecto
    • docs/
      • architecture/
        • initial-architecture.md
        • initial-architecture.html
        • module-boundaries.md
        • database-strategy.md
        • security-strategy.md
        • tenancy-strategy.md
      • decisions/
        • ADR-0001-modular-monolith.md
        • ADR-0002-shared-database-tenancy.md
    • src/
      • server.js
      • app.js
      • bootstrap/
        • loadEnv.js
        • container.js
        • routes.js
        • middlewares.js
        • gracefulShutdown.js
        • workers.js
      • config/
        • index.js
        • app.config.js
        • auth.config.js
        • cors.config.js
        • database.config.js
        • redis.config.js
        • security.config.js
        • pwa.config.js
        • i18n.config.js
        • money.config.js
        • tax.config.js
        • queue.config.js
        • observability.config.js
        • phoenix.config.js
      • shared/
        • constants/
        • errors/
        • http/
        • utils/
        • validation/
        • events/
        • money/
        • tax/
        • tracing/
        • i18n/
      • infrastructure/
        • database/postgres/
        • redis/
        • queues/
        • logger/
        • security/
        • storage/
        • notifications/
        • observability/
      • interfaces/
        • http/routes/
        • http/middlewares/
        • http/controllers/
        • views/layouts/
        • views/partials/
        • views/pages/
        • views/phoenix/
      • modules/
        • core/ tenants, subscriptions, audit
        • auth/ login, sesiones, refresh tokens
        • rbac/ roles, permisos, politicas
        • catalog/ productos, variantes, empaque, atributos
        • inventory/ stock, kardex, lotes, series, reservas
        • warehouse/ almacenes, racks, ubicaciones
        • purchasing/ compras y recepcion
        • sales/ ventas, pedidos, despacho
        • b2b/ portal B2B
        • cash/ caja y movimientos monetarios
        • finance/ monedas y tipo de cambio
        • tax/ impuestos configurables
        • reports/ reportes operativos
        • notifications/ notificaciones y dispositivos
      • workers/
      • public/
      • tests/

5. Responsabilidad por carpeta

Carpeta Responsabilidad Regla importante
docs/ Documentacion viva, ADRs, estrategias y reglas arquitectonicas. Las decisiones criticas deben quedar documentadas y versionadas.
src/bootstrap/ Inicio de aplicacion, carga de configuracion, container, rutas, middlewares y shutdown. No contiene reglas de negocio.
src/config/ Configuracion centralizada por area: app, auth, database, Redis, seguridad, PWA, dinero, impuestos. Ningun modulo debe leer process.env directamente.
src/shared/ Utilidades transversales puras: errores, validacion, eventos, money, tax, tracing e i18n. No debe depender de Express ni PostgreSQL.
src/infrastructure/ Adaptadores tecnicos: PostgreSQL, Redis, colas, logger, seguridad, storage, push, health checks. Implementa puertos, no define reglas centrales del negocio.
src/interfaces/http/ Entrada HTTP: rutas, middlewares y controladores comunes. Valida, autentica y delega a casos de uso.
src/interfaces/views/ Vistas EJS SSR, layouts, partials y adaptacion PHOENIX. La UI no reemplaza permisos backend.
src/modules/ Dominios empresariales independientes. Cada modulo debe tener boundaries claros y bajo acoplamiento.
src/workers/ Procesos asincronos: auditoria, notificaciones, proyecciones, reportes, invalidacion cache. No hacer en request lo que pertenece a un worker.
src/public/ Assets, manifest, service worker, offline page, PHOENIX y scripts PWA/push. No cachear datos sensibles sin estrategia por tenant/usuario.

6. Archivos criticos

Arranque y configuracion

server.jsLevanta el proceso Node, puerto y graceful shutdown.
app.jsCrea Express sin levantar puerto para facilitar tests.
container.jsRegistra dependencias, repositorios, servicios, providers, event bus y cache.
config/index.jsCompone y valida toda configuracion.

PostgreSQL y tenancy

pool.jsPool eficiente, limites conservadores y readiness.
query.jsWrapper de queries parametrizadas, metricas, logs y timeouts.
transaction.jsTransacciones atomicas con rollback seguro.
tenantQueryGuard.jsEvita queries tenant-scoped sin tenantId.

Seguridad y sesiones

auth.middleware.jsVerifica access token, firma, expiracion y sesion.
tenantContext.middleware.jsInyecta tenant seguro desde JWT/sesion.
jwtService.jsFirma y verifica JWT de corta duracion.
refreshTokenService.jsRotacion, hashing, revocacion y reuse detection.

Operacion y experiencia

requestContext.jsMantiene requestId, tenantId, userId, locale, currency y correlationId.
Money.jsRepresenta montos con precision exacta.
TaxEngine.jsCalcula impuestos desacoplados de ventas, compras y caja.
service-worker.jsCache offline, actualizacion segura y estrategia PWA.

7. Modulos del dominio

Cada modulo debe poder evolucionar sin romper a los demas. La comunicacion entre modulos se hace por casos de uso, servicios publicos del modulo o eventos, no por acceso directo a tablas ajenas.

Core: tenants, subscriptions y audit

Contiene la identidad de cada empresa, estado del tenant, planes, modulos habilitados, limites, uso y auditoria transversal.

Regla: todo modulo operativo depende del contexto del tenant, pero no decide la suscripcion.

Auth y RBAC

Administra usuarios, sesiones, refresh tokens, roles, permisos, politicas y versiones de permisos.

Regla: el permiso se valida en backend por modulo y accion, por ejemplo inventory.stock.read.

Catalog

Gestiona productos, perfiles de control logistico, variantes, unidades de medida, empaques y atributos extensibles.

Regla: la flexibilidad vive en perfiles y estrategias, no en condicionales dispersos.

Inventory

Controla stock por almacen, ubicacion, kardex, movimientos, lotes, vencimientos, series, transferencias, reservas y reposicion.

Regla: inventory_movements es historico e inmutable; stock_balances es una proyeccion optimizada.

Warehouse

Modela almacenes, zonas, pasillos, racks, niveles, ubicaciones, capacidad, ocupacion, picking, packing y volumetria.

Regla: la ubicacion fisica debe poder participar como dimension de stock y trazabilidad.

Purchasing, Sales y B2B

Compras maneja ordenes, recepcion, costos y cuentas por pagar basicas. Ventas maneja cotizaciones, pedidos, preparacion y despacho. B2B expone catalogo y pedidos a clientes autorizados.

Regla: compras y ventas no calculan stock directamente; solicitan casos de uso del modulo inventario.

Cash, Finance y Tax

Caja controla ingresos, egresos, arqueo, pagos y conciliacion simple. Finance resuelve monedas y tipos de cambio. Tax resuelve impuestos por pais, tenant y documento.

Regla: dinero e impuestos se calculan en motores compartidos, nunca duplicados por modulo.

Reports y Notifications

Reportes operativos y notificaciones se apoyan en cache, colas, workers y consultas optimizadas.

Regla: reportes pesados, push y recalculos se ejecutan fuera del request principal.

8. Flujo de autenticacion

1
IngresoEl usuario ingresa credenciales desde SSR o API.
2
Validacionauth.controller valida DTO y llama login.usecase.
3
Resolucion tenantEl tenant se resuelve por dominio, subdominio, slug seguro o invitacion, no por input libre.
4
CredencialesLa password se verifica con passwordHasher.
5
Access tokenSe emite JWT corto con sub, tenant_id, session_id, roles, version de permisos, plan y jti.
6
Refresh tokenSe emite token opaco, aleatorio, hasheado en DB y asociado a sesion, dispositivo e IP aproximada.
7
Cookies segurasRefresh en cookie HttpOnly, Secure, SameSite. Access en cookie segura para SSR o header controlado para API.
8
RotacionCada refresh rota el token. Reuso de token antiguo revoca la familia de sesiones.
9
RevocacionLogout revoca sesion en DB y Redis; Redis permite invalidacion inmediata por jti o session_id.

9. Flujo multi-tenant

Regla absoluta: tenant_id jamas viene del frontend. Solo se obtiene desde JWT, sesion segura o contexto backend.
1
Autenticacionauth.middleware verifica token, firma, expiracion y sesion.
2
Tenant contexttenantContext.middleware extrae tenant_id desde JWT/sesion y crea el contexto central.
3
Suscripcionsubscription.middleware valida tenant activo, plan, limites y vencimiento.
4
Modulo habilitadomoduleAccess.middleware verifica si el plan permite el modulo solicitado.
5
Permisorbac.middleware valida permiso por modulo y accion.
6
RepositorioEl caso de uso llama al repositorio con context.tenantId.
7
Query protegidaToda query aplica WHERE tenant_id = $tenantId.
8
AuditoriaSe registra accion, tenant, usuario, entidad, metadata, IP y request id.

Como evitar fuga de datos

Queries tenant-scoped

Todo repositorio operativo recibe context y aplica filtros por tenant_id.

Constraints compuestas

Usar (tenant_id, id), (tenant_id, code) y (tenant_id, document_number).

Tenant Query Guard

Bloquear ejecucion de consultas operativas sin tenant en repositorios multi-tenant.

Tests de aislamiento

Probar que tenant A nunca lee ni modifica datos del tenant B.

10. Flujo de request HTTP

Cliente/PWA/SSR
  -> Express
  -> requestId
  -> Helmet/CORS/body limits/compression
  -> rate limit
  -> locale
  -> CSRF si SSR muta estado
  -> auth
  -> tenant context
  -> subscription/module access
  -> RBAC
  -> validator DTO
  -> controller
  -> use case
  -> service/domain
  -> repository
  -> PostgreSQL/Redis
  -> event bus
  -> audit
  -> response EJS o JSON
Separacion de responsabilidades: el controlador no contiene reglas de negocio; el caso de uso coordina transacciones, repositorios y eventos; el repositorio no decide permisos; los middlewares no ejecutan logica de negocio.

11. Flujo de inventario

Ejemplo de recepcion de stock con motor logistico configurable por producto.

1
Documento fuenteCompras confirma la recepcion.
2
TransaccionreceiveStock.usecase abre una transaccion PostgreSQL.
3
Perfil logisticoCarga producto y product_control_profile.
4
EstrategiasSelecciona lote, vencimiento, serial, variante, ubicacion, empaque o FIFO segun corresponda.
5
ValidacionesValida datos obligatorios segun el perfil logistico.
6
KardexInserta movimiento en inventory_movements.
7
SaldoActualiza stock_balances por tenant, producto, almacen, ubicacion y dimensiones de control.
8
EventosPublica stockReceived para proyecciones, cache y reportes.

Motor logistico configurable

Producto simple

Control basico por SKU, almacen y cantidad.

Lote y vencimiento

Dimensiones obligatorias para industrias con caducidad o trazabilidad sanitaria.

FIFO

Seleccion automatica por orden de entrada o fecha de vencimiento.

Serializado

Cada unidad tiene identidad y estado individual.

Variantes

Talla, color, modelo, dimensiones y atributos extensibles.

Empaque y pallet

Unidades, conversiones, cajas, packs, pallets y peso/volumen.

Ubicaciones fisicas

Almacen, zona, pasillo, rack, nivel y bin location.

Reservas

Separacion de stock para pedidos, picking y despacho.

Trazabilidad

Cadena proveedor, recepcion, almacen, picking, despacho y cliente.

12. Flujo de trazabilidad

La trazabilidad se construye desde movimientos inmutables. El saldo es una proyeccion, no la historia.

Elemento Funcion
inventory_movements Kardex inmutable con tenant, producto, origen, destino, lote, serie, vencimiento, usuario, documento fuente y timestamp.
stock_balances Saldo agregado optimizado para consultas rapidas.
lots y expirations Control de lotes, vencimientos y retiros por fecha.
serials Estado y ubicacion actual de cada unidad serializada.
Documentos fuente Compras, ventas, transferencias, ajustes y despacho referencian movimientos.

13. Flujo monetario

1
DocumentoTodo documento define currency_code, exchange_rate_id, moneda base y totales.
2
TenantEl tenant define moneda base, moneda de visualizacion y formato regional.
3
PrecisionMontos en NUMERIC o enteros menores; nunca float.
4
ConversionSe guarda la tasa historica usada en el documento.
5
RedondeoReglas centralizadas por moneda y politica fiscal.

14. Estrategia PostgreSQL

Modelado

  • Usar UUID para IDs publicos y distribucion futura.
  • Toda tabla operacional incluye tenant_id NOT NULL.
  • Campos base: id, tenant_id, timestamps, created_by, updated_by, deleted_at si aplica.
  • JSONB solo para atributos variables, metadata y configuraciones controladas.

Integridad

  • Constraints por tenant: UNIQUE (tenant_id, code).
  • FK compuestas cuando sea critico evitar cruces entre tenants.
  • Transacciones para stock, caja, pagos, recepcion y despacho.
  • Locking selectivo con SELECT ... FOR UPDATE en saldos sensibles.

Performance

  • Indices compuestos: (tenant_id, id), (tenant_id, product_id), (tenant_id, created_at).
  • Particionamiento futuro para movimientos, auditoria, notificaciones y caja.
  • Vistas o materialized views para reportes pesados.
  • Paginacion obligatoria en listados.

Acceso

  • Wrapper unico para queries parametrizadas.
  • Repositorios por modulo.
  • Migraciones versionadas y revisables.
  • Tests de aislamiento por tenant.

15. Estrategia Redis

Redis se integra por adaptadores. Si en desarrollo no esta activo, puede existir fallback de memoria solo para entorno local, nunca para produccion multi-instancia.

Adaptador Uso
cacheStoreConfiguracion de tenant, permisos, planes, catalogos frecuentes e invalidacion por eventos.
sessionStoreSesiones SSR si se habilitan sesiones server-side.
rateLimitStoreRate limiting distribuido por IP, usuario y tenant.
lockManagerLocks cortos para operaciones concurrentes.
tokenRevocationStoreRevocacion inmediata de JWT, JTI y session ids.
queueClientColas para auditoria, notificaciones, reportes e invalidacion.
pubSubEventos entre procesos e invalidacion de cache.

16. Estrategia PWA

Archivos

  • manifest.json: nombre, iconos, display standalone, theme color y start URL.
  • service-worker.js: cache offline, assets versionados y fallback seguro.
  • offline.html: pagina minima sin datos sensibles.
  • pwa.js: registro del service worker y control de actualizaciones.

Reglas

  • Cachear assets estaticos y vistas no sensibles.
  • No cachear respuestas privadas sin clave por usuario y tenant.
  • Versionar caches y limpiar caches antiguos.
  • Activar service worker nuevo con estrategia controlada.

17. Estrategia Push Notifications

Modelo

  • device_subscriptions: tenant, user, endpoint, keys, provider, device metadata y estado.
  • notifications: tenant, tipo, titulo i18n, payload minimo y estado.
  • Providers desacoplados: Web Push primero, FCM futuro.

Seguridad operativa

  • El registro de dispositivo requiere usuario autenticado.
  • El envio no ocurre dentro del request; se publica evento y worker envia.
  • Payloads sin informacion sensible.
  • Rate limits por tenant.

18. Estrategia de seguridad

Headers

Helmet, CSP ajustada a PHOENIX, HSTS en produccion y politicas de recursos.

CORS

Origenes permitidos por config. No usar wildcard con credenciales.

CSRF

Obligatorio en formularios SSR que mutan estado.

XSS

EJS escapado por defecto, sanitizacion de HTML, CSP y evitar HTML crudo.

SQL injection

Solo queries parametrizadas. Prohibida concatenacion de inputs.

Validacion

DTOs por endpoint, schemas robustos y limites de payload.

Tokens

Access token breve, refresh opaco, rotacion y deteccion de reuse.

Cookies

HttpOnly, Secure, SameSite segun flujo.

Auditoria

Acciones sensibles por tenant, usuario, entidad, IP y request id.

Secrets

.env local, secrets manager en produccion, nunca commitear claves.

SSR

CSRF, escape EJS, permisos backend y menus por RBAC/plan.

PWA/Push

No cachear datos sensibles y usar payload push minimo.

19. Estrategia de performance

Optimizar desde el request

  • Pool PostgreSQL pequeno y eficiente al inicio.
  • SSR con layouts y partials reutilizables.
  • Evitar queries repetidas por vista.
  • Cachear permisos, configuracion de tenant, planes y catalogos frecuentes.
  • Evitar N+1 con cargas por lote.
  • Paginacion obligatoria.

No ejecutar dentro del request

  • Envio de emails o push masivos.
  • Generacion de PDFs pesados.
  • Reportes de alto volumen.
  • Recosteo o reprocesamiento de inventario.
  • Importaciones masivas.
  • Sincronizaciones externas.

20. Estrategia multi moneda

Tabla Proposito
currenciesCodigo, nombre, simbolo, decimales, modo de redondeo y estado.
tenant_currency_settingsMoneda base, moneda de visualizacion y locale del tenant.
exchange_ratesTasas por tenant, moneda origen, moneda destino, fuente y vigencia.
Documentos monetariosGuardan moneda propia, tasa historica usada, totales y total en moneda base.
Precision: nunca usar Number flotante para dinero. Usar decimal exacto, NUMERIC en PostgreSQL y reglas centralizadas de redondeo por moneda y pais.

21. Estrategia de impuestos

El motor tributario debe permitir IGV, IVA, VAT, precios con impuesto incluido, precios sin impuesto y porcentajes por pais o tenant.

Tablas

  • tax_profiles: politica del tenant y definicion de precios con/sin impuesto.
  • tax_rates: tasas por pais, region, tipo y vigencia.
  • tax_rules: reglas por producto, categoria, cliente, pais y documento.

Motor

  • TaxEngine calcula base imponible, impuesto, total y redondeo.
  • Ventas, compras y caja no implementan calculos fiscales propios.
  • Las reglas tributarias cambian por configuracion, no por forks de codigo.

22. Estrategia multi idioma

Organizacion

  • Traducciones en src/shared/i18n/locales/es.json y en.json.
  • Claves estables: inventory.movements.title, auth.login.invalid_credentials.
  • EJS recibe helper t(key, params).

Reglas

  • No hardcodear textos en controladores ni vistas.
  • Fechas, numeros y monedas se formatean por locale.
  • Los nombres propios del negocio pueden ser datos del tenant.

23. Buenas practicas obligatorias

  • Ningun endpoint acepta tenant_id desde frontend.
  • Toda query operacional filtra por tenant_id.
  • Toda mutacion relevante genera auditoria.
  • DTOs y validadores por endpoint.
  • Transacciones para stock, caja, pagos y despacho.
  • Eventos para procesos derivados.
  • Repositorios no devuelven datos de otro tenant aunque reciban IDs validos.
  • Permisos siempre validados en backend.
  • Paginacion y limites por defecto.
  • Errores centralizados sin stack trace en produccion.
  • Logs estructurados con requestId y tenantId.
  • Tests de aislamiento multi-tenant.

24. Errores criticos que deben evitarse

  • Usar tenant_id enviado por body, query o frontend.
  • Construir SQL concatenando strings con inputs.
  • Guardar dinero como float.
  • Mezclar reglas de negocio en controladores.
  • Hacer reportes pesados dentro del request.
  • Cachear respuestas privadas sin clave por tenant/usuario.
  • Usar JSONB para todo y perder constraints.
  • No versionar API.
  • No auditar cambios de inventario o caja.
  • Acoplar Redis o providers externos al dominio.
  • Hardcodear impuestos, monedas o textos.
  • Confiar en que ocultar botones en EJS equivale a seguridad.

25. Recomendaciones para escalamiento empresarial

  1. Mantener monolito modular hasta que existan limites reales de equipo, despliegue o carga.
  2. Separar workers antes que microservicios.
  3. Agregar PgBouncer y replicas de lectura cuando los reportes crezcan.
  4. Particionar tablas historicas de movimientos y auditoria por tiempo o tenant cuando el volumen lo exija.
  5. Introducir outbox pattern para eventos criticos e integraciones externas.
  6. Usar feature flags por tenant y plan.
  7. Implementar billing y metering por usuarios, documentos, SKUs, almacenes y notificaciones.
  8. Crear pruebas contractuales por modulo antes de exponer integraciones B2B.
  9. Preparar despliegue regional con configuracion por pais, moneda, impuestos y locale.
  10. Medir latencia p95, queries lentas, memoria, pool usage y tiempo de render SSR.

26. Uso del Template PHOENIX

Ubicacion

  • Assets vendor en src/public/assets/phoenix/.
  • Layouts adaptados en src/interfaces/views/phoenix/.
  • Personalizacion en public/assets/css/app.css o pipeline equivalente.

Reglas de uso

  • PHOENIX acelera UI, pero no entra al dominio.
  • Menus segun RBAC y modulos habilitados por plan.
  • Componentes visuales reutilizables como partials EJS.
  • No modificar vendor si puede resolverse con overrides propios.
Recordatorio: la UI puede ocultar opciones, pero la seguridad real siempre vive en backend.