# アーキテクチャ設計書

## 概要

本ドキュメントは、Legacy CMS（コンテンツ管理システム）のアーキテクチャ設計について記述する。本システムはZend Framework 1.12をベースとしたPHPウェブアプリケーションであり、MVCアーキテクチャパターンを採用している。LAMPスタック（Linux/Apache/MySQL/PHP）上で動作し、記事、ページ、イベント、リソース等のコンテンツを管理する機能を提供する。

## システム全体構成

### アーキテクチャ概要図

システム全体のアーキテクチャ構成図は [アーキテクチャ構成図.md](./アーキテクチャ構成図.md) を参照。

### システム境界と外部連携

| 外部システム | 連携方式 | 用途 |
| --- | --- | --- |
| MySQL Database | PDO_MYSQL | コンテンツデータの永続化 |
| SMTP Server | Zend_Mail_Transport_Smtp | メール送信（パスワードリセット、通知等） |
| Google Maps API | HTTP API | イベント会場の地図表示 |
| Google Analytics | JavaScript埋め込み | アクセス解析 |
| YouTube API | HTTP API（YouTube3モデル） | 動画コンテンツ連携 |
| Twitter API | HTTP API | ソーシャルメディア連携 |

## レイヤー構成

### アーキテクチャスタイル

**MVC（Model-View-Controller）アーキテクチャ** - Zend Framework 1.12のMVC実装に基づく。

- **フロントコントローラーパターン**: すべてのHTTPリクエストは `public/index.php` を経由
- **モジュラーMVC**: admin/default の2つのモジュールで機能を分離
- **プラグインアーキテクチャ**: `Zend_Controller_Plugin` による横断的関心事の処理

### レイヤー定義

| レイヤー | 責務 | 主なコンポーネント |
| --- | --- | --- |
| Presentation | ユーザーインターフェース、HTMLレンダリング | `application/layouts/*.phtml`, `application/modules/*/views/` |
| Controller | リクエスト処理、ビジネスロジック呼び出し | `application/modules/*/controllers/*Controller.php` |
| Model | ビジネスロジック、データアクセス | `application/models/*.php` |
| Infrastructure | フレームワーク基盤、共通ライブラリ | `library/CMS/`, Zend Framework |

### レイヤー間の依存関係ルール

```
Presentation (View/Layout)
    ↓ 依存
Controller
    ↓ 依存
Model
    ↓ 依存
Infrastructure (Zend Framework, CMS Library)
```

**許可される参照:**
- Controller から Model への参照
- Controller から View への変数受け渡し（`$this->view`）
- Model から Zend_Registry 経由でのデータベースアクセス
- すべてのレイヤーから Infrastructure（Zend Framework）への参照

**禁止される参照:**
- Model から Controller への直接参照
- View から Model への直接参照

## モジュール構成

### ドメイン分割

| ドメイン | 責務 | 関連モジュール |
| --- | --- | --- |
| コンテンツ管理 | 記事・ページ・リソースのCRUD | Articles, Pages, Resources |
| イベント管理 | イベント・会場・カテゴリの管理 | Events, EventsCategories, EventsVenues |
| ユーザー管理 | 認証・認可・ユーザープロファイル | Users, Auth, Acl |
| アセット管理 | ファイルアップロード・画像リサイズ | Assets, Attachments |
| メール管理 | メーリングリスト・ニュースレター | Mail, MailGroups, MailSubscriptions |
| 検索 | 全文検索インデックス | Search（Zend_Search_Lucene） |
| コメント | コンテンツへのコメント機能 | Comments |
| その他 | タグ、ローテーター、YouTube連携 | Tags, Rotators, YouTube, Twitter |

### パッケージ構造

