Skip to content

Plataforma técnica · Arquitectura

Diagramas del Sistema

Arquitectura Diagramas

Esta página contiene los diagramas técnicos del ecosistema Nostromo: flujos de datos, capas, autenticación, multi-tenancy y despliegue. Las explicaciones narrativas viven en Arquitectura del Sistema y Arquitectura Hexagonal.


Flujo completo de datos desde servicios externos hasta la UI:

flowchart LR
    %% External Services
    subgraph External["Servicios Externos"]
        SII["SII"]
        PREVIRED["Previred"]
        BC["Banco Central"]
    end

    %% Loaders Layer
    subgraph Loaders["Nostromo · Python ETL"]
        BCL["bc_loader.py"]
        I2C["impuesto_2cat_loader.py"]
        PRL["previred_loader.py"]
        SIIL["sii_loader.py"]
        RCS["run_cargas_sii.py"]
    end

    %% Database Layer
    subgraph DB["Mother · PostgreSQL 16"]
        direction TB

        subgraph Parametros["nostromo_common · parametros"]
            direction TB
            PAR_MON["monedas"]
            PAR_IND["indicadores"]
            PAR_TAX["impuesto_2cat"]
            PAR_AFP["afp · afc"]
        end

        subgraph Operaciones["tenant · operaciones_sii"]
            OP_COM["compras"]
            OP_VEN["ventas"]
            OP_DET["compras_ventas_detalle"]
        end

        subgraph Remuneraciones["tenant · remuneraciones"]
            REM_EMP["empleados"]
            REM_CON["contratos"]
            REM_LIQ["liquidaciones"]
            REM_HON["honorarios"]
        end

        SP_DET(["sp_generar_compras_ventas_detalle"])
    end

    %% Orchestrator + Sevastopol
    subgraph Backend["Orchestrator · Node + TS"]
        PAYENG["PayrollEngine.calculate()"]
        SVCS["Services / Repositories"]
    end

    subgraph Frontend["Sevastopol · Astro + SolidJS"]
        VIEWS["Islands UI"]
        API["authenticatedFetch"]
    end

    %% Connections
    BC -->|API| BCL
    BCL -->|Upsert| PAR_MON

    PREVIRED -->|JSON| PRL
    PRL -->|Upsert| PAR_IND
    PRL -->|Upsert| PAR_AFP

    SII -->|Scraping| I2C
    I2C -->|Upsert| PAR_TAX

    SII -->|Playwright| SIIL
    SIIL --> RCS
    RCS -->|Insert| OP_COM
    RCS -->|Insert| OP_VEN

    OP_COM -->|Trigger| SP_DET
    OP_VEN -->|Trigger| SP_DET
    SP_DET -->|Gen| OP_DET

    VIEWS -->|HTTPS + cookie sid| API
    API -->|REST JSON| SVCS
    SVCS -->|Pool| REM_EMP
    SVCS -->|Pool| REM_CON
    SVCS -->|Pool| PAR_IND
    SVCS --> PAYENG
    PAYENG -->|PayrollResult| SVCS
    SVCS -->|Insert| REM_LIQ

    classDef ext fill:#2d2d2d,stroke:#d3095f,stroke-width:2px,color:#fff;
    classDef py fill:#0d1117,stroke:#e3b341,stroke-width:2px,color:#fff;
    classDef db fill:#0d1117,stroke:#2b95d6,stroke-width:2px,color:#fff;
    classDef sp fill:#1f1235,stroke:#8e44ad,stroke-width:2px,stroke-dasharray: 5 5,color:#fff;
    classDef back fill:#0d1117,stroke:#27ae60,stroke-width:2px,color:#fff;
    classDef front fill:#0d1117,stroke:#16a085,stroke-width:2px,color:#fff;

    class SII,PREVIRED,BC ext;
    class BCL,I2C,PRL,SIIL,RCS py;
    class PAR_MON,PAR_IND,PAR_TAX,PAR_AFP,OP_COM,OP_VEN,OP_DET,REM_EMP,REM_CON,REM_LIQ,REM_HON db;
    class SP_DET sp;
    class PAYENG,SVCS back;
    class VIEWS,API front;
ColorComponenteDescripción
MagentaExternoServicios fuera de la red (SII, Previred, Banco Central).
AmarilloPython ETLLoaders de accounting_system/.
CyanPostgreSQLTablas en nostromo_common y bases por tenant.
VioletaStored ProcLógica residual en PostgreSQL (compras/ventas detalle).
VerdeOrchestratorServicios y motor de cálculo en TypeScript.
TealSevastopolIslands SolidJS y proxy autenticado.

Carga de parámetros desde servicios externos hacia el schema parametros de nostromo_common:

flowchart LR
    BC["Banco Central"] -->|API| BCL["bc_loader.py"]
    BCL -->|Upsert| PM["parametros.monedas"]

    PR["Previred"] -->|JSON| PRL["previred_loader.py"]
    PRL -->|Upsert| PP["parametros.indicadores · afp · afc"]

    SII["SII"] -->|Scraping| I2C["impuesto_2cat_loader.py"]
    I2C -->|Upsert| PI["parametros.impuesto_2cat"]

    classDef ext fill:#2d2d2d,stroke:#d3095f,stroke-width:2px,color:#fff;
    classDef py fill:#0d1117,stroke:#e3b341,stroke-width:2px,color:#fff;
    classDef db fill:#0d1117,stroke:#2b95d6,stroke-width:2px,color:#fff;

    class BC,PR,SII ext
    class BCL,PRL,I2C py
    class PM,PP,PI db

Capas que recorre una petición POST /api/remuneraciones/payroll/generate:

  1. Route (routes/remuneraciones/payroll.ts) — Parsea el body, valida contrato_id y periodo_mes, resuelve la base del tenant.
  2. Service (PayrollService.generatePayroll) — Orquesta getPayrollContextmapContextToInputcalculatesavePayroll.
  3. Repository.getPayrollContext — Consulta empleados, contratos, asistencia, indicadores, topes y tramos de impuesto. Llamadas paralelas con Promise.all.
  4. Engine.calculate — Función pura: sueldo base prorrateado, gratificación, leyes sociales (AFP, salud, AFC), impuesto único, totales.
  5. Repository.savePayroll — Inserta cabecera en liquidaciones y líneas en liquidaciones_detalle dentro de una transacción.
  6. Response — JSON snake_case con id_liquidacion, total_liquido, total_haberes, total_descuentos.
flowchart TD
    A[POST /api/remuneraciones/payroll/generate] --> R[Route handler]
    R --> S[PayrollService.generatePayroll]
    S --> CTX[PayrollRepository.getPayrollContext]
    CTX --> MAP[mapContextToInput]
    MAP --> ENG[PayrollEngine.calculate]
    ENG --> SAVE[PayrollRepository.savePayroll]
    SAVE --> RES[Response JSON]

    style ENG stroke:#27ae60,stroke-width:4px
    style CTX stroke:#2b95d6,stroke-width:2px
    style SAVE stroke:#2b95d6,stroke-width:2px

graph TD
    subgraph Client["Cliente"]
        Browser["Navegador Web"]
    end

    subgraph CDN["Cloudflare Pages"]
        Static["Jean d'Arc (Docs)"]
        App["Sevastopol (App)"]
    end

    subgraph Backend["Servidor Node.js"]
        Orchest["Orchestrator API"]
    end

    subgraph Data["Base de Datos"]
        PG["PostgreSQL · Mother"]
    end

    Browser -->|HTTPS / 443| CDN
    Browser -->|HTTPS / API| Orchest
    Orchest -->|TCP / 5432| PG

    classDef cdn fill:#f39c12,stroke:#d35400,color:#000;
    classDef node fill:#27ae60,stroke:#2ecc71,color:#fff;
    classDef db fill:#16a085,stroke:#1abc9c,color:#fff;

    class Static,App cdn;
    class Orchest node;
    class PG db;

sequenceDiagram
    participant U as Usuario
    participant F as Sevastopol
    participant A as Orchestrator
    participant D as PostgreSQL

    U->>F: Ingresa credenciales
    F->>A: POST /api/auth/login
    A->>D: SELECT * FROM nostromo_command.users WHERE email = $1
    D-->>A: User + hash
    A->>A: bcrypt.compare(password, hash)

    alt Credenciales válidas
        A->>D: INSERT INTO nostromo_command.sessions
        A->>A: jwt.sign(payload, JWT_SECRET)
        A-->>F: Set-Cookie sid=JWT (HttpOnly)
        F->>U: Redirige al dashboard
    else Credenciales inválidas
        A-->>F: 401 Unauthorized
        F->>U: Muestra error
    end

Cada empresa tiene su propia base de datos PostgreSQL. El Orchestrator mantiene un pool cacheado por tenant.

flowchart LR
    U["Usuario autenticado"] --> M["authenticateToken<br/>+ tenantResolver"]
    M -->|tenant A| PA["Pool nostromo_60004317"]
    M -->|tenant B| PB["Pool nostromo_70001234"]

    subgraph DBA["nostromo_60004317"]
        SA1[("remuneraciones")]
        SA2[("operaciones_sii")]
        SA3[("administracion")]
    end

    subgraph DBB["nostromo_70001234"]
        SB1[("remuneraciones")]
        SB2[("operaciones_sii")]
        SB3[("administracion")]
    end

    PA --> SA1 & SA2 & SA3
    PB --> SB1 & SB2 & SB3

    linkStyle 0 stroke:#27ae60,stroke-width:2px;
    linkStyle 1 stroke:#f39c12,stroke-width:2px;
    linkStyle 2 stroke:#f39c12,stroke-width:2px;