# アーキテクチャ設計書

## 概要

本ドキュメントは、serve-d（D言語用Language Server Protocol実装）のアプリケーションアーキテクチャを記述する。serve-dは、エディタ/IDEとD言語ツールチェーン（DCD、DScanner、dfmt、DUB等）の間を仲介するLanguage Serverであり、LSP仕様に準拠したJSON-RPC通信を介してコード補完、静的解析、フォーマット、リファクタリング等の機能を提供する。

## システム全体構成

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

[アーキテクチャ構成図](./アーキテクチャ構成図.md) を参照。

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

| 外部システム | 連携方式 | 用途 |
| --- | --- | --- |
| エディタ/IDE（VSCode等） | LSP (JSON-RPC over stdin/stdout) | リクエスト/通知の送受信、診断結果の表示 |
| DCD (D Completion Daemon) | ライブラリ統合（サーバープロセス内包） | コード補完、定義ジャンプ、ホバー情報、参照検索 |
| DScanner | ライブラリ統合 | 静的コード解析、診断情報の生成 |
| dfmt | ライブラリ統合 | Dソースコードのフォーマット |
| DUB | ライブラリ統合 + プロセス連携 | プロジェクト構成管理、依存関係解決、ビルド診断 |
| DMD | コンパイラパス参照 | インポートパス検出、標準ライブラリパス解決 |
| ファイルシステム | 直接アクセス | ソースファイル読み取り、ワークスペース探索 |
| HTTP（オプション） | HTTP/HTTPS | DCD等の外部ツールダウンロード |

## レイヤー構成

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

serve-dは **レイヤードアーキテクチャ** と **コンポーネントベースアーキテクチャ** を組み合わせた構造を採用している。

- 上位レイヤーは LSP プロトコル処理とコマンドディスパッチを担当する
- 下位レイヤー（workspace-d）はD言語ツールとの統合をコンポーネントとして提供する
- workspace-dのバックエンドは、プラグイン的にコンポーネントを登録・管理するコンポーネントレジストリパターンを採用している
- ファイバー（協調スレッド）ベースの並行処理モデルにより、非同期リクエスト処理を実現している

### レイヤー定義

| レイヤー | 責務 | 主なコンポーネント |
| --- | --- | --- |
| Protocol | LSPプロトコル定義、JSON-RPCメッセージの型定義 | `served.lsp.protocol`, `served.lsp.jsonops`, `served.lsp.uri` |
| Transport | JSON-RPCメッセージの送受信、stdin/stdout通信管理 | `served.lsp.jsonrpc` (RPCProcessor), `served.lsp.filereader` |
| Server Base | LSPサーバーの基盤フレームワーク、リクエストルーティング、ファイバー管理 | `served.serverbase` (LanguageServerRouter), `served.utils.events` (EventProcessor), `served.utils.fibermanager` (FiberManager) |
| Extension / Commands | LSPメソッドの具体的な実装、D言語固有のロジック | `served.extension`, `served.commands.*`, `served.linters.*`, `served.workers.*` |
| Backend Adapter | workspace-dバックエンドへの遅延ロード付きアダプタ | `served.backend.lazy_workspaced` (LazyWorkspaceD) |
| Backend (workspace-d) | D言語ツールチェーンとの統合コンポーネント群 | `workspaced.backend` (WorkspaceD), `workspaced.api`, `workspaced.com.*` |

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

- **依存の方向**: 上位レイヤーは下位レイヤーに依存する（Protocol <- Transport <- Server Base <- Extension <- Backend Adapter <- Backend）
- **許可される参照**: 上位レイヤーから下位レイヤーへの参照のみ許可。Extension層はProtocol型定義とBackend APIの両方を参照可能
- **禁止される参照**: BackendレイヤーからExtensionレイヤーやTransportレイヤーへの直接参照は禁止。Backendはコールバック/イベント機構を通じて上位に通知する
- **サブパッケージ依存**: `protocol` -> `lsp` -> `serverbase` の順に依存。`workspace-d` は `dcd` サブパッケージに依存し、外部のD言語ツールライブラリ（dfmt, dscanner, dub, libdparse等）に依存する

## モジュール構成

### ドメイン分割