```
legacycms-master/
├── application/
│   ├── bootstrap.php           # アプリケーション起動
│   ├── initializer.php         # 初期化プラグイン
│   ├── configs/                # 設定ファイル
│   │   ├── config.ini          # メイン設定
│   │   ├── articles.ini        # 記事設定
│   │   ├── events.ini          # イベント設定
│   │   └── ...
│   ├── helpers/                # アクションヘルパー
│   │   ├── MakeDate.php
│   │   ├── Messages.php
│   │   ├── SearchIndex.php
│   │   └── Strings.php
│   ├── layouts/                # レイアウトテンプレート
│   │   ├── main.phtml          # フロント用
│   │   ├── admin.phtml         # 管理画面用
│   │   └── admin-auth.phtml    # 認証画面用
│   ├── models/                 # ビジネスロジック・データアクセス
│   │   ├── Articles.php
│   │   ├── Pages.php
│   │   ├── Comments.php
│   │   ├── Search.php
│   │   └── ...
│   └── modules/
│       ├── admin/              # 管理画面モジュール
│       │   ├── controllers/
│       │   └── views/
│       └── default/            # フロントエンドモジュール
│           ├── controllers/
│           ├── helpers/
│           └── views/
├── library/
│   └── CMS/                    # カスタムライブラリ
│       ├── Acl/Factory.php     # ACL生成
│       ├── Controller/
│       │   ├── Action/         # 基底コントローラー
│       │   └── Plugin/         # コントローラープラグイン
│       ├── Password/Strength.php
│       └── Wordcloud/
├── public/                     # ドキュメントルート
│   ├── index.php               # エントリーポイント
│   ├── .htaccess               # URL書き換え
│   └── _scripts/               # 外部スクリプト（FCKEditor等）
├── assets/                     # アップロードファイル保存先
├── cache/                      # キャッシュディレクトリ
└── bin/
    └── install.php             # インストールスクリプト
```

### コンポーネント依存関係

**コントローラー階層:**
```
Zend_Controller_Action
    ├── CMS_Controller_Action_Default    # フロントエンド用基底
    ├── CMS_Controller_Action_Admin      # 管理画面用基底（認証必須）
    ├── CMS_Controller_Action_Auth       # 認証画面用基底
    └── CMS_Controller_Action_Error      # エラー処理用基底
```

**主要な依存関係:**
- 全コントローラー → Zend_Registry（設定・DB接続取得）
- Model クラス → Zend_Db（データベース操作）
- Search モデル → Zend_Search_Lucene（全文検索）
- 認証 → Zend_Auth, Zend_Auth_Adapter_DbTable

## ミドルウェア構成

### データベース

| 種類 | ミドルウェア | バージョン | 用途 |
| --- | --- | --- | --- |
| RDB | MySQL | 5.x以上推奨 | コンテンツ・ユーザーデータの永続化 |

**テーブル一覧:**
- `articles`, `articles_categories` - 記事管理
- `pages` - ページ管理
- `events`, `events_categories`, `events_venues` - イベント管理
- `resources`, `resources_categories`, `resources_brands`, `resources_types` - リソース管理
- `users`, `users_profiles`, `users_roles`, `users_privileges`, `users_resources` - ユーザー・権限管理
- `assets`, `assets_folders`, `attachments` - アセット管理
- `mail`, `mail_groups`, `mail_subscriptions` - メール管理
- `comments` - コメント管理
- `tags` - タグ管理
- `rotators`, `rotators_slides` - ローテーター管理

### キャッシュ

| ミドルウェア | バージョン | 用途 | TTL |
| --- | --- | --- | --- |
| Zend_Cache (File Backend) | 1.12.20 | 一般的なキャッシュ | 10秒 |

**設定詳細（initializer.php より）:**
```php
$front = array(
    'lifetime' => 10,
    'automatic_serialization' => true
);
$back = array(
    'cache_dir' => '../cache'
);
$cache = Zend_Cache::factory('Core', 'File', $front, $back);
```

### メッセージキュー

| ミドルウェア | バージョン | 用途 |
| --- | --- | --- |
| なし | - | 本システムではメッセージキューは使用していない |

### 検索エンジン

| ミドルウェア | バージョン | 用途 |
| --- | --- | --- |
| Zend_Search_Lucene | 1.12.20 | サイト内全文検索 |

