# アーキテクチャ設計書

## 概要

本ドキュメントは、clojure-lsp プロジェクトのアーキテクチャ設計を記述する。clojure-lsp は Language Server Protocol (LSP) を実装した Clojure/ClojureScript 向けの開発支援ツールであり、エディタに対して静的コード解析に基づく IDE 機能を提供する。

## システム全体構成

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

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

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

| 外部システム | 連携方式 | 用途 |
| --- | --- | --- |
| LSP クライアント（エディタ） | JSON-RPC over stdio | LSP プロトコルによる双方向通信（リクエスト/レスポンス/通知） |
| clj-kondo | ライブラリ連携 | 静的コード解析、lint、分析データ生成 |
| cljfmt | ライブラリ連携 | コードフォーマット |
| clj-depend | ライブラリ連携 | 依存関係解析 |
| Clojuredocs | HTTP API | ドキュメント参照 |
| ファイルシステム | ファイル I/O | ソースコード読み取り、キャッシュ永続化 |
| Babashka Pod | bencode プロトコル | Babashka からの API 呼び出し |

## レイヤー構成

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

**レイヤードアーキテクチャ + コンポーネントベース設計**

clojure-lsp はレイヤード構成を採用しており、以下の特徴を持つ:
- CLIモジュール（cli）とコアライブラリ（lib）の明確な分離
- プロトコルベースの依存性逆転（IProducer, ILogger）
- イミュータブルな状態管理（アトムによるデータベース管理）

### レイヤー定義

| レイヤー | 責務 | 主なコンポーネント |
| --- | --- | --- |
| Presentation / CLI | CLIエントリーポイント、LSPサーバー、JSON-RPC処理 | `clojure-lsp.main`, `clojure-lsp.server`, `clojure-lsp.clojure-coercer` |
| Application / Handlers | LSPリクエストのハンドリング、機能オーケストレーション | `clojure-lsp.handlers`, `clojure-lsp.startup`, `clojure-lsp.internal-api` |
| Domain / Features | 個別機能の実装（補完、リファクタリング、診断等） | `clojure-lsp.feature.*`, `clojure-lsp.refactor.*` |
| Infrastructure | データ永続化、外部ライブラリ連携、クエリ実行 | `clojure-lsp.db`, `clojure-lsp.kondo`, `clojure-lsp.queries`, `clojure-lsp.classpath` |

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

- **上位レイヤー -> 下位レイヤー**: Presentation -> Application -> Domain -> Infrastructure
- **依存性逆転**: Infrastructure 層の一部はプロトコル（IProducer, ILogger）を介して抽象化
- **禁止される参照**: Infrastructure から Domain への直接参照（ただし queries は全レイヤーから参照可能）
- **共有ユーティリティ**: `clojure-lsp.shared`, `clojure-lsp.settings` は全レイヤーから参照可能

## モジュール構成

### ドメイン分割

| ドメイン | 責務 | 関連モジュール |
| --- | --- | --- |
| Code Analysis | コード解析、シンボル解決、依存グラフ管理 | `kondo`, `queries`, `dep-graph`, `parser` |
| Code Intelligence | 定義へのジャンプ、参照検索、補完 | `feature.completion`, `feature.hover`, `feature.call-hierarchy` |
| Code Actions | リファクタリング、クイックフィックス | `feature.code-actions`, `refactor.transform`, `feature.rename` |
| Diagnostics | 診断、lint、カスタムリンター | `feature.diagnostics`, `feature.diagnostics.built-in`, `feature.diagnostics.custom` |
| Document Management | ドキュメント同期、ファイル管理 | `feature.file-management`, `db` |
| Formatting | コードフォーマット、ns整理 | `feature.format`, `feature.clean-ns` |
| Java Interop | Java連携、JAR内ソース参照 | `feature.java-interop`, `feature.stubs` |

### パッケージ構造

