2. Arquitectura de iDempiere
iDempiere corre como un solo proceso Java (una JVM) que aloja, al mismo tiempo, el contenedor
OSGi con todos los bundles, el servidor web Jetty, la interfaz ZK y la API REST. No hay
microservicios separados: todo vive dentro de ese proceso, lo que simplifica el despliegue (un
servicio systemd, idempiere.service, en la instalación de referencia de FINORA) a cambio de que
toda actualización de un bundle de interfaz requiera reiniciar ese único proceso.
Las piezas
Sección titulada «Las piezas»flowchart TB
subgraph JVM["Un solo proceso Java (Equinox / OSGi)"]
direction TB
Jetty["Jetty embebido<br/>(servidor HTTP)"]
ZK["org.adempiere.ui.zk<br/>(cliente web ZK)"]
REST["idempiere-rest<br/>(API REST)"]
Base["org.adempiere.base<br/>(modelo, reglas, Diccionario)"]
Plugins["Plugins OSGi<br/>(FINORA LVE: base, fiscal, contable, nómina)"]
Jetty --> ZK
Jetty --> REST
ZK --> Base
REST --> Base
Plugins -.->|"event handlers, fábricas, 2Pack"| Base
end
Browser["Navegador"] -->|HTTPS| Jetty
Base --> PG[("PostgreSQL")]
- El servidor es la JVM completa: arranca Equinox (la implementación de OSGi que usa iDempiere), que a su vez arranca todos los bundles instalados, entre ellos Jetty.
- El contenedor OSGi (ver Tutorial 6) es lo que hace posible que
el core, la interfaz ZK, el REST y los plugins de FINORA convivan como módulos independientes con
sus propias dependencias declaradas, en vez de un único
.warmonolítico. - El cliente web ZK (
org.adempiere.ui.zk, ver Tutorial 5) es la interfaz que ve una persona que entra por el navegador a/webui: ventanas, pestañas, grillas — todo generado en el servidor y enviado como fragmentos de interfaz actualizados por Ajax. - La API REST (
com.trekglobal.idempiere.rest.api, ver Tutorial 10) expone el mismo modelo de datos y las mismas reglas de negocio bajo/api/v1, para integraciones que no pasan por el navegador (el portal de FINORA, apps, otros sistemas). - La base de datos es PostgreSQL. iDempiere no usa un ORM de terceros: el acceso a datos pasa por
las clases
PO/M*del propio core, que saben leer su estructura desde el Diccionario de Aplicación (AD_Table,AD_Column) y aplicar reglas de acceso por cliente, organización y rol antes de tocar la base.
El ciclo de vida de una petición
Sección titulada «El ciclo de vida de una petición»Tomemos el caso más común: alguien completa una factura de compra desde la ventana Factura de Proveedor en el navegador.
sequenceDiagram
participant N as Navegador (ZK)
participant J as Jetty
participant Z as org.adempiere.ui.zk
participant M as Modelo (MInvoice, PO)
participant E as Event handlers (core + FINORA LVE)
participant D as PostgreSQL
N->>J: Clic en "Completar" (petición Ajax)
J->>Z: Despacha el evento del componente ZK
Z->>M: MInvoice.processIt("CO")
M->>E: dispara DOC_BEFORE_COMPLETE
E->>E: FINORA: congela tasa BCV, calcula retenciones
M->>D: UPDATE dentro de una transacción
M->>E: dispara DOC_AFTER_COMPLETE
E->>E: FINORA: materializa comprobante de retención
M-->>Z: resultado (éxito o mensaje de error)
Z-->>N: actualiza la ventana (estado, mensaje)
Los mismos pasos —validar, disparar eventos DOC_BEFORE_*/DOC_AFTER_*, escribir en una
transacción, disparar DOC_AFTER_* de nuevo— ocurren exactamente igual si la petición llega por
PUT /api/v1/models/c_invoice/{id} con {"doc-action":"CO"} en vez de por un clic en ZK
(finora-erp-experience/idempiere/API_MAP.md, §6.2: “la misma acción de flujo de documento que el
botón Process de ZK”). Esto es intencional: las reglas de negocio de FINORA se escriben una sola vez
como event handlers sobre el modelo (PO), y valen tanto para la interfaz web como para
integraciones REST (finora-lve/decisions/ADR.md, ADR-LVE-004).
Dónde vive cada cosa en el disco
Sección titulada «Dónde vive cada cosa en el disco»En un servidor instalado, la carpeta plugins/ contiene un directorio por bundle
(org.adempiere.base_13.0.0.<qualifier>/, org.idempiere.zk.iceblue_c.theme_13.0.0.<qualifier>/,
com.trekglobal.idempiere.rest.api_1.0.0.<qualifier>/, y los bundles de FINORA LVE:
com.finora.lve.base_<versión>/, etc.), listados en
configuration/org.eclipse.equinox.simpleconfigurator/bundles.info
(finora-erp-experience/idempiere/EXTENSION_GUIDE.md, §5). No hay dropins ni recarga automática de
archivos: instalar o actualizar un bundle significa copiar su carpeta, editar bundles.info, y
reiniciar el servicio.
Un solo proceso, no un clúster horizontal
Sección titulada «Un solo proceso, no un clúster horizontal»Que todo viva en una JVM tiene una consecuencia que conviene conocer antes de planear una instalación: ZK guarda el estado de cada sesión (qué ventana tiene abierta cada usuario, en qué pestaña, con qué filtros) en el servidor, no en el navegador. Eso hace que poner dos instancias de iDempiere detrás de un balanceador de carga sin session affinity (afinidad de sesión) rompa la interfaz a mitad de uso — el balanceador podría mandar la siguiente petición del mismo usuario a una instancia que no tiene su sesión. La forma soportada de escalar la carga de un tenant grande es verticalmente (más memoria y CPU a la misma JVM) o repartiendo tenants distintos en instancias distintas, no repartiendo un mismo tenant entre varias instancias sin sticky sessions.
Fuentes
Sección titulada «Fuentes»- What is iDempiere? — iDempiere Developer
- Repositorio idempiere/idempiere en GitHub
finora-erp-experience/idempiere/API_MAP.md, §6.2 (acciones de documento por REST)finora-erp-experience/idempiere/EXTENSION_GUIDE.md, §5 (bundles.info, Equinox, systemd)finora-lve/decisions/ADR.md, ADR-LVE-004 (event handlers OSGi, mismas reglas por ZK y REST)