**インデックス保存先:** `/srv/legacycms/search/site-index` （config.ini で設定）

### その他ミドルウェア

| ミドルウェア | バージョン | 用途 |
| --- | --- | --- |
| Apache mod_rewrite | - | URLリライト（きれいなURL） |
| PHP GD Extension | - | 画像リサイズ・サムネイル生成 |
| getID3 | 1.9.15 | 音声ファイルメタデータ取得 |
| deresh/thumb | 1.0 | サムネイル生成 |

## データフロー

### リクエスト処理の流れ

1. **HTTPリクエスト受信**: Apache が `public/index.php` にリクエストを転送（mod_rewrite）
2. **Bootstrap**: `application/bootstrap.php` が実行され、Composer オートロードと初期化
3. **Front Controller**: `Zend_Controller_Front::getInstance()` でフロントコントローラー取得
4. **Initializer Plugin**: `Initializer` プラグインが登録され、環境設定を実行
5. **Route Startup**:
   - データベース接続初期化（`initDb()`）
   - アクションヘルパー登録（`initHelpers()`）
   - レイアウト初期化（`initView()`）
   - カスタムプラグイン登録（`initPlugins()`）
   - ルーティング設定読み込み（`initRoutes()`）
   - コントローラーディレクトリ設定（`initControllers()`）
6. **ルーティング**: `config.ini` の routes セクションに基づきコントローラー/アクションを決定
7. **Dispatch**: 対応するコントローラーのアクションメソッドを実行
8. **Model呼び出し**: コントローラーからモデルクラスを利用してビジネスロジック実行
9. **View レンダリング**: アクション対応のビュースクリプト（.phtml）をレイアウトと共にレンダリング
10. **レスポンス送信**: 生成されたHTMLをクライアントに返却

### 非同期処理の流れ

本システムでは非同期処理（ジョブキュー等）は実装されていない。すべての処理は同期的に実行される。

メール送信は `Zend_Mail` を使用して同期的に実行される。

### データ永続化の流れ

1. **コントローラーでのバリデーション**: `Zend_Filter_Input` でフォーム入力を検証・フィルタリング
2. **モデル呼び出し**: コントローラーからモデルの `newXxx()`, `updateXxx()`, `deleteXxx()` メソッドを呼び出し
3. **SQL発行**: モデル内で `$this->registry->db->insert/update/delete()` を使用
4. **検索インデックス更新**: コンテンツ変更時に `Search` モデル経由で Lucene インデックスを更新

## 横断的関心事

### 認証・認可

| 方式 | 実装箇所 | 対象 |
| --- | --- | --- |
| セッションベース認証 | `Zend_Auth`, `Zend_Session` | 管理画面ログイン |
| DBテーブル認証アダプタ | `Zend_Auth_Adapter_DbTable` | ユーザー名・パスワード検証 |
| MD5+Salt ハッシュ | `AuthController::loginAction()` | パスワード保存 |
| ACL（アクセス制御リスト） | `CMS_Acl_Factory`, `Zend_Acl` | リソース別権限制御 |
| Cookie記憶 | `setcookie()` | ログイン状態の永続化（オプション） |

**認証フロー:**
1. `Admin_AuthController::loginAction()` でログインフォーム処理
2. `Zend_Auth_Adapter_DbTable` で users テーブルを検証
3. 認証成功時、`Zend_Auth::getStorage()->write()` でセッションに保存
4. `CMS_Controller_Action_Admin::preDispatch()` で認証チェック
5. 未認証時は `admin/auth/login` にリダイレクト

**認可（ACL）:**
- `users_roles` テーブル: ロール定義（User, Guest, Administrator）
- `users_resources` テーブル: リソース定義
- `users_privileges` テーブル: ロールとリソースの紐付け
- `CMS_Acl_Factory::createGlobalAcl()`: 全体ACL構築
- 各コントローラーで `$this->view->acl->isAllowed()` による権限チェック

### ロギング・監査

