Skip to content

Plataforma técnica · Convenciones

Convenciones de Código

Desarrollo Convenciones

Estas convenciones aplican a los cuatro sistemas del ecosistema (Mother, Orchestrator, Sevastopol, Nostromo) y al sitio de documentación Jean d’Arc. Cuando una regla es específica de un componente, está marcada.

  1. Claridad sobre brevedad — Código legible gana sobre código corto.
  2. Consistencia con el código existente — Imitar los patrones que ya están en el repositorio antes de proponer otros nuevos.
  3. Explícito sobre implícito — Nombrar claramente, evitar abreviaciones, evitar magia.
  4. Fail fast — Validar en el borde (entrada HTTP, parsing CLI, lectura de .env); fallar con mensaje claro.
  5. Una capa, una responsabilidad — Route maneja HTTP, Service orquesta, Engine calcula, Repository persiste. No mezclar.

ComponenteGestorNotas
orchestrator/pnpm (workspace)El monorepo Accounting/ pinea [email protected] en packageManager. Scripts: dev, build, test:unit, …
sevastopol/pnpm (workspace)Mismo workspace que Orchestrator; comparte pnpm-workspace.yaml.
accounting_system/pip + venvSin dependencias compartidas con Node.
jean_d_arc/pnpm (corepack) onlynpm está prohibido. El package-lock.json no se mantiene y pnpm-workspace.yaml solo aplica con pnpm.

Aplica a Orchestrator y Sevastopol.

ElementoConvenciónEjemplo
ArchivosPascalCase para clases; camelCase para utilidadesPayrollService.ts, dateUtils.ts
ClasesPascalCaseContractRepository, PayrollEngine
InterfacesPascalCase sin prefijo IServiceContext, PayrollInput
TypesPascalCaseHealthMode, GratificationMode
FuncionescamelCasecalculateGrossPay, getTenantPool
ConstantesUPPER_SNAKE_CASEMAX_RETRIES, JWT_EXPIRES_IN
VariablescamelCasetotalAmount, baseSalary

Los servicios, engines y repositories del Orchestrator usan métodos estáticos, no clases con constructor. La instancia de pool se pasa como argumento.

PayrollService.ts
export class PayrollService {
static async generatePayroll(
pool: Pool,
input: PayrollInput,
): Promise<PayrollResult> {
// 1. Cargar contexto (Repository)
const ctx = await PayrollRepository.getPayrollContext(pool, input);
// 2. Cálculo puro (Engine)
const result = PayrollEngine.calculate(ctx);
// 3. Persistir (Repository)
await PayrollRepository.savePayroll(pool, result);
return result;
}
}

Sin acceso a base de datos ni Pool. Solo recibe input, retorna result. Permite unit tests sin mockear PG.

export class PayrollEngine {
static calculate(input: PayrollInput): PayrollResult {
const base = BaseSalaryCalculator.calculate(input);
const grat = GratificationCalculator.calculate(input);
// ...
return { total_liquido, total_haberes, total_descuentos };
}
}
// 1. Node.js built-ins
import { readFileSync } from "node:fs";
// 2. Dependencias externas
import express from "express";
import { Pool } from "pg";
// 3. Alias internos (@/lib, @/middleware, @/domain, @/routes)
import { getTenantPool } from "@/lib/db";
import { authenticateToken } from "@/middleware/auth";
// 4. Imports relativos
import { PayrollRepository } from "./PayrollRepository";
import type { PayrollInput } from "./types";

Convención crítica del Orchestrator:

LadoConvenciónEjemplo
Variables internascamelCasetotalHaberes, sueldoBase
Columnas PostgreSQLsnake_casetotal_haberes, sueldo_base
Respuestas APIsnake_case{ "total_haberes": 1500000 }

Las respuestas de la API replican la forma de la base de datos para evitar una capa de transformación adicional. El frontend consume snake_case directamente.


ElementoConvenciónEjemplo
Basesnostromo_<contexto>nostromo_common, nostromo_command, nostromo_60004317
Schemassnake_case, sin prefijoremuneraciones, operaciones_sii, parametros, command, accounting
Tablassnake_case, pluralcontratos, empleados, liquidaciones
Vistasv_<descripción>v_liquidaciones_departamento
Columnassnake_casefecha_inicio, monto_bruto
Índicesidx_<tabla>_<columna>idx_contratos_empleado_id
Foreign keysfk_<tabla>_<referencia>fk_contratos_empleados
-- Aliases descriptivos, JOIN explícito, columnas listadas
SELECT
c.id,
c.fecha_inicio,
c.monto_bruto,
e.nombres,
e.apellido_paterno
FROM remuneraciones.contratos c
INNER JOIN remuneraciones.empleados e ON e.id = c.empleado_id
WHERE c.activo = true
ORDER BY c.fecha_inicio DESC;
  • SELECT * solo en pruebas exploratorias.
  • Aplicar filtros y agregaciones en vistas (v_*), no en TypeScript (principio Hybrid Core).

Aplica a los loaders en accounting_system/.

ElementoConvenciónEjemplo
Archivossnake_casebc_loader.py, previred_loader.py
Funcionessnake_caseload_uf_series, parse_period
ClasesPascalCaseBcLoader, SiiScraper
ConstantesUPPER_SNAKE_CASEBASE_URL, DEFAULT_TIMEOUT

Cada loader expone una CLI con argparse, lee .env con python-dotenv, conecta a Mother con psycopg2 y soporta --dry-run.

