Skip to content
GitHub

Orchestrator (Backend)

Orchestrator es la puerta de enlace API backend del sistema de contabilidad, construida en Node.js con Express y TypeScript e implementando una arquitectura basada en dominios. Sirve como fuente única de verdad para todas las operaciones de lógica empresarial, validación de datos y bases de datos de múltiples inquilinos. Orchestrator expone los puntos finales RESTful consumidos por la interfaz de Sebastopol y aplica autenticación, autorización y coherencia de datos en todas las operaciones.

Este documento cubre la arquitectura del Orchestrator, la organización del dominio, el flujo de middleware y los patrones de desarrollo. Para obtener información sobre la aplicación frontend, consulte Frontend (Sebastopol). Para obtener detalles sobre dominios específicos como el cálculo de nómina, consulte Sistema de nóminas y recursos humanos.

PaqueteVersiónPropósito
express5.1.0Marco del servidor HTTP
pg8.16.3Cliente PostgreSQL (nodo-postgres)
jsonwebtoken9.0.2Manejo de tokens JWT
bcryptjs3.0.2Hashing de contraseñas
helmet8.1.0Middleware de seguridad
cors2.8.5Intercambio de recursos entre orígenes
morgan1.10.1Registro de solicitudes HTTP
express-validator7.0.1Validación de entrada
cookie-parser1.4.7Análisis de cookies
dotenv17.2.1Variables ambientales
date-fns4.1.0Manipulación de fecha
puppeteer23.7.0Generación de PDF
jest29.6.1Marco de pruebas (dev)
supertest6.3.3Pruebas HTTP (dev)
typescript5.9.2Sistema de tipos (dev)

Tiempo de ejecución: Node.js 20+

El Orchestrator utiliza un patrón de factoría para crear la instancia de aplicación Express, habilitando testabilidad y arranque limpio del servidor:

flowchart TB
  A["createApp()"] --> P[Pipeline de Middleware]
  A --> H[Servidor HTTP]

  P --> R[Montaje de Rutas]
  P --> HM["helmet()"]
  P --> CS["cors()"]
  P --> MG["morgan('combined')"]
  P --> EJ["express.json()"]
  P --> CP["cookieParser()"]

  %% Rutas
  R --> HE["/health"]
  R --> TA["/api/tenant, /api/admin,<br/>/api/auth"]
  R --> PA["/api/parameters, /api/afc"]
  R --> AC["/api/accounting"]
  R --> RE["/api/remuneraciones/*,<br/>/api/employees"]

  %% Relación middleware -> grupos (como en el diagrama)
  HM --> TA
  CS --> PA
  MG --> AC
  EJ --> RE
  CP --> RE

La función createApp retorna una aplicación Express configurada sin iniciar el servidor, permitiendo importarla para testing:

Organización de Rutas Orientada al Dominio

Section titled “Organización de Rutas Orientada al Dominio”

Las rutas están organizadas por dominio de negocio, con cada archivo de ruta importado y montado en app.ts:

Registro Completo de Rutas:

Prefijo de RutaRuta de ArchivoDominioSchema de Base de Datos
/api/tenantroutes/command/tenant.tsCommandcommand.tenants
/api/tenant-dbroutes/command/tenant-db.tsCommandcommand.tenant_databases
/api/adminroutes/command/users.tsCommandcommand.users
/api/sessionsroutes/command/sessions.tsCommandcommand.sessions
/api/monitoringroutes/command/monitoring.tsCommandMétricas del sistema
/api/authroutes/command/auth.tsCommandAutenticación
/api/menuroutes/command/menu.tsCommandMenús dinámicos
/api/parametersroutes/common/parameters.tsCommonparametros.indicadores
/api/afcroutes/common/afc.tsCommonparametros.afc
/api/impuesto_2catroutes/common/impuesto_2cat.tsCommonparametros.impuesto_2cat
/api/accountingroutes/accounting.tsOperationsoperaciones_sii.*
/api/afproutes/remuneraciones/afp.tsRemuneracionesremuneraciones.afp
/api/attendanceroutes/remuneraciones/attendance.tsRemuneracionesremuneraciones.asistencia_dia
/api/cargosroutes/remuneraciones/cargos.tsRemuneracionesremuneraciones.cargos
/api/contractsroutes/remuneraciones/contracts.tsRemuneracionesremuneraciones.contratos
/api/departmentsroutes/remuneraciones/departments.tsRemuneracionesremuneraciones.departamentos
/api/employeesroutes/remuneraciones/employees.tsRemuneracionesremuneraciones.empleados
/api/apv_contractsroutes/remuneraciones/apv_contracts.tsRemuneracionesremuneraciones.contrato_apv
/api/isapre_contractsroutes/remuneraciones/isapre_contracts.tsRemuneracionesremuneraciones.contrato_isapre
/api/isapreroutes/remuneraciones/isapre.tsRemuneracionesremuneraciones.isapre
/api/remuneraciones/payrollroutes/remuneraciones/payroll.tsRemuneracionesremuneraciones.liquidaciones
/api/permissionsroutes/remuneraciones/permissions.tsRemuneracionesremuneraciones.permisos
/api/previsionesroutes/remuneraciones/previsiones.tsRemuneracionesSeguridad social agregada
/api/honorariosroutes/remuneraciones/honorarios.tsRemuneracionesremuneraciones.honorarios
/api/vacationsroutes/remuneraciones/vacations.tsRemuneracionesremuneraciones.vacaciones
/api/working_dayroutes/remuneraciones/working_day.tsRemuneracionesremuneraciones.jornadas
/api/generate-pdfroutes/remuneraciones/generate-pdf.tsRemuneracionesGeneración de PDF
/api/finiquitosroutes/remuneraciones/finiquitos.tsRemuneracionesremuneraciones.finiquitos
/api/admin/plan-contableroutes/admin/plan_contable.tsAdmin administracion.plan_contable
flowchart LR
  subgraph REM["Dominio Remuneraciones"]
    PAY["payroll.ts<br/>/api/remuneraciones/payroll"]
    EMP["employees.ts<br/>/api/employees"]
    CON["contracts.ts<br/>/api/contracts"]
    ATT["attendance.ts<br/>/api/attendance"]
    VAC["vacations.ts<br/>/api/vacations"]
    PER["permissions.ts<br/>/api/permissions"]
    PREV["previsiones.ts<br/>/api/previsiones"]
    HON["honorarios.ts<br/>/api/honorarios"]
    FIN["finiquitos.ts<br/>/api/finiquitos"]
    PDF["generate-pdf.ts<br/>/api/generate-pdf"]
  end
  subgraph OPS["Dominio Operations"]
    ACC["accounting.ts<br/>/api/accounting"]
    PLAN["plan_contable.ts<br/>/api/admin/plan-contable"]
  end
  subgraph COM["Dominio Common"]
    PAR["parameters.ts<br/>/api/parameters"]
    AFC["afc.ts<br/>/api/afc"]
    IMP["impuesto_2cat.ts<br/>/api/impuesto_2cat"]
  end
  subgraph CMD["Dominio Command"]
    TEN["tenants.ts<br/>/api/tenant"]
    TENDB["tenant-db.ts<br/>/api/tenant-db"]
    USR["users.ts<br/>/api/admin"]
    SES["sessions.ts<br/>/api/sessions"]
    AUTH["auth.ts<br/>/api/auth"]
    MON["monitoring.ts<br/>/api/monitoring"]
    MENU["menu.ts<br/>/api/menu"]
  end

Multi-Tenencia y Gestión de Bases de Datos

Section titled “Multi-Tenencia y Gestión de Bases de Datos”

El Orchestrator gestiona tres tipos de pools de conexión PostgreSQL:

flowchart TB
  subgraph GP["Gestor de Pools - db.ts"]
    CP["centralPool"]
    CMP["commonPool"]
    GET["getTenantPool(dbName)"]
    MAP["Map<br/>(tenantPools)"]
    GET --> MAP
  end
  subgraph DB["Bases de Datos"]
    CMD_DB[("nostromo_command<br/>tenants · users · sessions")]
    COM_DB[("nostromo_common<br/>parametros · monedas · impuesto_2cat")]
    T1_DB[("nostromo_6000431<br/>remuneraciones · operaciones_sii")]
    T2_DB[("nostromo_6000432<br/>remuneraciones · operaciones_sii")]
  end
  CP --> CMD_DB
  CMP --> COM_DB
  MAP --> T1_DB
  MAP --> T2_DB

Características de los Pools:

Tipo de PoolSingletonPropósitoConfiguración
centralPoolDatos del sistema (usuarios, tenants)Creado al inicio
commonPoolDatos de referencia compartidos (parámetros)Creado al inicio
TenantPoolsCacheadosDatos de negocio aislados por tenantCreados bajo demanda, cacheados en memoria

La función getTenantPool implementa una estrategia de caché para reutilizar conexiones:

sequenceDiagram
  autonumber
  participant R as Router
  participant A as authenticateToken
  participant D as getDatabaseNameForUser
  participant P as getTenantPool
  participant DB as PostgreSQL
  R ->> A: Validar sesión
  A -->> R: Contexto de usuario (userId | role)
  R ->> D: Resolver DB de tenant
  D ->> D: Verificar permisos RBAC
  D -->> R: Nombre de tenantDb
  R ->> P: Obtener pool para tenant
  P ->> P: Verificar caché
  alt Pool existe
    P -->> R: Retornar pool cacheado
  else Crear nuevo pool
    P ->> DB: new Pool(config)
    P -->> R: Retornar nuevo pool
  end
  R ->> DB: Ejecutar query
  DB -->> R: Resultados

Funciones Clave:

authenticateToken - Valida cookie de sesión y adjunta contexto de usuario a la petición getDatabaseNameForUser - Determina a qué base de datos de tenant puede acceder el usuario getTenantPool - Retorna o crea un pool de conexión cacheado para el tenant


El stack de middleware procesa las peticiones en el siguiente orden:

Pipeline de Middleware (desde createApp()):

flowchart TB
  A["Petición HTTP"]
  B["helmet()<br/>Línea 59"]
  C["cors()<br/>Líneas 60–63"]
  D["morgan('combined')<br/>Línea 64"]
  E["express.json({ limit: '2mb' })<br/>Línea 65"]
  F["cookieParser()<br/>Línea 66"]
  G["Montaje de Rutas<br/>Líneas 68–106"]
  H["authenticateToken<br/>(por ruta)"]
  I["Manejador de Ruta"]
  J["Respuesta HTTP"]

  A --> B
  B --> C
  C --> D
  D --> E
  E --> F
  F --> G
  G --> H
  H --> I
  I --> J

Configuración Global de Middleware:

LíneaMiddlewarePropósitoConfiguración
59helmet()Headers de seguridad (XSS, CSP, HSTS)Configuración por defecto
60-63cors({ origin: [...], credentials: true })CORS para frontendlocalhost:4321, localhost:4322, localhost:4320
64morgan('combined')Logging de peticiones HTTP estilo ApacheSalida a consola
65express.json({ limit: '2mb' })Parsear cuerpos de petición JSONMax payload: 2MB
66cookieParser()Parsear cookies desde header CookieHabilita req.cookies

Autenticación por Ruta:

Las rutas protegidas usan el middleware authenticateToken de src/middleware/auth.ts:

// Patrón usado en todas las rutas protegidas (ej., payroll.ts línea 10)
router.use(authenticateToken);
// O aplicado a rutas individuales
router.get("/", authenticateToken, async (req: AuthenticatedRequest, res) => {
const user = req.user!; // user: { userId, username, role }
// ... lógica de ruta
});