| ドメイン | 責務 | 関連モジュール |
| --- | --- | --- |
| LSPプロトコル | LSP仕様準拠の型定義とJSON操作 | `served.lsp.protocol`, `served.lsp.jsonops`, `served.lsp.uri`, `served.lsp.protoext` |
| JSON-RPC通信 | stdin/stdout経由のJSON-RPCメッセージ処理 | `served.lsp.jsonrpc`, `served.lsp.filereader` |
| ドキュメント管理 | テキストドキュメントのインメモリ管理と差分同期 | `served.lsp.textdocumentmanager` |
| サーバー基盤 | LSPサーバーのライフサイクル管理とイベント処理 | `served.serverbase`, `served.utils.events`, `served.utils.fibermanager` |
| コマンド実装 | 各LSPメソッドの具体的な処理ロジック | `served.commands.*`（calltips, complete, definition, format, rename等） |
| 静的解析（リンター） | DScanner, DUB, dfmtによる診断情報生成 | `served.linters.dscanner`, `served.linters.dub`, `served.linters.dfmt`, `served.linters.diagnosticmanager` |
| バックグラウンドワーカー | 非同期バックグラウンド処理 | `served.workers.profilegc`, `served.workers.rename_listener` |
| バックエンド統合 | workspace-dを介したD言語ツール統合 | `workspaced.backend`, `workspaced.api`, `workspaced.com.*` |
| 設定管理 | ユーザー設定とワークスペース設定の管理 | `served.types` (Configuration), `served.utils.serverconfig` |

### パッケージ構造

```
serve-d/
├── source/                          # メインパッケージ（serve-d本体）
│   ├── app.d                        # エントリーポイント、コマンドライン引数処理
│   ├── crash_handler.d              # クラッシュ時のバックトレースハンドラ
│   └── served/
│       ├── extension.d              # LSPメソッド実装の集約、初期化・設定変更処理
│       ├── types.d                  # 共通型定義、グローバル状態、設定構造体
│       ├── info.d                   # バージョン情報
│       ├── backend/
│       │   └── lazy_workspaced.d    # LazyWorkspaceD（遅延ロード付きバックエンド）
│       ├── commands/                # LSPメソッドハンドラ群
│       │   ├── calltips.d           # シグネチャヘルプ
│       │   ├── code_actions.d       # コードアクション
│       │   ├── code_lens.d          # コードレンズ
│       │   ├── color.d              # ドキュメントカラー
│       │   ├── complete.d           # 自動補完
│       │   ├── dcd_update.d         # DCDの更新処理
│       │   ├── definition.d         # 定義ジャンプ
│       │   ├── dub.d                # DUB関連コマンド
│       │   ├── file_search.d        # ファイル検索
│       │   ├── folding.d            # 折りたたみ範囲
│       │   ├── format.d             # フォーマット
│       │   ├── highlight.d          # ドキュメントハイライト
│       │   ├── index.d              # インデックス管理
│       │   ├── inlay_hints.d        # インレイヒント
│       │   ├── references.d         # 参照検索
│       │   ├── rename.d             # リネーム
│       │   ├── symbol_search.d      # シンボル検索
│       │   └── test_provider.d      # テストプロバイダ
│       ├── io/                      # I/Oユーティリティ
│       │   ├── git_build.d          # Gitベースビルド情報
│       │   ├── http_wrap.d          # HTTPダウンロードラッパー
│       │   └── nothrow_fs.d         # 例外なしファイル操作
│       ├── linters/                 # リンター統合
│       │   ├── dfmt.d               # dfmtリンター
│       │   ├── diagnosticmanager.d  # 診断情報の統合管理
│       │   ├── dscanner.d           # DScannerリンター
│       │   └── dub.d                # DUBビルドリンター
│       ├── lsp/
│       │   └── protoext.d           # LSPプロトコル拡張型定義
│       ├── utils/                   # ユーティリティ
│       │   ├── ddoc.d               # Ddocドキュメント処理
│       │   ├── diet.d               # Diet テンプレート補完
│       │   ├── progress.d           # 進捗報告
│       │   ├── stdlib_detect.d      # 標準ライブラリパス検出
│       │   ├── trace.d              # パフォーマンストレース
│       │   └── translate.d          # 多言語対応
│       └── workers/                 # バックグラウンドワーカー
│           ├── profilegc.d          # GCプロファイル
│           └── rename_listener.d    # リネームリスナー
├── protocol/                        # サブパッケージ: LSPプロトコル型定義
│   └── source/served/lsp/
│       ├── protocol.d               # LSP v3.16準拠の型定義
│       ├── jsonops.d                # JSON操作ユーティリティ
│       └── uri.d                    # URI変換ユーティリティ
├── lsp/                             # サブパッケージ: LSP通信層
│   └── source/served/lsp/
│       ├── jsonrpc.d                # JSON-RPCプロセッサ（RPCProcessor）
│       ├── filereader.d             # stdin読み取りスレッド
│       └── textdocumentmanager.d    # テキストドキュメントマネージャ
├── serverbase/                      # サブパッケージ: LSPサーバー基盤
│   └── source/served/
│       ├── serverbase.d             # LanguageServerRouterミックスインテンプレート
│       └── utils/
│           ├── async.d              # 非同期ユーティリティ
│           ├── events.d             # イベントプロセッサ（UDAベースディスパッチ）
│           ├── fibermanager.d       # ファイバーマネージャ
│           ├── memory.d             # メモリユーティリティ
│           └── serverconfig.d       # サーバー設定管理
├── http/                            # サブパッケージ: HTTP通信
│   └── source/served/http.d         # HTTPダウンロード機能
├── dcd/                             # サブパッケージ: DCD統合
│   └── source/served/dcd/
│       └── client.d                 # DCDクライアント
├── workspace-d/                     # サブパッケージ: バックエンド（D言語ツール統合）
│   └── source/workspaced/
│       ├── api.d                    # コンポーネントAPI定義
│       ├── backend.d                # WorkspaceD バックエンドコア
│       ├── coms.d                   # コンポーネント一括インポート
│       ├── com/                     # 個別コンポーネント
│       │   ├── dcd.d                # DCD統合コンポーネント
│       │   ├── dcdext.d             # DCD拡張コンポーネント
│       │   ├── dfmt.d               # dfmt統合コンポーネント
│       │   ├── dscanner.d           # DScanner統合コンポーネント
│       │   ├── dub.d                # DUB統合コンポーネント
│       │   ├── dmd.d                # DMD統合コンポーネント
│       │   ├── fsworkspace.d        # ファイルシステムワークスペース
│       │   ├── importer.d           # 自動インポート
│       │   ├── index.d              # インデックスコンポーネント
│       │   ├── moduleman.d          # モジュール管理
│       │   ├── references.d         # 参照管理
│       │   ├── snippets/            # スニペット管理
│       │   └── ...
│       ├── visitors/                # ASTビジタ
│       └── dub/                     # DUB診断
├── views/                           # テンプレートファイル（stringImportPaths）
├── test/                            # テストコード
└── ci/                              # CI/CDスクリプト
```

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