def main() -> int:
args = parse_args()
load_dotenv()
with get_connection() as conn:
rows = fetch_from_source(args.period)
if args.dry_run:
print(f"DRY-RUN: {len(rows)} filas")
return 0
insert_rows(conn, rows)
return 0
FlagUso
--dry-runEjecutar el loader sin escribir en la base.
--periodPeríodo objetivo en formato YYYY-MM.
-fi, -fnFecha inicial y final (loaders con rango de fechas).
-mMétrica o moneda a cargar (UF, USD, EUR).

TipoUbicaciónNaming
Layoutssrc/layouts/BaseLayout.astro
Pagessrc/pages/index.astro, [slug].astro
Islandssrc/islands/<dominio>/PayrollViewIsland.tsx
UI Componentssrc/components/ui/Button.tsx, Modal.tsx
Hookssrc/hooks/useTenant.ts, useSession.ts

El patrón vigente para fetch + render usa createResource, no createSignal + onMount.

import { createResource, Show, For } from "solid-js";
export function EmployeesViewIsland() {
const [employees] = createResource(async () => {
const res = await fetch("/api/employees");
return res.json();
});
return (
<Show when={!employees.loading} fallback={<Spinner />}>
<For each={employees()}>
{(emp) => <EmployeeRow employee={emp} />}
</For>
</Show>
);
}

createSignal se reserva para estado local del componente (formularios, tabs, modales).


TipoSufijoQué cubre
Unit*.unit.test.tsFunciones puras de la capa Engine. Sin acceso a DB.
Integration*.integration.test.tsRepository contra una base de datos real (o transacciones).
E2E*.e2e.test.tsCiclo HTTP completo vía supertest.
CapaCobertura mínima
Endpoints críticos100% (auth, generación de nómina)
Engine (lógica)90%+
Repository (SQL)70%+

E2E con @playwright/test. Unit/component tests con vitest cuando aplique. Sin mockear el backend en E2E: levantar Orchestrator contra una base de pruebas.


<tipo>(<scope>): <descripción corta>
[cuerpo opcional]
[footer opcional]
TipoUso
featNueva funcionalidad
fixCorrección de bug
docsDocumentación
refactorRefactor sin cambio funcional
styleFormato, indentación, sin cambio lógico
testAgregar o ajustar tests
choreMantenimiento, deps, config
feat(payroll): agregar prorrateo de gratificación al cálculo bruto
- Implementa cálculo proporcional sobre días trabajados
- Cubre tope de 4.75 SMM y modalidad 25% mensual
- Agrega unit tests para edge cases (mes parcial, recontratado)
Closes #123
  • Mensajes en español (consistente con el corpus contable).
  • Imperativo: “agregar”, “corregir”, “mover” — no “agregado” ni “agregamos”.
PatrónUso
mainProducción (deploy automático en CD).
feature/<nombre>Nueva funcionalidad.
fix/<nombre>Corrección de bug.
docs/<nombre>Documentación.
refactor/<nombre>Refactor.

Las ramas se crean directamente desde main y se mergean vía PR. No hay rama develop.


---
title: Título descriptivo
description: Una línea breve para SEO y preview.
sidebar:
label: Etiqueta en sidebar
order: 1
updated: 2026-05-23
audience: dev # dev | auditor | both
domain: orchestrator # ver lista canónica en DEV-STRUCTURE.md
layer: orchestrator # mother | orchestrator | sevastopol | nostromo | infraestructura | seguridad | accounting
kind: reference # reference | concept | service | ui | runbook | standard
badge: Orchestrator
tags:
- orchestrator
topic:
- orchestrator
related:
upstream:
- /dev/orchestrator/
downstream: []
references:
- /accounting/remuneraciones/
---
ClaveQué incluye
upstreamPadre directo en el árbol de carpetas (solo uno; raíces sin upstream).
downstreamHijos directos. Solo el index de cada sección los lista.
referencesCualquier cruce que no sea padre/hijo directo (dev↔accounting, servicio→island, etc.).
standardsSolo páginas IFRS/NIC en /accounting/ifrs/.
accountsCódigos del manual de cuentas (solo en páginas accounting).
patternsPatrones de diseño implementados (solo en páginas kind: service).

Regla de oro: si un link no es padre/hijo directo en el árbol de carpetas, va en references.

  • El title del frontmatter es el H1. Nunca escribir # en el cuerpo.
  • Las secciones del cuerpo empiezan en ##.
  • Jerarquía: #########.

Usar la sintaxis fenced de Starlight, no <Aside>:

:::note
Nota informativa.
:::
:::tip
Sugerencia o atajo útil.
:::
:::caution
Advertencia que el lector debe leer antes de actuar.
:::
:::danger
Acción irreversible o que rompe producción.
:::

Bloques fenced con lenguaje mermaid. Renderizados por astro-mermaid.

<MermaidLightbox>
```mermaid
graph LR
A[Orchestrator] --> B[Mother]
```
</MermaidLightbox>

LaTeX vía remark-math + rehype-katex. Inline con $...$, bloque con $$...$$ en línea propia. No simular fórmulas con <sub> o <sup>.

En landings que apuntan a sub-páginas dev, usar LinkCard con la clase audience-link--dev. En landings contables, audience-link--auditor.

<LinkCard
class="audience-link--dev"
title="Orchestrator"
href="/dev/orchestrator/"
description="API Node.js y dominios DDD."
/>
  • <Steps> para procedimientos numerados.
  • <Tabs> / <TabItem> para alternativas (Linux/Windows, npm/pnpm).
  • <CardGrid> con <Card> o <LinkCard> para navegación o agrupación visual.
  • <FileTree> para árboles de directorios.
  • <Badge> para marcar audiencia o estado.