```
clojure-lsp-master/
├── cli/                              # CLI モジュール
│   ├── src/clojure_lsp/
│   │   ├── main.clj                  # エントリーポイント
│   │   ├── server.clj                # LSP サーバー実装
│   │   ├── nrepl.clj                 # 開発用 nREPL サーバー
│   │   └── clojure_coercer.clj       # 型変換
│   └── deps.edn
├── lib/                              # コアライブラリ
│   ├── src/
│   │   ├── clojure_lsp/
│   │   │   ├── api.clj               # 公開 API
│   │   │   ├── internal_api.clj      # 内部 API
│   │   │   ├── handlers.clj          # LSP ハンドラー
│   │   │   ├── startup.clj           # 初期化処理
│   │   │   ├── db.clj                # データベース管理
│   │   │   ├── queries.clj           # クエリ実行
│   │   │   ├── kondo.clj             # clj-kondo 連携
│   │   │   ├── producer.clj          # メッセージ送信インターフェース
│   │   │   ├── logger.clj            # ロギングインターフェース
│   │   │   ├── settings.clj          # 設定管理
│   │   │   ├── shared.clj            # 共有ユーティリティ
│   │   │   ├── config.clj            # 設定ファイル解析
│   │   │   ├── classpath.clj         # クラスパス解析
│   │   │   ├── dep_graph.clj         # 依存グラフ
│   │   │   ├── parser.clj            # コードパーサー
│   │   │   ├── feature/              # 機能モジュール群
│   │   │   │   ├── completion.clj
│   │   │   │   ├── hover.clj
│   │   │   │   ├── rename.clj
│   │   │   │   ├── code_actions.clj
│   │   │   │   ├── diagnostics.clj
│   │   │   │   ├── format.clj
│   │   │   │   └── ...
│   │   │   └── refactor/             # リファクタリング
│   │   │       ├── transform.clj
│   │   │       └── edit.clj
│   │   └── pod/clojure_lsp/          # Babashka Pod API
│   │       └── api.clj
│   └── deps.edn
└── test-helper/                      # テストヘルパー
```

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

**コアコンポーネント間の依存関係**:

1. **server.clj** -> handlers.clj, producer.clj, settings.clj
2. **handlers.clj** -> feature.*, queries.clj, startup.clj
3. **feature.*** -> queries.clj, refactor.*, kondo.clj
4. **queries.clj** -> dep-graph.clj, shared.clj
5. **kondo.clj** -> dep-graph.clj, config.clj, settings.clj
6. **db.clj** -> config.clj, shared.clj

## ミドルウェア構成

### データベース

| 種類 | ミドルウェア | バージョン | 用途 |
| --- | --- | --- | --- |
| インメモリDB | Clojure Atom | - | ランタイム状態管理（analysis, documents, dep-graph, diagnostics） |
| ファイルキャッシュ | Transit JSON | - | 分析結果の永続化（.lsp/.cache/db.transit.json） |

### キャッシュ

| ミドルウェア | バージョン | 用途 | TTL |
| --- | --- | --- | --- |
| core.memoize | - | 設定の一時キャッシュ | 1000ms |
| Transit JSON ファイル | - | 分析結果キャッシュ | 永続（ハッシュ変更まで） |
| clj-kondo キャッシュ | - | 静的解析結果キャッシュ | 永続 |

### メッセージキュー

| ミドルウェア | バージョン | 用途 |
| --- | --- | --- |
| core.async channels | 1.8.741 | 非同期イベント処理（diagnostics, changes, watched-files, edits） |

### 検索エンジン

該当なし（インメモリでのシーケンシャル/インデックスベース検索）

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

| ミドルウェア | バージョン | 用途 |
| --- | --- | --- |
| lsp4clj | 2.0.1 | LSP プロトコル実装 |
| clj-kondo | 2026.01.12 | 静的コード解析 |
| cljfmt | 0.15.6 | コードフォーマット |
| clj-depend | 0.11.1 | 依存関係解析 |
| rewrite-clj | 1.2.51 | Clojure コード操作 |
| babashka/fs | 0.5.30 | ファイルシステム操作 |
| timbre | 6.8.0 | ロギング |
| promesa | 11.0.678 | 非同期処理 |
| OpenTelemetry | 1.57.0 | 観測性（オプション） |

## データフロー

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

**LSP リクエスト処理（例: textDocument/definition）**

1. **JSON-RPC 受信**: stdio から JSON-RPC メッセージを受信
2. **デシリアライズ**: lsp4clj がメッセージをパース
3. **ルーティング**: `jsonrpc.server/receive-request` マルチメソッドでディスパッチ
4. **URI 正規化**: `normalize-doc-uri` で URI を正規化
5. **ハンドラー実行**: `handler/definition` が呼び出される
6. **クエリ実行**: `queries/find-definition-from-cursor` で分析データを検索
7. **レスポンス変換**: `conform-or-log` でレスポンス形式に変換
8. **非同期返却**: `eventually` マクロで非同期にレスポンスを返却