serve-dの主要コンポーネント間の依存関係は以下の通り。

1. **app.d** (エントリーポイント) -> `served.extension`, `served.types`, `served.lsp.jsonrpc`, `served.lsp.filereader`
2. **served.types** -> `served.serverbase` (LanguageServerRouter), `served.extension`, `served.backend.lazy_workspaced`
3. **served.extension** -> `served.commands.*`, `served.workers.*`, `workspaced.api`, `workspaced.coms`
4. **served.commands.*各モジュール** -> `served.extension`, `served.types`, `workspaced.api`, `workspaced.com.*`
5. **served.linters.*各モジュール** -> `served.linters.diagnosticmanager`, `served.types`, `workspaced.com.*`
6. **served.backend.lazy_workspaced** -> `workspaced.api`, `workspaced.backend`
7. **workspaced.backend** -> 外部ライブラリ(dcd, dfmt, dscanner, dub, libdparse)

## ミドルウェア構成

### データベース

serve-dはデータベースを使用しない。全てのデータはインメモリで管理される。

| 種類 | ミドルウェア | バージョン | 用途 |
| --- | --- | --- | --- |
| インメモリ | TextDocumentManager | - | 開いているドキュメントのインメモリ管理 |
| インメモリ | DiagnosticManager | - | 診断情報（3プロバイダ分）のインメモリ管理 |
| インメモリ | WorkspaceD Backend | - | ワークスペースインスタンスとコンポーネント状態管理 |

### キャッシュ

| ミドルウェア | バージョン | 用途 | TTL |
| --- | --- | --- | --- |
| DCD補完キャッシュ | DCD 0.16.2相当 | シンボル情報のキャッシュ | セッション中 |
| StringCache (libdparse) | libdparse 0.25.1 | パーサートークンのキャッシュ | インスタンスライフタイム |

### メッセージキュー

serve-dは明示的なメッセージキューミドルウェアを使用しない。JSON-RPCメッセージは `RPCProcessor` 内部の `DList!RequestMessageRaw` で管理される。

| ミドルウェア | バージョン | 用途 |
| --- | --- | --- |
| RPCProcessor内部メッセージキュー | - | 受信したJSON-RPCメッセージの順序管理 |

### 検索エンジン

該当なし。シンボル検索はDCD及びlibdparseを利用したインメモリ検索で実現。

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