El middleware authenticateToken:

  1. Extrae JWT de la cookie sid
  2. Verifica firma y expiración
  3. Adjunta contexto de usuario a req.user
  4. Retorna 401 Unauthorized si el token es inválido

Estructura de Dominios: Arquitectura en Capas

Section titled “Estructura de Dominios: Arquitectura en Capas”

El Orchestrator implementa una arquitectura Hybrid Core donde la lógica de negocio compleja se ejecuta en TypeScript (Engine Layer) mientras que las operaciones de datos masivos se delegan a PostgreSQL (Smart Views). Este patrón es más visible en el dominio de Remuneraciones:

Dominio de Remuneraciones: Arquitectura de 4 Capas

Section titled “Dominio de Remuneraciones: Arquitectura de 4 Capas”
flowchart LR
subgraph R["Capa 1 · Route"]
  ER["express.Router()"]
  GD["getDetails()<br/>Líneas 575–589"]
  GET["GET /<br/>Línea 15"]
  POSTG["POST /generate<br/>Línea 101"]
  POSTP["POST /preview<br/>Línea 174"]
end
subgraph S["Capa 2 · Service"]
  GP["generatePayroll()<br/>Líneas 32–69"]
  PP["previewPayroll()<br/>Líneas 16–26"]
  MAP["mapContextToInput()<br/>Líneas 71–144"]
end
subgraph E["Capa 3 · Engine"]
  ENG["PayrollEngine.calculate()<br/>Líneas 96–216"]
  BASE["BaseSalaryCalculator"]
  GRAT["GratificationCalculator"]
  SOC["SocialLawsCalculator"]
  TAX["TaxCalculator"]
  PRO["ProrrataCalculator"]
end
subgraph REP["Capa 4 · Repository"]
  SAVE["savePayroll()<br/>Líneas 289–482"]
  FBID["findById()<br/>Líneas 567–573"]
  LIST["list()<br/>Líneas 493–536"]
  CTX["getPayrollContext()<br/>Líneas 49–76"]
end
subgraph DB["Schemas de Base de Datos"]
  LIQ["remuneraciones.liquidaciones<br/>remuneraciones.liquidaciones_detalle"]
  VLIQ["v_liquidaciones_departamento<br/>v_liquidaciones_detalle_completo<br/>v_resumen_liquidaciones_departamento"]
  PAR["parametros.monedas<br/>parametros.indicadores<br/>parametros.impuesto_2cat<br/>parametros.topes"]
end
ER --> GET
ER --> POSTG
ER --> POSTP
GET --> GD
POSTG --> GP
POSTP --> PP
GP --> CTX
PP --> CTX
CTX --> MAP
MAP --> ENG
SAVE --> LIQ
FBID --> LIQ
LIST --> VLIQ
CTX --> PAR
ENG --> BASE
ENG --> GRAT
ENG --> SOC
ENG --> TAX
ENG --> PRO

Responsabilidades por Capa:

CapaArchivoFunciones ClaveAcceso a Base de DatosTestabilidad
Routepayroll.tsManejo de peticiones HTTP, formato de respuestasVía Service/RepositoryE2E (Supertest)
ServicePayrollService.tsOrquestación, transformación de datosVía RepositoryIntegración
EnginePayrollEngine.tsCálculo puro, reglas de negocioNingunoUnit tests
RepositoryPayrollRepository.tsQueries SQL, mapeo de datosDirecto (Pool)Integración

Ejemplo de Flujo de Ejecución (POST /generar):

  1. Capa Route (payroll.ts:101-132): Parsear petición, validar contrato_id y periodo_mes
  2. Capa Service (PayrollService.ts:32-69): Llamar getPayrollContext()mapContextToInput()calculate()savePayroll()
  3. Capa Engine (PayrollEngine.ts:96-216): Ejecutar 8 calculadores en secuencia, retornar PayrollResult
  4. Capa Repository (PayrollRepository.ts:49-76, 289-482): Obtener datos de contexto, persistir liquidación + detalles