### 非同期処理の流れ

**ドキュメント変更の非同期処理**

1. **変更通知受信**: `did-change` で変更内容を受信
2. **チャネルへの送信**: `current-changes-chan` に変更を送信
3. **デバウンス処理**: 50ms のデバウンス後に処理開始
4. **再解析**: `f.file-management/analyze-changes` で clj-kondo 再実行
5. **DB 更新**: 分析結果を atom に反映
6. **診断送信**: `diagnostics-chan` 経由で診断を送信（25ms デバウンス）
7. **クライアント通知**: `publish-diagnostic` でエディタに通知

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

**キャッシュの永続化**

1. **初回分析完了**: プロジェクト分析完了時
2. **キャッシュ構築**: `build-db-cache` で必要なデータを抽出
3. **シリアライズ**: Transit JSON 形式でシリアライズ
4. **ファイル書き込み**: `.lsp/.cache/db.transit.json` に書き込み
5. **次回起動時読み込み**: `load-db-cache!` でキャッシュを読み込み
6. **ハッシュ検証**: project-hash, settings-hash, kondo-config-hash で有効性を検証

## 横断的関心事

### 認証・認可

| 方式 | 実装箇所 | 対象 |
| --- | --- | --- |
| 該当なし | - | clojure-lsp は認証機能を持たない |

### ロギング・監査

| 種類 | 実装方式 | 保存先 |
| --- | --- | --- |
| アプリケーションログ | timbre (ILogger プロトコル) | `.lsp/.cache/clojure-lsp.*.out` |
| トレースログ | JSON-RPC トレース | LSP トレースチャネル |
| OpenTelemetry | OTLP エクスポーター（オプション） | 設定された OTLP エンドポイント |

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

| エラー種別 | ハンドリング方式 | レスポンス |
| --- | --- | --- |
| LSP リクエストエラー | try-catch + エラーレスポンス | `{:error {:code :request-failed :message "..."}}` |
| 解析エラー | ログ出力 + 継続 | 部分的な結果を返却 |
| ファイルI/Oエラー | ログ出力 + nil 返却 | 機能縮退 |
| タイムアウト | 60秒タイムアウト | `{:error {:code :request-failed :message "Timed out..."}}` |
| キャンセル | リクエストキャンセルチェック | `{:error {:code :request-cancelled}}` |

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

| 範囲 | 管理方式 | 分離レベル |
| --- | --- | --- |
| DB 更新 | Clojure Atom (swap!) | CAS (Compare-And-Swap) による楽観的並行制御 |
| ワークスペース編集 | LSP workspace/applyEdit | クライアント側で管理 |

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

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

| 原則 | 適用箇所 | 実装例 |
| --- | --- | --- |
| 依存性逆転原則 (DIP) | Producer, Logger | `IProducer` プロトコルによるメッセージ送信の抽象化 |
| 単一責任原則 (SRP) | feature モジュール群 | 各機能ごとに独立した名前空間 |
| インターフェース分離原則 (ISP) | プロトコル定義 | 小さなプロトコル（IProducer: 8メソッド, ILogger: 4レベル） |
| イミュータブルデータ | 状態管理全般 | Atom 内のイミュータブルマップによる状態管理 |
| 関数合成 | データ変換パイプライン | transducer による効率的なデータ変換 |

### コーディング規約

- **名前空間**: `clojure-lsp.{レイヤー}.{機能}` 形式
- **プライベート関数**: `^:private` メタデータ、または `-` プレフィックス
- **リフレクション警告**: `(set! *warn-on-reflection* true)` を各 ns で有効化
- **ロギング**: `clojure-lsp.logger` マクロを使用（ファイル/行番号情報を保持）
- **設定アクセス**: `clojure-lsp.settings/get` を通じて統一的にアクセス
- **URI 処理**: `shared/normalize-uri-from-client` で正規化後に使用

## 備考

- clojure-lsp はマルチモジュール構成（cli, lib, test-helper）であり、lib は単独でライブラリとして利用可能
- GraalVM ネイティブイメージとしてもビルド可能（cli/graalvm）
- Babashka Pod としても動作可能（pod/clojure_lsp/api.clj）