| ミドルウェア | バージョン | 用途 |
| --- | --- | --- |
| DCD (D Completion Daemon) | 0.16.2 | コード補完、定義ジャンプ、シンボル検索 |
| DScanner | 0.16.0-beta.5 | 静的コード解析、診断生成 |
| dfmt | 0.15.2 | D言語ソースコードフォーマット |
| DUB | 1.40.0 | プロジェクト管理、依存関係解決、ビルド診断 |
| libdparse | 0.25.1 | D言語パーサー |
| libddoc | 0.8.0 | Ddocドキュメント処理 |
| mir-ion | 2.3.5 | JSON直列化/逆直列化 |
| mir-algorithm | 3.22.4 | アルゴリズムユーティリティ、SumType/Variant |
| requests | 2.2.0 | HTTPクライアント（ツールダウンロード） |
| sdlfmt | 0.1.1 | SDL形式設定ファイルのフォーマット |

## データフロー

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

1. エディタがJSON-RPCメッセージをstdinに書き込む（`Content-Length: N\r\n\r\n{JSON}`形式）
2. `FileReader`（別スレッド）がstdinからバイトストリームを読み取り、バッファに蓄積する
3. `RPCProcessor`（Fiber）がFileReaderからヘッダーとボディを読み取り、JSONをパースして `RequestMessageRaw` を生成し、内部メッセージキューに格納する
4. `LanguageServerRouter.runImpl` のメインループが `RPCProcessor.poll()` でメッセージを取得し、リクエストかノーティフィケーションかを判定する
5. リクエストの場合、新しいFiberとして `processRequest` が起動される
6. `EventProcessor` がUDAベースのメソッドディスパッチで `@protocolMethod` 付きのハンドラを検索・呼び出す
7. ハンドラ（`served.commands.*` のメソッド）がworkspace-dバックエンドを介してDCD/DScanner等を呼び出す
8. 結果が `ResponseMessageRaw` としてJSON直列化され、`RPCProcessor.send` を通じてstdoutに書き出される

### 非同期処理の流れ

1. ファイバーベースの協調マルチタスク: `FiberManager` が全てのファイバーを管理し、メインループから定期的に `call()` で各ファイバーを実行する
2. テキスト変更通知（`textDocument/didChange`）受信時、DScannerリントとdfmtリントが遅延タイマー付きで非同期にスケジュールされる（`setTimeout` でディレイを設定）
3. DCD更新やプロジェクト読み込みなどの長時間処理は `spawnFiber` で非同期ファイバーとして実行される
4. `LazyWorkspaceD` による遅延初期化: ワークスペースコンポーネントは初回アクセス時に実際にロードされる。`onLazyLoad` コールバックで必要時にのみコンポーネントを起動する
5. GCコレクションが定期的（デフォルト30秒ごと）にメインループから呼び出され、メモリを回収する

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

serve-dは永続化ストレージを持たない。全てのデータはプロセスのライフタイム中にインメモリで管理される。

- **ドキュメントデータ**: `TextDocumentManager` がエディタからの `didOpen`/`didChange`/`didClose` 通知に基づいてインメモリで同期管理する
- **診断データ**: `DiagnosticManager` が3つの診断プロバイダ（DScanner, DUB, dfmt）からの結果をマージし、`textDocument/publishDiagnostics` 通知としてエディタに送信する
- **設定データ**: ワークスペース設定はエディタからの設定変更通知で受信し、`Configuration` 構造体としてメモリ上に保持する
- **DCD関連データ**: DCDサーバーのインデックスデータはDCDプロセス内で管理される

## 横断的関心事

### 認証・認可

serve-dはローカルプロセスとして動作するため、認証・認可機構は不要。stdin/stdoutを通じた通信はエディタとの1対1接続に限定される。

| 方式 | 実装箇所 | 対象 |
| --- | --- | --- |
| なし（ローカルプロセス間通信） | - | 全てのリクエスト |

### ロギング・監査

| 種類 | 実装方式 | 保存先 |
| --- | --- | --- |
| アプリケーションログ | `std.experimental.logger` (FileLogger) | stderr（デフォルト）または `--logfile` 指定ファイル |
| LSPログメッセージ | `window/logMessage` LSP通知 | エディタのOutput Channel |
| パフォーマンストレース | `TraceData` (`served.utils.trace`) | `served_trace.log`（debug=PerfTraceLog有効時） |
| 進捗ログ | `reportProgress` (`served.utils.progress`) | LSP `window/logMessage` 通知 |

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

serve-dのエラーハンドリングは複数層にわたって実装されている。

