Arquitectura
Esta página describe cómo está organizada una instalación de Histrix y cómo el motor resuelve qué XML ejecutar. Es la base para entender por qué un mismo programa puede comportarse distinto en dos bases de la misma instalación.
Estructura de directorios
Sección titulada «Estructura de directorios»Una instalación tiene el motor en la raíz y los datos de cada cliente separados en
database/:
histrix/ src/ Motor (namespace \Histrix\) admin/ Programas de administración y migraciones del core modules/ Módulos funcionales compartidos (erp-base, erp-full, ...) plugins/ Plugins del core lang/ Traducciones de la interfaz (es, en, fr, pt) js/ css/ Assets del cliente web database/ config.xml Configuración de conexiones y empresas {cliente}/ xml/ XML propios del cliente (sobrescriben los del módulo) files/ Archivos subidos log/{año}/{mes}/ Logs de SQL y errores plugins/ Plugins propios del cliente tests/ Tests de aceptación de la base vendor/ Dependencias de Composerdatabase/ y modules/ son repositorios independientes del motor. Un cambio en un
módulo o en los XML de un cliente no se versiona junto con el core.
Configuración de conexiones
Sección titulada «Configuración de conexiones»database/config.xml declara una entrada <base> por cada base atendida. El atributo
xmlPath define el directorio raíz de sus XML, es decir database/{xmlPath}/xml/:
<?xml version="1.0" encoding="UTF-8"?><sistema> <empresa> <img_fondo>histrix_back.jpg</img_fondo> <lang>es</lang> </empresa> <conexiones> <base id="cliente" tipo="mysql" xmlPath="cliente/"> <descripcion>Cliente S.A.</descripcion> <base>htx_cliente</base> <driver>mysql</driver> <user>usuario</user> <password>clave</password> <host>db</host> <empresa> <nombre>Cliente S.A.</nombre> <direccion>Dirección</direccion> <cuit>30-00000000-0</cuit> <logo_pdf_1 posx="6" width="30">logo.jpg</logo_pdf_1> <logo_ini>logo.png</logo_ini> <modulos>|clientes|proveedores|stock|iva|contabilidad</modulos> </empresa> </base> </conexiones></sistema>El atributo opcional group en <base> filtra qué bases aparecen en el selector
según el subdominio por el que se accede.
Resolución de XML
Sección titulada «Resolución de XML»Cada base tiene además un parámetro CONFIG::baseModule en la tabla HTXOPTIONS
(por ejemplo erp-base o erp-full) que define contra qué módulo hace fallback. Al
pedir un XML, el motor prueba las rutas en este orden y toma la primera que existe:
database/{xmlPath}/xml/{dir}/{archivo}database/{xmlPath}/xml/{archivo}modules/{baseModule}/{dir}/{archivo}modules/{dir}/{archivo}admin/{dir}/{archivo}modules/erp-base/{dir}/{archivo}
Consecuencias prácticas:
- Un XML colocado en la raíz del árbol del cliente (paso 2) satisface cualquier
dir=, porque ese paso ignora el subdirectorio. - Si el
xmlPathde una base apunta a un directorio vacío, todo se resuelve por fallback al módulo. - Dos bases con
baseModuledistinto pueden cargar versiones diferentes del mismo archivo. Si un programa se comporta distinto entre bases del mismo motor, comparar primeroCONFIG::baseModuley el contenido de cadaxmlPath.
Overrides de módulo
Sección titulada «Overrides de módulo»Un XML en database/{cliente}/xml/ reemplaza por completo al del módulo: no hay
merge parcial de campos ni de atributos. Por lo tanto:
- Antes de modificar un override, compararlo con
diffcontra el original del módulo. Un override desactualizado se rompe en silencio cuando el archivo del módulo evoluciona. - Al crear un override, dejar registrado qué se cambió respecto del original.
Ciclo de vida de una instancia
Sección titulada «Ciclo de vida de una instancia»Cuando se abre un programa, el motor parsea el XML y construye un contenedor
(\Histrix\Data\Container) con las tablas, campos y condiciones declaradas. Ese
contenedor se serializa en la sesión y queda identificado por un id de instancia,
que es el que usan todas las operaciones siguientes:
GET /api/db/{db}/instance/xml/{ruta.xml} crea la instanciaGET /api/db/{db}/instance/{instance} lee datosPUT /api/db/{db}/instance/{instance}/set/ actualiza un campoPOST /api/db/{db}/instance/{instance}/process/ grabaDELETE /api/db/{db}/instance/{instance} destruye la instanciaEl estado vive en la sesión de PHP, no en el cliente: los campos calculados en el servidor, los valores de los filtros y las filas confirmadas de una grilla de ingreso se resuelven contra esa instancia. La interfaz web y cualquier cliente externo usan las mismas rutas.
- PHP 8 con Slim 3 para el enrutamiento HTTP y Composer para las dependencias.
- Acceso a datos por PDO. El tag
<driver>de cada base selecciona la implementación:mysql(valor por omisión),mssql,dblibosqlsrvpara SQL Server,progressysqlite. - OAuth 2.0 y OpenID Connect para autenticación de la API.
- Redis como caché de aplicación y almacenamiento de las filas temporales de los formularios de ingreso.
- jQuery, jQuery UI y DataTables en el cliente web.
El servidor Redis se resuelve, en este orden, desde los tags <redis_server> y
<redis_password> de la base en config.xml, desde las variables de entorno
REDIS_SERVER_HOST y REDIS_SERVER_PASSWORD, o por defecto localhost.