---
title: "Arquitectura (MMVC)"
description: "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.

<figure>
  <img src="/img/architecture-lotus.png" alt="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." />
  <figcaption>El entorno de ejecución Nibiru, dibujado como un loto.</figcaption>
</figure>

## 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

### 1. Controladores (`application/controller/`)

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.

### 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/`)

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>/`)

Un módulo agrupa **sus propios** tratos, complementos, interfaces y configuraciones. El [Registro](/core/registry/) 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

| 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?

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:
```php
// 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

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.
```php
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?

- **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.

## Dónde ir a continuación

- [Bootstrap & Dispatcher](/core/dispatcher/) — el ciclo de vida en código.
- [Routing](/core/routing/) — reglas de mapeo URL → controlador.
- [Módulos](/core/modules/) — construye tu primero.
