注意
この記事の内容は、開発途中のものです。
今後、変更などを行う可能性が高いのでご注意ください。

はじめに

車両の燃費、整備履歴、経費などを一か所で管理したいと思い、 Webアプリケーション「vehicle-management」を開発しています。

個人利用を前提とした小規模なアプリですが、Cloudflare Workers + D1を使うことで、 サーバーレスかつ低コストで運用できる構成にしています。

この記事では、技術スタックや主な機能、設計上の工夫について紹介します。

技術スタック

項目技術
ランタイムCloudflare Workers
フレームワークHono 4
データベースCloudflare D1(SQLite)
ストレージCloudflare R2
バリデーションZod
言語TypeScript(strict mode)
認証Cloudflare Access
フロントエンドAdminLTE 3 + Bootstrap 4 + jQuery
グラフChart.js 4
PWAService Worker
テストVitest + Playwright
CI/CDGitHub Actions

バックエンドにはHonoを採用しました。Cloudflare Workersとの親和性が高く、 軽量ながらミドルウェアやルーティングの機能が充実しています。

データベースにはCloudflare D1(SQLiteベース)を使っています。

SQLiteの手軽さとCloudflareのエッジ配信を組み合わせることで、個人利用の規模では十分な性能が得られています。

主な機能

車両管理

複数の車両を登録・管理できます。車検証のQRコードをスキャンして登録情報を読み取る機能も実装しました。

給油記録

給油日、走行距離、給油量、単価を記録し、燃費を自動計算します。

小数点以下の走行距離にも対応しており、過去のデータも含めて燃費の推移をグラフで確認できます。

整備記録

整備日、走行距離、費用、店舗情報を記録します。

タグベースのカテゴリ分類(オイル交換、タイヤローテーションなど)に対応しており、 デフォルトで20種類以上のタグを用意しています。

整備間隔のリマインダー機能も実装しました。

月数または走行距離で間隔を設定でき、車両ごとに個別のスケジュールを上書きすることも可能です。

書類管理

車検証、保険証書などの書類をアップロードして管理できます。

PDF、画像(JPG、PNG)、Wordファイル(DOCX)に対応しており、ファイルはCloudflare R2に保存されます。

有効期限を設定すると、期限切れが近い書類のアラートを表示します。

経費管理

ユーザー定義のカテゴリで経費を分類できます。

カテゴリごとに色を設定でき、車両別・カテゴリ別の費用集計が可能です。

ダッシュボード・レポート

ダッシュボードでは、登録車両数、月間費用、燃費のサマリーを表示しています。

レポート機能では、次のグラフを提供しています。

  • 燃費推移グラフ(折れ線)
  • 費用比較チャート(ドーナツ)
  • 月間総費用の推移(積み上げ棒グラフ)

データはCSV形式でエクスポートも可能です。

バックアップ・リストア

データベース全体をJSON形式でエクスポート・インポートできます。

バックアップの履歴はローカルに保持され、復元時にはユーザー検証を行います。

アーキテクチャ

レイヤー構成

アプリケーションは、次の4層構成で設計しています。

Routes → Services → Validators → Repositories → D1 Database
  • Routes: HTTPリクエストのルーティングとレスポンスの整形
  • Services: ビジネスロジックの実装
  • Validators: Zodによる入力バリデーション
  • Repositories: D1データベースへのアクセス(クエリの組み立て)

依存関係の注入にはファクトリパターンを使っています。

createServices()関数でサービス群を生成し、ミドルウェア経由で各ルートに注入します。

エラーハンドリング

カスタムエラークラスを定義し、エラーの種類に応じたHTTPステータスコードと JSON形式のレスポンスを返す設計にしています。

  • ValidationError: 入力値の検証エラー(400)
  • NotFoundError: リソースが見つからない(404)
  • ConflictError: 競合(409)
  • ForbiddenError: アクセス権限なし(403)
  • AuthenticationError: 認証エラー(401)

楽観的ロック

同時更新による競合を防ぐため、楽観的ロック(version token)を採用しています。

更新時にバージョントークンを検証し、競合が発生した場合はConflictErrorを返します。

データベース設計

主要なテーブルは次のとおりです。

テーブル用途
usersユーザー情報
vehicles車両情報
fuel_records給油記録
maintenance_records整備記録
maintenance_tags整備カテゴリ(タグ)
maintenance_schedules車両別の整備間隔
expense_categories経費カテゴリ
vehicle_expenses経費記録
vehicle_documents書類メタデータ
vehicle_registrations車検証データ

設計上の特徴として、次の点を挙げます。

  • 論理削除: deletedフラグとdeleted_atタイムスタンプによる論理削除
  • 楽観的ロック: version_tokenカラムによる競合検出
  • 外部キー制約: データ整合性の維持
  • インデックス: 頻繁にクエリされるカラムへのインデックス設定

マイグレーションは11ファイルで管理しており、 D1のマイグレーション機能を使ってスキーマの変更を追跡しています。

認証

認証にはCloudflare Accessを利用しています。

Cloudflare Accessが発行するJWTを検証し、ユーザーのメールアドレスを取得します。

アプリケーション側では、初回アクセス時に利用規約の同意フローを表示し、 同意後にユーザーレコードを作成します。

フロントエンド

フロントエンドは、AdminLTE 3 + Bootstrap 4をベースにした従来型のHTMLページです。

APIとの通信はjQueryの$.ajax()で行い、グラフ描画にはChart.js 4を使っています。

PWA(Progressive Web App)にも対応しており、Service Workerによるオフラインサポートを提供しています。

テスト

テストは2種類を実装しています。

  • ユニットテスト(Vitest): バリデーター、サービス、リポジトリの単体テスト(13ファイル)
  • E2Eテスト(Playwright): 車両CRUD、給油記録、整備記録、ダッシュボードなど主要フローのテスト(8スペック)

カバレッジの目標として、バリデーター100%、サービス90%以上、リポジトリ80%以上を設定しています。

デプロイ

GitHub Actionsで自動デプロイを構成しています。

  • developブランチ: 開発環境(vms-dev.223n.tech)に自動デプロイ
  • masterブランチ: 本番環境(vms.223n.tech)に自動デプロイ

PRごとに型チェック、リント、フォーマット、ユニットテスト、ビルドを自動実行しています。

おわりに

Cloudflare Workers + D1 + R2の構成で、車両管理に必要な機能をひととおり実装しました。

Cloudflareのエコシステム内で完結しているため、インフラの管理コストが低く、 個人利用の範囲では無料枠内で運用できています。

Honoの軽量さとZodの型安全なバリデーションの組み合わせは、 Cloudflare Workers上のAPI開発に適しており、開発体験も良好でした。

今後は、整備スケジュールの通知機能や、車検証のOCR読み取りなど、 さらなる機能の追加を検討しています。

参考サイト