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.
Resumen ejecutivo
tenant_id en todas las tablas operativas.
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.
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
- architecture/
- 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.js | Levanta el proceso Node, puerto y graceful shutdown. |
app.js | Crea Express sin levantar puerto para facilitar tests. |
container.js | Registra dependencias, repositorios, servicios, providers, event bus y cache. |
config/index.js | Compone y valida toda configuracion. |
PostgreSQL y tenancy
pool.js | Pool eficiente, limites conservadores y readiness. |
query.js | Wrapper de queries parametrizadas, metricas, logs y timeouts. |
transaction.js | Transacciones atomicas con rollback seguro. |
tenantQueryGuard.js | Evita queries tenant-scoped sin tenantId. |
Seguridad y sesiones
auth.middleware.js | Verifica access token, firma, expiracion y sesion. |
tenantContext.middleware.js | Inyecta tenant seguro desde JWT/sesion. |
jwtService.js | Firma y verifica JWT de corta duracion. |
refreshTokenService.js | Rotacion, hashing, revocacion y reuse detection. |
Operacion y experiencia
requestContext.js | Mantiene requestId, tenantId, userId, locale, currency y correlationId. |
Money.js | Representa montos con precision exacta. |
TaxEngine.js | Calcula impuestos desacoplados de ventas, compras y caja. |
service-worker.js | Cache 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
auth.controller valida DTO y llama login.usecase.passwordHasher.sub, tenant_id, session_id, roles, version de permisos, plan y jti.HttpOnly, Secure, SameSite. Access en cookie segura para SSR o header controlado para API.jti o session_id.9. Flujo multi-tenant
tenant_id jamas viene del frontend. Solo se obtiene desde JWT, sesion segura o contexto backend.
auth.middleware verifica token, firma, expiracion y sesion.tenantContext.middleware extrae tenant_id desde JWT/sesion y crea el contexto central.subscription.middleware valida tenant activo, plan, limites y vencimiento.moduleAccess.middleware verifica si el plan permite el modulo solicitado.rbac.middleware valida permiso por modulo y accion.context.tenantId.WHERE tenant_id = $tenantId.Como evitar fuga de datos
Todo repositorio operativo recibe context y aplica filtros por tenant_id.
Usar (tenant_id, id), (tenant_id, code) y (tenant_id, document_number).
Bloquear ejecucion de consultas operativas sin tenant en repositorios multi-tenant.
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
11. Flujo de inventario
Ejemplo de recepcion de stock con motor logistico configurable por producto.
receiveStock.usecase abre una transaccion PostgreSQL.product_control_profile.inventory_movements.stock_balances por tenant, producto, almacen, ubicacion y dimensiones de control.stockReceived para proyecciones, cache y reportes.Motor logistico configurable
Control basico por SKU, almacen y cantidad.
Dimensiones obligatorias para industrias con caducidad o trazabilidad sanitaria.
Seleccion automatica por orden de entrada o fecha de vencimiento.
Cada unidad tiene identidad y estado individual.
Talla, color, modelo, dimensiones y atributos extensibles.
Unidades, conversiones, cajas, packs, pallets y peso/volumen.
Almacen, zona, pasillo, rack, nivel y bin location.
Separacion de stock para pedidos, picking y despacho.
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
currency_code, exchange_rate_id, moneda base y totales.NUMERIC o enteros menores; nunca float.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_atsi 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 UPDATEen 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 |
|---|---|
cacheStore | Configuracion de tenant, permisos, planes, catalogos frecuentes e invalidacion por eventos. |
sessionStore | Sesiones SSR si se habilitan sesiones server-side. |
rateLimitStore | Rate limiting distribuido por IP, usuario y tenant. |
lockManager | Locks cortos para operaciones concurrentes. |
tokenRevocationStore | Revocacion inmediata de JWT, JTI y session ids. |
queueClient | Colas para auditoria, notificaciones, reportes e invalidacion. |
pubSub | Eventos 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 |
|---|---|
currencies | Codigo, nombre, simbolo, decimales, modo de redondeo y estado. |
tenant_currency_settings | Moneda base, moneda de visualizacion y locale del tenant. |
exchange_rates | Tasas por tenant, moneda origen, moneda destino, fuente y vigencia. |
| Documentos monetarios | Guardan moneda propia, tasa historica usada, totales y total en moneda base. |
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
TaxEnginecalcula 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.jsonyen.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_iddesde 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
requestIdytenantId. - Tests de aislamiento multi-tenant.
24. Errores criticos que deben evitarse
- Usar
tenant_idenviado 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
- Mantener monolito modular hasta que existan limites reales de equipo, despliegue o carga.
- Separar workers antes que microservicios.
- Agregar PgBouncer y replicas de lectura cuando los reportes crezcan.
- Particionar tablas historicas de movimientos y auditoria por tiempo o tenant cuando el volumen lo exija.
- Introducir outbox pattern para eventos criticos e integraciones externas.
- Usar feature flags por tenant y plan.
- Implementar billing y metering por usuarios, documentos, SKUs, almacenes y notificaciones.
- Crear pruebas contractuales por modulo antes de exponer integraciones B2B.
- Preparar despliegue regional con configuracion por pais, moneda, impuestos y locale.
- 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.csso 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.