分析時点: 2026年1月 (冒頭の注記と「現状」の注記は 2026年9月に追記)
注意: 本書は Cloudflare Workers へ移行する前 (gRPC + PostgreSQL 構成) の分析です。 本文の指摘、ファイル名、行番号はすべて分析した時点のものです。現在の構成は architecture.md を参照してください。
移行 (#1640) で
stationapi/src/infrastructure/(sqlx のリポジトリ)、stationapi/src/presentation/(tonic)、stationapi/src/import.rs(PostgreSQL への取り込み) は削除され、StationRowもなくなりました。 そのため「SQL クエリの未最適化」「複雑な SQL クエリ」「デッドコード」 「gRPC コントローラーテスト」といった項目は、対象のコードごとなくなっています。 該当する項目には「現状」の注記を付けました。一方、domain / use_case 層への次の指摘は今も当てはまります。
Stationエンティティ (stationapi/src/domain/entity/station.rs) の フィールドが多い (現在 65 個)。Lineエンティティ (line.rs) も 34 個あり、StationとTrainTypeを埋め込んだままですStation/Line/TrainType/Companyの impl ブロックに#![allow(clippy::too_many_arguments)]が残っています- clone が多い (例:
query.rsのline.station = Some(station.clone()))- ハードコードされた値 (
normalize.rsの0x60/0xFEE0)FIXME付きのメソッド名get_by_line_group_id_vec_for_routes- 駅ナンバリングと路線記号を 1〜4 番まで手作業で並べるマッピング処理
StationAPI の技術負債を洗い出し、整理したドキュメントです。項目は優先度別に 分け、それぞれに該当するファイルと行番号を記載しています。
| 項目 | 内容 |
|---|---|
| 言語 | Rust (Edition 2021) |
| アーキテクチャ | クリーンアーキテクチャ (Domain/UseCase/Infrastructure/Presentation) ※分析時点 |
| 主要な依存関係 | tokio 1.28.0, sqlx 0.8.3, tonic 0.12.3 |
| コード規模 | 約 10,600 行 (Rust) |
| データ | 8 つの CSV ファイル (日本の鉄道データ) |
現状 (2026年9月): sqlx と tonic は依存から外れ、GraphQL は async-graphql 7、 実行環境は Cloudflare Workers (
worker0.8) です。レイヤー構成は Presentation / Model / UseCase / Domain / Index に変わりました (architecture.md)。
- ファイル:
stationapi/src/domain/entity/station.rs:8-76 - フィールド数: 64 個 (分析時点。現在の
domain/entity/station.rsでは 65 個。API が返すmodel.rsのStationとは別の型) - 問題点:
- 駅・路線・列車種別の情報が 1 つの構造体に混在している
Line、TrainType、StationNumberなどの関連データを抱え込んでいる- 責務の境界がはっきりしない
- 路線記号 (
symbol1-4) と、その色・形の組み合わせを手作業で管理している
pub struct Station {
// 駅情報 (station_cd, station_g_cd, station_name, ...)
// 路線情報 (line_cd, line, lines, line_name, line_symbol1, ...)
// 列車種別情報 (train_type, type_name, ...)
// 合計64フィールド (分析時点)
}- ファイル:
stationapi/src/domain/entity/line.rs:6-41 - フィールド数: 33 個 (分析時点。現在の
domain/entity/line.rsでは 34 個。API が返すmodel.rsのLineとは別の型) - 問題点:
Stationを埋め込んでいる (循環参照になるおそれがある)TrainTypeも埋め込んでいる- 路線記号が 4 つ (
line_symbol1-4) までしか持てず、拡張しにくい
- ファイル:
stationapi/src/infrastructure/station_repository.rs:19-79 - フィールド数: 58 個 (初版では 79 個と書いていましたが、79 は定義の最終行の 行番号でした)
- 問題点:
- 複数のテーブルを JOIN して大量のカラムを取得している
- Row 構造体から Entity への変換が複雑
現状 (2026年9月):
infrastructure/ごと削除され、StationRowも存在しません。
次の impl ブロックで #![allow(clippy::too_many_arguments)] を使っています。
| ファイル | 構造体 |
|---|---|
src/domain/entity/station.rs:79 |
Station |
src/domain/entity/line.rs:43 |
Line |
src/domain/entity/train_type.rs:25 |
TrainType |
src/domain/entity/company.rs:20 |
Company |
データベースから全件を取得した後、アプリケーション側のメモリ上で絞り込んでいる 箇所があります。
| ファイル | 行番号 | 内容 |
|---|---|---|
stationapi/src/use_case/interactor/query.rs |
604 | // TODO: SQLで同等の処理を行う - 経路の検証をアプリケーション側で実行 |
stationapi/src/use_case/interactor/query.rs |
702 | // TODO: SQLで同等の処理を行う - 経路の絞り込みをアプリケーション層で実行 |
// query.rs:604-610
// TODO: SQLで同等の処理を行う
let includes_requested_station = stops
.iter()
.any(|stop| stop.group_id == from_station_id || stop.group_id == to_station_id);影響: パフォーマンスが落ちる可能性があります。
現状 (2026年9月): 対象コードごと削除済みです。SQL はなくなり、検索はすべて インメモリ索引に対して行います。2 つの TODO コメントも残っていません。
ステータス: ✅ 対応済み (2026年1月)
次の最適化を行いました。
| 改善内容 | 詳細 |
|---|---|
| HashMap による検索 | O(n) の線形探索を O(1) の HashMap 検索に変更 (Company, TrainType, Station) |
build_route_tree_map の参照化 |
BTreeMap<i32, Vec<Station>> → BTreeMap<i32, Vec<&Station>> にして Station の clone を回避 |
train_types.clone() の削除 |
ベクター全体の clone をやめ、必要な要素だけを HashMap に格納 |
| バス停検索の最適化 | get_nearby_bus_lines を HashMap による検索に変更 |
次の clone() は、構造体のフィールドに所有権を移すために必要で、避けられません。
line.station = Some(station.clone())- Line 構造体がOption<Station>を所有しているline.company = ...- Line 構造体がOption<Company>を所有している- 絞り込んだ後に Vec を組み立てるときの
.cloned()
| ファイル | 行番号 | 問題 |
|---|---|---|
stationapi/src/domain/repository/line_repository.rs |
23 | // FIXME: もっとマシな命名 - get_by_line_group_id_vec_for_routes() |
命名の規則がはっきりせず、メソッドの意図が読み取りにくくなっています。
- ファイル:
stationapi/src/infrastructure/station_repository.rs:950-1088 - クエリの長さ: 140 行を超える多段の CTE (Common Table Expression)
問題点:
- 駅名検索が複数言語のフィールド (
LIKE $2-$6) に対応している - 同じような処理が複数のメソッドで繰り返されている
- クエリの設計意図が文書化されていない
繰り返されているクエリのパターン:
find_by_id(): 駅を 1 件取得するget_by_line_id(): 路線ごとに駅を取得するget_by_station_group_id(): 駅グループごとに駅を取得するget_route_stops(): 経路上の駅と停車条件を処理する
現状 (2026年9月): 対象コードごと削除済みです。
// stationapi/src/infrastructure/station_repository.rs:25
#[allow(dead_code)]
pub station_name_rn: Option<String>,現状 (2026年9月): 対象コードごと削除済みです。
#[allow(dead_code)]は リポジトリのどこにも残っていません。
| ファイル | 行番号 | 値 | 用途 |
|---|---|---|---|
stationapi/src/infrastructure/station_repository.rs |
1494 | "99991231" |
廃止駅の終了日付 |
stationapi/src/domain/normalize.rs |
8 | 0x60 |
ひらがな → カタカナ変換のコードポイント差 |
stationapi/src/domain/normalize.rs |
11, 14 | 0xFEE0 |
全角英数字 → 半角変換のコードポイント差 |
これらの値は定数として定義し、意味が分かるようにすべきです。
補足: station_repository.rs:1494 は #[cfg(test)] (1466 行目から) の中にある
テスト用データでした。
現状 (2026年9月):
station_repository.rsは削除済みです。normalize.rsの0x60/0xFEE0は同じ行に残っています。
- ファイル:
stationapi/src/use_case/interactor/query.rs:292-349
// 線号シンボル(1-4)を手動で配列に変換
let line_symbols_raw = [
&station.line_symbol1,
&station.line_symbol2,
&station.line_symbol3,
&station.line_symbol4,
];
let station_numbers_raw = [
station.station_number1.as_deref().unwrap_or_default(),
// ... (4つすべて手動で列挙)
];ステータス: ✅ 対応済み (2026年1月)
docs/architecture.md に次の内容をまとめました。
| 領域 | 対応状況 |
|---|---|
| アーキテクチャドキュメント | ✅ 4 層構造 (Domain/UseCase/Infrastructure/Presentation) の設計思想を文書化 |
| 命名規則 | ✅ Row 構造体と Entity の違いを明記 |
| キャッシュ戦略 | ✅ バッチクエリによる暗黙のキャッシュと、その設計判断を文書化 (query.rs:169-265) |
| データフロー | ✅ リクエストの流れとエラーの伝播経路を図示 |
| 領域 | 内容 |
|---|---|
| SQL の設計ドキュメント | 複雑なクエリの意図がインラインコメントにしか書かれていない |
現状 (2026年9月): architecture.md は移行後の構成に合わせて書き直しました。 SQL はなくなったため、SQL の設計ドキュメントという課題もなくなりました。
- テスト関数の数: 200 個
- テストの範囲: Repository 層が中心
| 領域 | 状態 |
|---|---|
| gRPC コントローラーのテスト | src/presentation/controller/grpc.rs (353 行) がテストされていない |
| End-to-End テスト | なし |
| パフォーマンステスト | なし |
現状 (2026年9月):
presentation/は削除済みです。
- unsafe コード: なし
- SQL インジェクション対策: sqlx のマクロ (
query_as!など) と、 プレースホルダへのバインドを使っている - 認証・認可: 初版では「gRPC レベルで実装あり」と書いていましたが、分析時点の
コード (
stationapi/src/) には認証・認可の処理は見当たりません
現状 (2026年9月): unsafe コードは今もありません。sqlx は依存から外れました。
- ファイル:
.github/workflows/ci.yml - 実行内容:
cargo check- コンパイルチェックcargo test- テストの実行cargo fmt --check- フォーマットの検証cargo clippy -- -D warnings- Lint (警告をエラーとして扱う)
現状 (2026年9月):
ci.ymlはネイティブの crate (stationapi、stationapi-preprocessor、data_validator) と wasm32 向けのstationapi-workerを分けてcargo check/cargo clippyしています。cargo testの対象はネイティブの 3 crate だけで、stationapi-workerの テストは含まれていません。
| パッケージ | バージョン | 状態 |
|---|---|---|
| tokio | 1.28.0 | 問題なし |
| sqlx | 0.8.3 | ほぼ最新 |
| tonic | 0.12.3 | ほぼ最新 |
| serde | 1.0.189 | 最新 |
現状 (2026年9月): sqlx と tonic は依存から外れました。tokio は
stationapiでmacrosフィーチャーだけを使い、ランタイムは持ち込んでいません。
- エラーハンドリングのテストが 17 個あります。
- SQL の最適化:
get_route_stopsでの絞り込みを SQL 側に移す (現状: SQL ごと削除済み) clone の削減: 参照ベースの処理を検討する✅ 対応済み- 命名の改善:
get_by_line_group_id_vec_for_routes()をより分かりやすい名前にする - 定数化: ハードコードされた値を定数として定義する
- Station 構造体のリファクタリング
StationCore(基本情報) とStationDetails(関連データ) に分割する
- DTO レイヤーの標準化
- コードの自動生成ツールを導入する
- Row → Entity → Protobuf の一貫性を保つ
- 現状: Row 構造体と Protobuf はなくなりました
- プレゼンテーション層のテスト
- gRPC コントローラーのテストを追加する
- 現状: gRPC コントローラーは削除済みです
- パフォーマンスの最適化
- クエリプランを見直す (現状: SQL ごと削除済み)
- キャッシュ戦略を導入する
- エラーハンドリングの統一
- domain、use_case、presentation の各層で方針を揃える
| 優先度 | 項目 | ファイル | 影響 | 現状 (2026年9月) |
|---|---|---|---|---|
| 高 | Station 構造体の設計見直し | src/domain/entity/station.rs |
保守性、パフォーマンス | 未対応 (65 フィールド) |
| 高 | SQL クエリの最適化 (TODO 対応) | src/use_case/interactor/query.rs:604,702 |
パフォーマンス | 対象コードごと削除済み |
| ✅ 対応済み (HashMap 検索、参照化) | メモリ効率 | - | ||
| ✅ 対応済み (docs/architecture.md) | オンボーディング、保守性 | - | ||
| 中 | Row 構造体のコード生成の検討 | src/infrastructure/*.rs |
保守性 | 対象コードごと削除済み |
| 中 | メソッド名の改善 | src/domain/repository/line_repository.rs:23 |
可読性 | 未対応 |
| 中 | ハードコード値の定数化 | 複数ファイル | 保守性 | normalize.rs の分が未対応 |
| 低 | UI レイヤーのテスト追加 | src/presentation/ |
テストカバレッジ | 対象コードごと削除済み |