Ir al contenido
Nibiru docsv0.9.2

Arquitectura (MMVC)

Cómo los módulos, controladores, vistas, modelos, el registro y el despachador orbitan entre sí.

Stable Reading time ~ 3 min Edit on GitHub

Nibiru es MMVC: Modelo — Vista — Controlador — y una segunda M para Módulo. Las primeras tres son familiares; la segunda M es lo que da a Nibiru su sabor.

Ilustración en sección transversal del entorno de ejecución Nibiru dibujado como un loto, con cada pétalo etiquetado como un componente del sistema: Router, Controller, View, Model, Module, Registry.
El entorno de ejecución Nibiru, dibujado como un loto.
┌──────────────┐
│ Browser │
└──────┬───────┘
│ HTTP
┌──────────────┐
│ index.php │
└──────┬───────┘
│ require
┌──────────────┐
│ framework.php│ ◀── boots Config, Router, Engine, Smarty,
└──────┬───────┘ all 28 form types, DB drivers, Auth.
┌──────────────┐
│ Dispatcher │
└──────┬───────┘
┌───────────────┼─────────────────┐
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌───────────┐
│ Router │ │ Modules │ │ Auto │
└────────┘ └──────────┘ │ loader │
└───────────┘
┌─────────────────────────────┐
│ applicationController.php │
│ navigationAction() │
│ <_action>Action() │
│ pageAction() │
└──────────────┬──────────────┘
│ View::assign(...)
┌──────────────┐
│ Smarty │ templates/<ctrl>.tpl + shared/*
└──────────────┘

Un controlador es una clase que extiende Nibiru\Adapter\Controller. El despachador invoca una secuencia fija en cada solicitud:

  1. navigationAction() — poblar datos de menús / rastro de migas.
  2. <_action>Action() — solo si ?_action=foo está establecido.
  3. pageAction() — asignación final de datos en tiempo de renderizado.

Después de que todos los tres regresen, Display::display() pasa las variables asignadas a Smarty.

Archivos Smarty .tpl. El despachador resuelve automáticamente <controller>.tpl; las acciones anidadas van bajo templates/<controller>/<action>.tpl. Todas las variables pasadas a través de View::assign(['x' => ...]) están disponibles como {$x}.

Los modelos son generados automáticamente desde su esquema de base de datos por Model::__construct(false) — una clase PHP por tabla. Estas clases extienden Nibiru\Adapter\<Driver>\Db y exponen ayudantes CRUD. Puede editar manualmente un modelo generado y deshabilitar el regenerador con [GENERATOR] database = false.

Un módulo agrupa sus propios tratos, complementos, interfaces y configuraciones. El Registro auto-descubre cada archivo settings/*.ini de los módulos y expone la configuración analizada a través de Registry::getInstance()->loadModuleConfigByName('users'). Los módulos pueden implementar SplSubject para el patrón observador, permitiendo que otras partes del sistema adjunten observadores y reaccionen a los cambios de estado.

SingletonTrabajo
Config::getInstance()Lee settings.<env>.ini y fusiona las configuraciones de módulo.
Router::getInstance()Analiza la URL en controlador/acción/parámetros; reconoce formas de URL SEO.
Registry::getInstance()Descubrimiento de módulos + caché de configuración.
Dispatcher::getInstance()El conductor. dispatch::run() es el latido de su aplicación.
View::getInstance()Envuelve Smarty. View::assign() es la caja de entrada global de variables de plantilla.

El MVC simple funciona hasta que tus controladores comienzan a compartir lógica — verificaciones de autenticación, fábricas de formularios, clientes de API de terceros. Las respuestas usuales son servicios + contenedor DI, pero eso es mucho ceremonia para un marco de prototipado rápido.

La respuesta de Nibiru: módulos. Un módulo posee un dominio (usuarios, cms, analíticas, cotizaciones-tpms), expone sus servicios a través de controladores de plugins que se pueden instanciar directamente:

// In a controller
$user = new \Nibiru\Module\Users\Plugin\User();
if (!$user->isAuthorized()) {
View::forwardTo('/login');
}

El módulo posee su propia configuración, sus tablas de base de datos, sus plantillas y sus formularios — y es removible como una unidad.

Algunos módulos implementan SplSubject para que otro código pueda reaccionar a eventos sin acoplamiento. En el showcase, el módulo analytics en prod.maschinen-stockert.de hace exactamente esto: cualquier controlador puede attach() un tracker (plugin de Matomo) y el módulo de análisis notify()s en cada vista de página, sin que el controlador sepa qué trackers existen.

class Analytics implements \SplSubject {
private \SplObjectStorage $observers;
public function __construct() { $this->observers = new \SplObjectStorage(); }
public function attach(\SplObserver $o): void { $this->observers->attach($o); }
public function detach(\SplObserver $o): void { $this->observers->detach($o); }
public function notify(): void {
foreach ($this->observers as $o) { $o->update($this); }
}
}

¿Qué está deliberadamente fuera del marco?

Sección titulada «¿Qué está deliberadamente fuera del marco?»
  • No contenedor DI. Los singletons y los plugins hacen el trabajo.
  • No ORM. Los modelos se generan desde el esquema; las consultas utilizan el adaptador Db o SQL crudo a través del controlador activo.
  • No herencia de plantillas a través de trucos de Twig/Blade. Smarty {include} es la unidad de composición.
  • No bus de eventos. SplSubject/SplObserver son de primera clase.
  • No trabajos en segundo plano de primer nivel. La CLI es tu programador — úsala desde cron o systemd timers.

Menos que aprender, más para enviar.