El sistema divide estratégicamente la responsabilidad entre TypeScript y PostgreSQL:

TypeScript (Engine Layer) - “Escrituras y Lógica”:

  • Cálculos complejos (remuneraciones, impuestos, prorrateo)
  • Validación de reglas de negocio (rangos de fechas, umbrales)
  • Workflows multi-paso (generar → validar → persistir)
  • Completamente testeable en aislamiento (no requiere base de datos)

PostgreSQL (Smart Views) - “Lecturas y Reportes”:

  • Recuperación masiva de datos con filtrado
  • Agregaciones y resúmenes
  • Joins complejos entre tablas normalizadas
  • Vistas pre-computadas para performance

Hybrid Core en Acción - Ejemplo de Remuneraciones:

Cálculo en TypeScript (Engine):

// PayrollEngine.calculate() - Líneas 96-216
// SIN acceso a base de datos, función pura
static calculate(input: PayrollInput): PayrollResult {
// 1. Calcular sueldo base prorrateado
const salary = BaseSalaryCalculator.calculate(
input.sueldo_base,
input.sueldo_minimo,
input.dias_trabajados
);
// 2. Calcular horas extras (50% y 100%)
const factorHora = (1 / 30 * 28) / input.work_hours;
const valorHora = Math.round(input.sueldo_base * factorHora);
const amountHex50 = Math.round(input.horas_extra_50 * valorHora * 1.5);
// 3. Calcular gratificación (25% o 4.75 SMM)
const gratification = GratificationCalculator.calculate(...);
// 4. Calcular leyes sociales (AFP, Salud, AFC)
const socialLaws = SocialLawsCalculator.calculate(...);
// 5. Calcular impuesto progresivo
const tax = TaxCalculator.calculate(...);
return { total_liquido, total_haberes, total_descuentos, ... };
}

Vista Inteligente de PostgreSQL (Repository):

// PayrollRepository.list() - Líneas 493-536
// Delega filtrado/agregación a la base de datos
static async list(db: Pool, filters: {...}): Promise<any[]> {
const { rows } = await db.query(`
SELECT * FROM remuneraciones.v_liquidaciones_departamento
WHERE año = $1 AND mes = $2
AND unaccent(lower(empleado_nombre)) LIKE unaccent(lower($3))
ORDER BY año DESC, mes DESC, empleado_nombre ASC
LIMIT $4 OFFSET $5
`, [año, mes, `%${empleado_nombre}%`, limit, offset]);
return rows; // La vista ya hace joins de empleados, contratos, departamentos
}

Principio Clave: Las escrituras (cálculos) ocurren en TypeScript para testabilidad. Las lecturas (consultas) ocurren en PostgreSQL para performance.


Todas las rutas siguen una estructura consistente demostrada en payroll.ts:

// 1. Importar dependencias (Líneas 1-7)
import express from "express";
import { getTenantPool } from "@/lib/db";
import { authenticateToken, AuthenticatedRequest } from "@/middleware/auth";
import { getDatabaseNameForUser } from "@/lib/tenantResolver";
import { PayrollService } from "@/domain/payroll/PayrollService";
import { PayrollRepository } from "@/domain/payroll/PayrollRepository";
// 2. Crear router (Línea 9)
const router = express.Router();
// 3. Aplicar middleware de autenticación globalmente (Línea 10)
router.use(authenticateToken);
// 4. Definir endpoints (Líneas 14-43)
router.get("/", async (req: AuthenticatedRequest, res) => {
try {
// 4a. Resolver base de datos de tenant
const db = await getDatabaseNameForUser(req.user, req);
const pool = getTenantPool(db);
// 4b. Verificar existencia de vista (opcional, por seguridad)
if (
!(await PayrollRepository.checkViewExists(
pool,
"remuneraciones.v_liquidaciones_departamento"
))
) {
return res.status(501).json({ error: "Vista no disponible" });
}
// 4c. Parsear parámetros de query
const q = req.query;
const rows = await PayrollRepository.list(pool, {
año: Number(q.año ?? q.anio ?? q.year) || undefined,
mes: Number(q.mes ?? q.month) || undefined,
empleado_id: q.empleado_id as string,
estado: q.estado as string,
limit: parseInt((q.limit as string) || "2000"),
offset: parseInt((q.offset as string) || "0"),
});
// 4d. Retornar respuesta
res.json(rows);
} catch (e: any) {
res.status(500).json({ error: e.message });
}
});
// 5. Exportar router (Línea 197)
export default router;
PatrónImplementaciónUbicación de Ejemplo
Autenticaciónrouter.use(authenticateToken)Línea 10
Resolución de TenantgetDatabaseNameForUser(req.user, req)Línea 17
Obtención de PoolgetTenantPool(db)Línea 18
Parseo de Params de QueryNumber(q.año ?? q.anio ?? q.year)Líneas 26-33
Manejo de Errorestry/catch con res.status(500).json({ error })Líneas 15-42
ValidaciónVerificar existencia antes de operacionesLíneas 20-23
{
"success": true,
"data": {
/* payload */
},
"meta": {
/* paginación, etc */
}
}
{
"success": false,
"error": "ERROR_CODE",
"message": "Descripción legible para humanos"
}
CódigoUsoEjemplo
200 OKGET/PUT exitosoListar liquidaciones
201 CreatedPOST exitosoCrear un tenant
204 No ContentDELETE exitosoEliminar registro de nómina
400 Bad RequestPayload inválidoFalta campos requeridos
401 UnauthorizedToken faltante/inválidoSin cookie sid
403 ForbiddenToken válido pero sin permisosRol de usuario insuficiente
404 Not FoundRecurso no existeID de empleado no encontrado
500 Internal Server ErrorError del servidorFallo de conexión a base de datos

El Orchestrator mantiene consistencia entre dominios y respuestas de API:

  • TypeScript usa camelCase para variables, funciones, clases
  • Ejemplo:totalAssets, baseSalary, PayrollEngine