| 種類 | 実装方式 | 保存先 |
| --- | --- | --- |
| アプリケーションログ | PHP標準エラー出力 | Apacheエラーログ |
| 監査ログ | 未実装 | - |
| アクセスログ | Apache標準 | Apacheアクセスログ |

**開発環境でのエラー表示:**
```php
error_reporting(E_ALL | E_STRICT);
ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
```

### エラーハンドリング

| エラー種別 | ハンドリング方式 | レスポンス |
| --- | --- | --- |
| 404 Not Found | `CMS_Controller_Plugin_ErrorSelector` | カスタムエラーページ |
| 500 Internal Error | Zend_Controller_Plugin_ErrorHandler | カスタムエラーページ |
| 認証エラー | コントローラーで処理 | ログインページにリダイレクト |
| 権限エラー | `_forward('privileges','error','admin')` | 権限エラーページ |
| バリデーションエラー | Zend_Filter_Input | フォーム再表示+エラーメッセージ |

**ErrorController の責務:**
- `application/modules/admin/controllers/ErrorController.php`
- `application/modules/default/controllers/ErrorController.php`

### トランザクション管理

| 範囲 | 管理方式 | 分離レベル |
| --- | --- | --- |
| 単一SQL文 | 自動コミット | MySQL デフォルト（REPEATABLE READ） |
| 複数テーブル更新 | 明示的トランザクション管理なし | - |

**注記:** 本システムでは明示的なトランザクション管理は実装されていない。削除操作時に関連データ（タグ、コメント、検索インデックス）を順次削除する処理があるが、トランザクションでラップされていないため、途中で失敗した場合のロールバックは行われない。

## 設計原則・コーディング規約

### 適用している設計原則

| 原則 | 適用箇所 | 実装例 |
| --- | --- | --- |
| DRY（Don't Repeat Yourself） | 基底コントローラークラス | `CMS_Controller_Action_Admin` で認証チェックを共通化 |
| SoC（関心の分離） | MVC分離 | コントローラー、モデル、ビューの分離 |
| Registry パターン | グローバル状態管理 | `Zend_Registry` による設定・DB接続の共有 |
| Factory パターン | ACL生成 | `CMS_Acl_Factory::createGlobalAcl()` |
| Front Controller パターン | リクエスト処理 | `Zend_Controller_Front` |
| Plugin パターン | 横断的関心事 | `Zend_Controller_Plugin_Abstract` を継承した Initializer |

### コーディング規約

- **命名規則:**
  - クラス名: PascalCase（例: `ArticlesController`, `CMS_Acl_Factory`）
  - メソッド名: camelCase（例: `fetchArticles`, `preDispatch`）
  - テーブル名: snake_case（例: `articles_categories`, `users_profiles`）
  - カラム名: テーブル省略形_プロパティ名（例: `article_title`, `user_email`）

- **ファイル構成:**
  - 1ファイル1クラス
  - クラス名とファイル名の一致（例: `Articles.php` → `class Articles`）
  - PSR-0 に準拠したオートロード（Zend Framework 1.x 標準）

- **コメント:**
  - PHPDoc 形式のドキュメンテーションコメント
  - `@author`, `@version`, `@copyright` ヘッダー

## 備考

### 技術的負債・改善ポイント

1. **パスワードハッシュ**: MD5 を使用しているが、bcrypt 等のより安全なアルゴリズムへの移行を推奨
2. **トランザクション管理**: 複数テーブル更新時のトランザクション未実装
3. **フレームワーク**: Zend Framework 1.x はサポート終了。モダンフレームワークへの移行を検討
4. **Cookie セキュリティ**: Remember Me 機能でパスワードを平文で Cookie に保存している（セキュリティリスク）
5. **セッションハイジャック対策**: Flash アップローダー対応のセッション ID 引き渡し処理にセキュリティリスクあり

### 環境設定

- **開発環境**: `APPLICATION_ENV = "development"`
- **本番環境**: `APPLICATION_ENV = "production"`

環境変数は Apache の `SetEnv` ディレクティブで設定する。
