Arquitectura (MMVC)
Cómo los módulos, controladores, vistas, modelos, el registro y el despachador orbitan entre sí.
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.
El modelo mental en 30 segundos
Sección titulada «El modelo mental en 30 segundos» ┌──────────────┐ │ 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/* └──────────────┘Los cinco ciudadanos
Sección titulada «Los cinco ciudadanos»1. Controladores (application/controller/)
Sección titulada «1. Controladores (application/controller/)»Un controlador es una clase que extiende Nibiru\Adapter\Controller. El despachador invoca una secuencia fija en cada solicitud:
navigationAction()— poblar datos de menús / rastro de migas.<_action>Action()— solo si?_action=fooestá establecido.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.
2. Vistas (application/view/templates/)
Sección titulada «2. Vistas (application/view/templates/)»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}.
3. Modelos (application/model/)
Sección titulada «3. Modelos (application/model/)»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.
4. Módulos (application/module/<nombre>/)
Sección titulada «4. Módulos (application/module/<nombre>/)»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.
5. Singleton que mantiene el cosmos unido
Sección titulada «5. Singleton que mantiene el cosmos unido»| Singleton | Trabajo |
|---|---|
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. |
¿Por qué MMVC, no MVC?
Sección titulada «¿Por qué MMVC, no MVC?»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.
El patrón observador, en práctica
Sección titulada «El patrón observador, en práctica»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
Dbo 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/SplObserverson 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.
Dónde ir a continuación
Sección titulada «Dónde ir a continuación»- Bootstrap & Dispatcher — el ciclo de vida en código.
- Routing — reglas de mapeo URL → controlador.
- Módulos — construye tu primero.