####Base de Datos:

  • PostgreSQL usa snake_case para todos los identificadores
  • Ejemplo: total_haberes, sueldo_base, liquidaciones_detalle`
  • Siempre retornan snake_case coincidiendo con la estructura de base de datos
  • Elimina capa de transformación, simplifica integración con frontend
// Cálculo de dominio (camelCase)
const result = { totalAssets: 1000, baseSalary: 800 };
// Respuesta de API (snake_case)
res.json({ total_haberes: result.totalAssets, sueldo_base: result.baseSalary });

ComandoPropósitoImplementación
npm run devServidor de desarrollo con hot reloadnodemon -r tsconfig-paths/register src/server.ts
npm run buildCompilar TypeScript a JavaScripttsc (salida: dist/)
npm startServidor de producciónnode dist/server.js
npm run testEjecutar solo unit testsjest --testMatch="**/*.unit.test.ts"
npm run test:allEjecutar todos los testsjest (incluye unit + integration)
npm run test:unitUnit tests explícitosjest --testMatch="**/*.unit.test.ts"
npm run test:integrationSolo integration testsjest --testMatch="**/*.integration.test.ts"
npm run test:domainTestear capa de dominiojest --testPathPattern=src/domain
npm run test:payrollTestear dominio de remuneracionesjest --testPathPattern=src/domain/payroll
npm run test:honorariosTestear dominio de honorariosjest --testPathPattern=src/domain/honorarios
npm run test:previsionsTestear dominio de previsionesjest --testPathPattern=src/domain/previsions
npm run test:finiquitosTestear dominio de finiquitosjest --testPathPattern=src/domain/finiquitos
npm run test:watchModo watchjest --watch (auto-ejecutar al cambiar)

Organización de Pruebas:

  • Unit tests: *.unit.test.ts - Funciones puras, sin base de datos
  • Integration tests: *.integration.test.ts - Capa de repositorio con base de datos
  • E2E tests: *.e2e.test.ts - Ciclo completo de petición/respuesta HTTP

El Orchestrator usa Jest con diferentes patrones de test:

  • Testean funciones puras en la capa Engine
  • No requieren base de datos
  • Ejecución rápida (<100ms por test)
  • Ejemplo: PayrollEngine.test.ts

Integration Tests (*.integration.test.ts):

Section titled “Integration Tests (*.integration.test.ts):”
  • Testean capa de Repository con base de datos real
  • Usan base de datos de test o transacciones
  • Verifican queries SQL y mapeo de datos
  • Testean ciclo completo de petición/respuesta HTTP
  • Usan Supertest para hacer peticiones
  • Ejemplo: users.e2e.test.ts
  • Endpoints críticos: 100% (auth, generación de nómina)
  • Capa de Repository: 70%+ (queries SQL)
  • Capa de Engine: 90%+ (lógica de negocio)
  • Directoryorchestrator/
    • Directorysrc/
      • app.ts # Factoría de aplicación Express (createApp)
      • server.ts # Arranque del servidor HTTP
      • Directorylib/
        • db.ts # Gestión de pools (centralPool, commonPool, getTenantPool)
        • rbac.ts # Control de acceso basado en roles
        • tenantResolver.ts # getDatabaseNameForUser()
        • accessControl.ts # Verificaciones de permisos
      • Directorymiddleware/
        • auth.ts # authenticateToken, AuthenticatedRequest
        • authorization.ts # authorizeRoute
      • Directorydomain/ Capa de Dominio (DDD)
        • Directoryafp/ — AFP y Previsión
          • AfpRepository.ts
          • AfpService.ts
          • Directory__tests__/
        • Directoryattendance/ — Control de Asistencia
          • AttendanceRepository.ts
          • AttendanceService.ts
          • Directory__tests__/
        • Directorycargos/ — Gestión de Cargos
          • CargoRepository.ts
          • CargoService.ts
          • Directory__tests__/
        • Directorycommand/ Gestión del Sistema
          • MonitoringRepository.ts
          • MonitoringService.ts
          • SessionRepository.ts
          • SessionService.ts
          • TenantRepository.ts
          • TenantService.ts
          • types.ts
          • Directory__tests__/
        • Directorycontracts/ — Contratos Laborales
          • ContractGenerator.ts
          • ContractRepository.ts
          • ContractService.ts
          • Directory__tests__/
        • Directoryemployees/ — Maestro de Empleados
          • EmployeeRepository.ts
          • EmployeeService.ts
          • Directory__tests__/
        • Directoryfiniquitos/ # Pagos de finiquito
          • FiniquitoCalculator.ts
          • FiniquitoRepository.ts
          • FiniquitoService.ts
          • Directory__tests__/
        • Directoryhonorarios/ # Dominio de honorarios
          • HonorariosRepository.ts
          • HonorariosService.ts
          • types.ts
          • Directory__tests__/
        • Directoryisapre/ — Instituciones de Salud
          • IsapreRepository.ts
          • IsapreService.ts
          • Directory__tests__/
        • Directorypayroll/
          • PayrollService.ts # generatePayroll(), previewPayroll()
          • PayrollEngine.ts # calculate() - función pura
          • PayrollRepository.ts # getPayrollContext(), savePayroll(), list()
          • types.ts # ContractType, HealthMode, GratificationMode
          • Directorycalculators/
            • BaseSalaryCalculator.ts
            • GratificationCalculator.ts
            • SocialLawsCalculator.ts
            • TaxCalculator.ts
            • ProrrataCalculator.ts
        • Directorypermissions/ — Permisos y Licencias
          • PermissionRepository.ts
          • PermissionService.ts
          • Directory__tests__/
        • Directoryprevisions/ # Agregaciones de seguridad social
          • ApvRepository.ts
          • ApvService.ts
          • PrevisionsRepository.ts
          • PrevisionsService.ts
          • types.ts
          • Directory__tests__/
        • Directoryvacations/ — Vacaciones
          • VacationRepository.ts
          • VacationService.ts
          • Directory__tests__/
        • Directoryworking_day/ — Jornadas Laborales
          • WorkingDayRepository.ts
          • WorkingDayService.ts
          • Directory__tests__/
      • Directoryroutes/
        • Directorycommand/ # 7 rutas (tenant, users, auth, sessions, etc.)
          • tenant.ts # /api/tenant
          • tenant-db.ts # /api/tenant-db
          • users.ts # /api/admin
          • sessions.ts # /api/sessions
          • monitoring.ts # /api/monitoring
          • auth.ts # /api/auth
          • menu.ts # /api/menu
        • Directorycommon/ # 3 rutas (parameters, afc, impuesto)
          • parameters.ts # /api/parameters
          • afc.ts # /api/afc
          • impuesto_2cat.ts # /api/impuesto_2cat
        • accounting.ts # /api/accounting
        • Directoryremuneraciones/ # 18 rutas (payroll, employees, contracts, etc.)
          • payroll.ts # /api/remuneraciones/payroll
          • employees.ts # /api/employees
          • contracts.ts # /api/contracts
          • attendance.ts # /api/attendance
          • vacations.ts # /api/vacations
          • permissions.ts # /api/permissions
          • previsiones.ts # /api/previsiones
          • honorarios.ts # /api/honorarios
          • finiquitos.ts # /api/finiquitos
          • generate-pdf.ts # /api/generate-pdf
          • afp.ts # /api/afp
          • isapre.ts # /api/isapre
          • apv_contracts.ts # /api/apv_contracts
          • isapre_contracts.ts # /api/isapre_contracts
          • cargos.ts # /api/cargos
          • departments.ts # /api/departments
          • working_day.ts # /api/working_day
        • Directoryadmin/
          • plan_contable.ts # /api/admin/plan-contable
      • Directorytests/
        • *.unit.test.ts # Tests de funciones puras
        • *.integration.test.ts # Tests de integración con base de datos
        • *.e2e.test.ts # Tests de endpoints HTTP
      • Directorystorage/
        • Directoryliquidaciones/ # PDFs de nómina generados
    • package.json # Dependencias + scripts
    • tsconfig.json # Configuración TypeScript
    • jest.orchestrator.config.js # Configuración de tests Jest

Alias de Rutas de TypeScript (tsconfig.json):

Section titled “Alias de Rutas de TypeScript (tsconfig.json):”
  • @/lib/*src/lib/*
  • @/middleware/*src/middleware/*
  • @/domain/*src/domain/*
  • @/routes/*src/routes/*

Comandos para desarrollo, build y testing.

ComandoDescripciónEjecución
devServidor de desarrollo con hot-reloadnpm run dev
buildCompila TypeScript a JavaScriptnpm run build
startInicia el servidor compiladonpm run start
test:orchestratorRunner principal de testsnpm run test:orchestrator
test:payrollTests del dominio de remuneracionesnpm run test:payroll
test:domainTests de todos los dominiosnpm run test:domain

El Orchestrator implementa un backend robusto multi-tenant con clara separación entre manejo HTTP (Routes), orquestación de negocio (Services), cálculo puro (Engines) y acceso a datos (Repositories). La arquitectura Hybrid Core balancea testabilidad con performance ejecutando lógica compleja en TypeScript mientras delega operaciones de datos masivos a Vistas Inteligentes de PostgreSQL. Todos los endpoints refuerzan autenticación, autorización y aislamiento de tenant, asegurando la seguridad de datos a través del sistema multi-tenant.