Arquitectura del Sistema
Esta página proporciona una visión integral de toda la arquitectura del sistema, incluyendo el diseño de servicios, patrones de comunicación, stack tecnológico y decisiones arquitectónicas clave.
Servicios del Sistema
Section titled “Servicios del Sistema”| Servicio | Tecnología | Puerto | Propósito |
|---|---|---|---|
| Sevastopol | Astro 5.x + SolidJS | 4321 | Capa de presentación, enrutamiento de vistas, componentes UI |
| Orchestrator | Node.js 20+ / Express 5.x | 8000 | Lógica de negocio, acceso a datos, servicios API |
Límites de Servicios
Section titled “Límites de Servicios”flowchart TD
A[Cliente Navegador<br/>Agente de Usuario]
A -->|HTTP GET /command| B
subgraph FE["Servicio Frontend :4321"]
B[Páginas Astro<br/>src/pages/*.astro]
B --> C[Renderiza SSR<br/>view router / Carga Dinámica de Vistas]
C --> D[Carga<br/>Islands SolidJS<br/>src/components/islands/*]
D --> E[Llamadas API]
E --> F[Proxy API<br/>src/pages/api/*]
end
F -->|Proxy con cookie sid| G
subgraph BE["Servicio Orchestrator :8000"]
G[App Express<br/>src/app.ts]
G --> H[authenticateToken<br/>src/middleware/auth.ts]
H --> I[authorizeRoute<br/>src/lib/rbac.ts]
I --> J[Manejadores de Rutas<br/>src/routes/*]
J --> K[Servicios de Dominio<br/>src/domain/*]
end
K -->|centralPool| DB1[(nostromo_command<br/>Central)]
K -->|commonPool| DB2[(nostromo_common<br/>Parámetros)]
K -->|getTenantPool| DB3[(nostromo_*<br/>DBs Tenant)]
Flujo de Peticiones Autenticadas
Section titled “Flujo de Peticiones Autenticadas”El siguiente diagrama muestra cómo fluye una petición API autenticada típica a través del sistema:
sequenceDiagram
participant N as 🌐 Navegador
participant V as Sevastopol
participant P as Proxy API
participant A as Orchestrator
participant M as Auth Middleware
participant R as RBAC
participant D as PostgreSQL
N->>V: Click en navegación
V->>V: Dispara sidebar:navigate
V->>P: authenticatedFetch('/api/employees')
Note right of V: Cookie sid adjunta
P->>A: HTTP con cookie sid
A->>M: Verifica JWT + sesión
M->>D: Consulta sessions
D-->>M: Sesión válida ✅
M->>R: Verifica permisos
R->>R: rbac.canAccess(role, route)
alt ✅ Autorizado
R->>A: Ejecuta handler
A->>D: Query SQL
D-->>A: Datos
A-->>P: JSON (snake_case)
P-->>V: Respuesta
V->>N: Re-renderiza UI
else ❌ No autorizado
R-->>A: 403 Forbidden
A-->>P: Error
P-->>V: Error
V->>N: Mensaje de error
end
Arquitectura Frontend (Sevastopol)
Section titled “Arquitectura Frontend (Sevastopol)”Sevastopol usa Astro con Islands Architecture de SolidJS para hidratación selectiva e interactividad.
| Componente | Tecnología | Versión |
|---|---|---|
| Framework | Astro (SSR) | 5.x |
| UI Library | SolidJS (Islands) | 1.x |
| Estilos | TailwindCSS | 3.x |
| Build Tool | Vite | 6.x |
Directorysrc/
Directorypages/
- command.astro (Contenedor principal)
Directoryapi/ (Proxies al backend)
- …
Directorycomponents/
Directoryatoms/ (Input, Button, Modal)
- …
Directorymolecules/ (StatCard, StatusBadge)
- …
Directoryorganisms/ (Sidebar, PageHeader)
- …
Directoryislands/ (Componentes interactivos)
- …
Directorytemplates/ (IslandBase.tsx)
- …
Directorylib/
- authFetch.ts
- view-router.ts
Directoryconfig/
- menu.config.ts
Componentes Principales
Section titled “Componentes Principales”flowchart TD
A[command.astro<br/>Contenedor Principal]
A -->|Inicializa| B
subgraph GV["Gestión de Vistas"]
B[view-router.ts<br/>Cargador dinámico]
M[menu.config.ts<br/>Estructura de navegación]
B -->|Lee| M
end
B -->|import dinámico| I1
B -->|import dinámico| I2
B -->|import dinámico| I3
subgraph IS["Islands SolidJS"]
I1[Remuneraciones<br/>PayrollViewIsland<br/>EmployeesViewIsland]
I2[Administración<br/>TenantsViewIsland<br/>CompanyViewIsland]
I3[Contabilidad<br/>OperationsViewIsland]
end
I1 & I2 & I3 -->|Envuelve en| C
subgraph BC["Biblioteca de Componentes"]
C[IslandBase.tsx]
C --> O[Organismos]
O --> MO[Moléculas]
MO --> AT[Átomos]
end
Mecanismo del View Router
Section titled “Mecanismo del View Router”- Usuario hace clic en elemento de navegación en
Sidebar.astro - Sidebar dispara
CustomEvent("sidebar:navigate", detail: viewKey) - View router escucha el evento en
view-router.ts - Router realiza
import()dinámico del módulo island correspondiente - Función
render()de SolidJS monta el island en#command-view - Router emite
CustomEvent("view:state")para actualizar estado activo
Arquitectura Backend (Orchestrator)
Section titled “Arquitectura Backend (Orchestrator)”Orchestrator implementa Diseño Orientado al Dominio (DDD) con contextos delimitados claros y una arquitectura por capas.
| Componente | Tecnología |
|---|---|
| Runtime | Node.js 20+ |
| Framework | Express 5.x |
| Lenguaje | TypeScript 5.x |
| Base de Datos | PostgreSQL (node-postgres) |
| Documentos | Puppeteer, docxtemplater |
Directorysrc/
- app.ts (Express factory)
- server.ts (HTTP bootstrap)
Directorylib/
- db.ts (Pool management)
- rbac.ts (Control de acceso)
Directorymiddleware/
- auth.ts (JWT validation)
Directoryroutes/
Directorycommand/ (tenant, users, auth)
- …
Directoryremuneraciones/ (payroll, employees)
- …
Directorycommon/ (parameters, afc)
- …
Directoryadmin/ (chart-of-accounts)
- …
Directorydomain/
Directorypayroll/ (Service, Repository, Calculators)
- …
Directorycommon/ (CommonDataService, PdfService)
- …
Estructura de Capas
Section titled “Estructura de Capas”flowchart TD
subgraph PE["Punto de Entrada"]
S["server.ts"] --> A["app.ts"]
end
subgraph R["Capa de Rutas"]
R1[command/]
R2[remuneraciones/]
R3[common/]
R4[admin/]
end
subgraph MW["Middleware"]
M1["auth.ts<br/>authenticateToken()"]
M2["rbac.ts<br/>canAccess()"]
end
subgraph D["Capa de Dominio"]
D1[PayrollService<br/>PayrollRepository]
D2[CommonDataService<br/>PdfService]
D3[ChartOfAccountsService]
end
subgraph DB["Base de Datos"]
DB1["db.ts<br/>centralPool / commonPool / getTenantPool()"]
end
A --> R1 & R2 & R3 & R4
A --> M1 & M2
R1 & R2 --> D1
R3 --> D2
R4 --> D3
D1 & D2 & D3 --> DB1
Inicialización de Express
Section titled “Inicialización de Express”- Middleware de Seguridad:
helmet()para encabezados HTTP seguros - CORS: Configurado para puertos 4320-4322 con credenciales
- Logging:
morgan('combined')para registro de peticiones - Parsing de Body:
express.json()con límite de 2MB - Parsing de Cookies:
cookieParser()para cookies de sesión
Registro de Rutas
Section titled “Registro de Rutas”| Prefijo | Módulo | Propósito |
|---|---|---|
/api/tenant | command/tenant.ts | Gestión de tenants (SUPER_ADMIN) |
/api/admin | command/users.ts | Gestión de usuarios |
/api/sessions | command/sessions.ts | Monitoreo de sesiones |
/api/parameters | common/parameters.ts | Indicadores económicos |
/api/remuneraciones/payroll | remuneraciones/payroll.ts | Liquidaciones |
/api/employees | remuneraciones/employees.ts | CRUD empleados |
/api/admin/chart-of-accounts | admin/chart-of-accounts.ts | Plan de cuentas |
Organización de Dominios
Section titled “Organización de Dominios”Dominio de Remuneraciones
Section titled “Dominio de Remuneraciones”Clases Clave:
PayrollService: Orquesta el flujo de generación de liquidacionesPayrollRepository: Recopilación de contexto y persistenciaSocialLawsCalculator: Cálculos de AFP, salud, seguro de cesantíaHealthPlanCalculator: Lógica FONASA vs ISAPRETaxCalculator: Impuesto progresivo (Impuesto 2da Categoría)
Patrón de Recopilación de Contexto:
// Consultas paralelas para optimizar rendimientoconst [attendanceRes, indicatorsRes, afpCode, taxRes] = await Promise.all([ this.getAttendanceData(db, contractRes.employeeId, periodStr), CommonDataService.getIndicators(periodStr), this.getAfpCode(db, contractId), CommonDataService.getTaxBrackets(periodStr)]);Dominio Común
Section titled “Dominio Común”Proporciona parámetros regulatorios compartidos:
CommonDataService: Consultas de parámetros temporalesPdfService: Generación de documentos usando Puppeteer
Patrones de Diseño API
Section titled “Patrones de Diseño API”Convenciones RESTful
Section titled “Convenciones RESTful”- Query params:
snake_case(ej:?empleado_id=123) - Body: JSON en
snake_case - Auth: Cookie
sidcon token JWT
{ "field_name": "value", "nested_object": { "snake_case_key": 123 }}{ "success": false, "error": "ERROR_CODE", "message": "Descripción legible"}Patrones de Rutas
Section titled “Patrones de Rutas”| Método | Patrón | Propósito |
|---|---|---|
GET | /api/resource | Listar todos (con filtros opcionales) |
GET | /api/resource/:id | Obtener por ID |
POST | /api/resource | Crear nuevo |
PUT/PATCH | /api/resource/:id | Actualizar existente |
DELETE | /api/resource/:id | Eliminar (con ?force=true opcional) |
router.get("/", async (req, res) => { /* Listar */ });router.get("/vista/:id", async (req, res) => { /* Obtener por ID */ });router.get("/:id/details", async (req, res) => { /* Obtener detalles */ });router.post("/generar", async (req, res) => { /* Crear/Generar */ });router.patch("/:id", async (req, res) => { /* Actualizar */ });router.delete("/:id", async (req, res) => { /* Eliminar */ });Capa de Acceso a Datos
Section titled “Capa de Acceso a Datos”Gestión de Connection Pools
Section titled “Gestión de Connection Pools”El sistema usa tres tipos de connection pools:
flowchart LR
subgraph PS["Pools Singleton"]
CP[centralPool]
CMP[commonPool]
end
subgraph PD["Pools Dinámicos"]
F["getTenantPool(dbName)"]
M[Map en caché]
F --> M
end
subgraph DB["Bases de Datos"]
DB1[(nostromo_command)]
DB2[(nostromo_common)]
DB3[(nostromo_6000431)]
DB4[(nostromo_7000123)]
end
CP --> DB1
CMP --> DB2
M --> DB3
M --> DB4
Resolución de Tenant
Section titled “Resolución de Tenant”- Extraer
tenant_idde los parámetros de consulta - Mapear a nombre de base de datos:
nostromo_${tenant_id} - Recuperar o crear connection pool
- Ejecutar consulta en base de datos específica del tenant
const tenantDb = await getDatabaseNameForUser(req.user, req);const pool = getTenantPool(tenantDb);const result = await pool.query(sql, params);Patrones de Comunicación
Section titled “Patrones de Comunicación”Frontend-a-Backend
Section titled “Frontend-a-Backend”Sevastopol usa un patrón de proxy para comunicarse con Orchestrator:
- Proxy de Desarrollo: Vite hace proxy de
/api/*ahttp://localhost:8000 - Páginas Proxy API:
sevastopol/src/pages/api/*implementan proxies simples - Fetch Autenticado: La utilidad
authenticatedFetch()adjunta la cookiesid
// sevastopol/src/pages/api/admin/chart-of-accounts.tsimport { createProxy } from '@/lib/proxyUtils';export const { GET, POST, PUT, DELETE } = createProxy('/api/admin/chart-of-accounts');Flujo de Autenticación
Section titled “Flujo de Autenticación”- Usuario inicia sesión vía
/api/auth/login - Orchestrator valida credenciales contra
nostromo_command.users - Crea sesión en tabla
sessions - Retorna cookie
sidcon token JWT (HttpOnly) - Todas las peticiones subsecuentes incluyen la cookie
- Middleware
authenticateTokenvalida token y sesión
Patrones Arquitectónicos Clave
Section titled “Patrones Arquitectónicos Clave”Separación de Responsabilidades
Section titled “Separación de Responsabilidades”| Capa | Responsabilidad | Ejemplo |
|---|---|---|
| Presentación | Renderizado UI, interacción | view-router.ts, Islands SolidJS |
| API Gateway | Enrutamiento, auth, RBAC | app.ts, rbac.ts |
| Servicio | Orquestación de lógica | PayrollService.generatePayroll() |
| Repositorio | Acceso a datos | PayrollRepository.getPayrollContext() |
| Calculador | Lógica de negocio pura | SocialLawsCalculator.calculate() |
Diseño Orientado al Dominio
Section titled “Diseño Orientado al Dominio”Cada dominio es autocontenido con:
Directorysrc/domain/chart-of-accounts/
- types.ts (Account, CreateAccountDTO)
- ChartOfAccountsService.ts (Lógica de negocio)
Directorysrc/routes/admin/
- chart-of-accounts.ts (Rutas HTTP)
Patrón Repository con Mapeo
Section titled “Patrón Repository con Mapeo”private static mapRow(row: any): LegalRep { return { fecha_desde: row.fecha_nombramiento, // Mapea DB a dominio fecha_hasta: row.fecha_termino, vigente: row.activo, // ... otros campos };}Configuración de Entorno
Section titled “Configuración de Entorno”Variables Requeridas
Section titled “Variables Requeridas”| Variable | Servicio | Propósito |
|---|---|---|
PORT | Orchestrator | Puerto HTTP (8000) |
DB_HOST | Orchestrator | Host PostgreSQL |
DB_PORT | Orchestrator | Puerto PostgreSQL (5432) |
DB_USER | Orchestrator | Usuario de BD |
DB_PASSWORD | Orchestrator | Contraseña de BD |
JWT_SECRET | Orchestrator | Clave de firma de token (256+ bits) |
LIQUIDACIONES_DIR | Orchestrator | Ruta de almacenamiento PDF |
Arquitectura de Despliegue
Section titled “Arquitectura de Despliegue”localhost:4321 ← Sevastopol (Vite dev server) ↓ (proxy /api/*)localhost:8000 ← Orchestrator (nodemon) ↓localhost:5432 ← PostgreSQL (Docker)- Sevastopol: Build estático
npm run buildservido por Nginx/CDN - Orchestrator: Proceso Node.js detrás de proxy reverso (Nginx/Caddy)
- PostgreSQL: Servidor dedicado con connection pooling (PgBouncer)