| エラー種別 | ハンドリング方式 | レスポンス |
| --- | --- | --- |
| JSON-RPCパースエラー | `RPCProcessor.handleRequestImpl` でキャッチ | `ErrorCode.parseError` レスポンス |
| 未初期化状態でのリクエスト | `processRequest` で `serverInitializeCalled` チェック | `ErrorCode.serverNotInitialized` レスポンス |
| 未知のメソッド | `processRequest` でハンドラ数0チェック | `ErrorCode.methodNotFound` レスポンス |
| ハンドラ内例外 | `gotRequest` でException/Throwableをキャッチ | `ErrorCode.internalError` レスポンス + エラーメッセージ |
| ノーティフィケーション処理エラー | `gotNotify` でキャッチ、エラーログ出力 | `window/showMessage` でユーザーに通知 |
| コンポーネント初期化エラー | `onBindFail` コールバック | `window/showErrorMessage` でユーザーに通知 |
| DCDサーバークラッシュ | `MessageHandler.handleCrash` | 自動再起動（`spawnFiber` で `startDCDServer` 再呼び出し） |
| プロセスクラッシュ（SIGSEGV等） | `crash_handler` モジュール | バックトレースをstderrに出力後、プロセス終了 |

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

serve-dはデータベースを使用しないため、従来のトランザクション管理は不要。ただし以下の並行制御メカニズムが存在する。

| 範囲 | 管理方式 | 分離レベル |
| --- | --- | --- |
| ファイバー実行 | `FiberManager` + `Mutex` (fibersMutex) による排他制御 | 協調マルチタスク（1つのファイバーのみアクティブ） |
| RPCメッセージキュー | `DList` 内部キュー | FIFO順序保証 |
| 診断データ更新 | `DiagnosticManager` の3スロット独立管理 | プロバイダ別の独立更新 |

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

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

| 原則 | 適用箇所 | 実装例 |
| --- | --- | --- |
| UDAベースメソッドディスパッチ | `served.utils.events` | `@protocolMethod`, `@protocolNotification` UDAによるLSPメソッドの宣言的登録 |
| コンポーネントベースアーキテクチャ | `workspaced.backend` | `WorkspaceD` のコンポーネント登録/取得 API (`register!T`, `get!T`, `has!T`) |
| 遅延初期化 (Lazy Loading) | `served.backend.lazy_workspaced` | `LazyWorkspaceD.LazyInstance` による初回アクセス時のコンポーネントロード |
| ミックスインテンプレートパターン | `served.serverbase` | `LanguageServerRouter` ミックスインテンプレートによるサーバー構築 |
| 協調マルチタスク（ファイバー） | `served.utils.fibermanager` | 全LSPリクエストをFiberとして実行し、I/O待機中はyieldする |
| プロバイダパターン | `served.linters.diagnosticmanager` | 3つの診断プロバイダ（DScanner=slot0, DUB=slot1, dfmt=slot2）の独立管理と結合 |
| 設定駆動型処理 | `served.types.Configuration` | `d.enableLinting`, `d.enableAutoComplete` 等のフラグによる機能の動的有効化/無効化 |
| イベント駆動アーキテクチャ | `served.utils.events` | `@onConfigChanged`, `@onAddedProject` 等のイベントフック |

### コーディング規約

- **プログラミング言語**: D言語
- **ビルドシステム**: DUB (dub.json/dub.sdl)
- **パッケージ構成**: DUBのサブパッケージ機能（`serve-d:protocol`, `serve-d:lsp`, `serve-d:serverbase`, `serve-d:workspace-d`, `serve-d:http`, `serve-d:dcd`）を利用したモジュール化
- **命名規則**: D言語標準のcamelCase。モジュール名はsnake_case
- **フォーマット**: dfmt（`//dfmt off`/`//dfmt on` でフォーマット制御）
- **ロギング**: `std.experimental.logger` 標準ロガーを使用
- **エラーハンドリング**: Exception/Throwableベースの例外処理。致命的エラーも可能な限りキャッチして継続実行を試みる
- **並行処理**: Fiberベースの協調マルチタスク。`@nogc` や `nothrow` 属性を適切に使用
- **テスト**: `unittest` ブロックによるソースコード内テスト。`silly` テストフレームワーク使用。MockRPCによるプロトコルレベルテスト
- **ライセンス**: MIT

## 備考

- serve-dはD言語エコシステムの主要なLanguage Server実装であり、VSCodeのcode-d拡張機能のバックエンドとして広く利用されている
- 標準出力(stdout)はRPC通信に占有されるため、起動時にstdoutをstderrにリダイレクトし、通常の`writeln`呼び出しがRPC出力を汚染しないようにしている（`served.serverbase` の共有静的コンストラクタで実装）
- プロセス終了時、POSIXシステムではGCクリーンアップのセグメンテーションフォルトを回避するため、`exit()` で即時終了する
- workspace-dコンポーネントのバージョンは `dub.selections.json` で固定されている
