---
title: "アーキテクチャ (MMVC)"
description: "モジュール、コントローラー、ビュー、モデル、レジストリ、ディスパッチャーがそれぞれ他に回る関係です。"
---

Nibiruは**MMVC**：Model — View — Controller — そして2つ目の**M**が**Module**です。最初の3つは一般的ですが、2つ目のMがNibiruに独特な風味を与えます。

<figure>
  <img src="/img/architecture-lotus.png" alt="Nibiruランタイムの横断図で、各葉がシステムコンポーネントとしてラベル付けされています：Router、Controller、View、Model、Module、Registry。" />
  <figcaption>Nibiruランタイムを花として表現。</figcaption>
</figure>

## 30秒のメンタルモデル
```
                ┌──────────────┐
                │   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/*
                └──────────────┘
```
## 五人の市民

### 1. コントローラー (`application/controller/`)

コントローラーは、`Nibiru\Adapter\Controller` を拡張するクラスです。ディスパッチャーは、すべてのリクエストに対して固定のシーケンスを呼び出します：

1. `navigationAction()` — メニュー/パンくずデータを設定します。
2. `<_action>Action()` — 仅当 `?_action=foo` が設定されている場合にのみ。
3. `pageAction()` — 最終的なレンダリング時データの割り当て。

すべての3人が戻ったら、`Display::display()` は割り当てられた変数を Smarty に渡します。

### 2. ビュー (`application/view/templates/`)

Smarty `.tpl` ファイル。ディスパッチャは `<controller>.tpl` を自動的に解決します；ネストされたアクションは `templates/<controller>/<action>.tpl` の下に置かれます。`View::assign(['x' => ...])` で渡されたすべての変数は `{$x}` として利用できます。

### 3. モデル (`application/model/`)

モデルは **自動生成** され、データベーススキーマから `Model::__construct(false)` によって生成されます — 1つのテーブルにつき1つのPHPクラスです。これらは `Nibiru\Adapter\<Driver>\Db` を拡張し、CRUDヘルパーを公開しています。生成されたモデルを手で編集し、再生成を無効にするには `[GENERATOR] database = false` を設定します。

### 4. モジュール (`application/module/<name>/`)

A モジュールは**その自らの**トレイト、プラグイン、インターフェース、および設定をバンドルします。[Registry](/core/registry/) は各モジュールの `settings/*.ini` を自動的に発見し、解析された構成を `Registry::getInstance()->loadModuleConfigByName('users')` 経由で公開します。モジュールは **observer パターン** を実装して、システムの他の部分がオブザーバーをアタッチし、状態変化に反応できるようにすることができます。

### 5. コズモスを結ぶシングルトン

| シングルトン | ジョブ |
|---|---|
| `Config::getInstance()` | `settings.<env>.ini` を読み込み、モジュールの設定をマージします。 |
| `Router::getInstance()` | URL をコントローラー/アクション/パラメータに解析し、SEO 形式の URL を認識します。 |
| `Registry::getInstance()` | モジュールの発見と設定のキャッシュを行います。 |
| `Dispatcher::getInstance()` | コンダクターです。`dispatch::run()` はアプリケーションのハートビートです。 |
| `View::getInstance()` | Smarty をラップします。`View::assign()` はグローバルテンプレート変数のインボックスです。 |

## なぜMMVCなのか、MVCではないのか？

シンプルなMVCは、コントローラーがロジックを共有し始めるまで機能します — 認証チェック、フォームファクトリー、サードパーティAPIクライアントなど。一般的な回答は*サービス + DIコンテナ*ですが、これは素早くプロトタイピングするためのフレームワークには多すぎる儀式です。

Nibiruの回答: **モジュール**。モジュールはドメイン（`users`, `cms`, `analytics`, `tpms-quotes`）を「所有」し、プラグインコントローラーを通じてそのサービスを公開します。これらのコントローラーは直接インスタンス化できます：
```php
// In a controller
$user = new \Nibiru\Module\Users\Plugin\User();
if (!$user->isAuthorized()) {
    View::forwardTo('/login');
}
```
モジュールは、その設定、データベーステーブル、テンプレート、フォームを所有しており、単体で削除可能です。

## オブザーバパターンの実践的な使用法

一部のモジュールは `SplSubject` を実装しているため、他のコードがイベントに反応できるようにする。デモンストレーションでは、`prod.maschinen-stockert.de` 上の `analytics` モジュールがこれを行っている：任意のコントローラーは `attach()` でトラッカー（Matomo プラグイン）をアタッチし、`analytics` モジュールはページビューごとに `notify()` する。コントローラーは存在するトラッカーについて何も知らない。
```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); }
    }
}
```
## フレームワークに意図的に含まれていないもの

- **DIコンテナなし。** シングルトンとプラグインで十分です。
- **ORMなし。** モデルはスキーマから生成され、クエリは`Db`アダプターまたはアクティブなドライバーを介した生のSQLを使用します。
- **Twig/Bladeのトリックを通じたテンプレート継承なし。** Smarty `{include}`が構成単位です。
- **イベントバスなし。** `SplSubject`/`SplObserver`は第一級です。
- **ファーストクラスのバックグラウンドジョブなし。** CLIがスケジューラです — cronやsystemdタイマーからドライブします。

学ぶことが少なく、出荷することが多い。

## 次にどこへ行くか

- [Bootstrap & Dispatcher](/core/dispatcher/) — コードでのライフサイクル。
- [Routing](/core/routing/) — URL → コントローラーのマッピング規則。
- [Modules](/core/modules/) — 最初のモジュールを作成する。
