アーキテクチャ/ Runtime topology & data flow
松葉 / Matsuba の実行時の全体像と、主要な業務シーケンスを図解します。Cloud Run(コンテナを HTTP リクエストごとに起動するサーバレス基盤)上に 4 つのサービスと 1 つの Jobs を分けてデプロイし、5 つの Cloud Storage バケットと Cloud Pub/Sub(Google Cloud の非同期メッセージング基盤)を組み合わせてイベント駆動で連携させています。
全体構成Runtime topology
ユーザのリクエストは Cloud Load Balancer(経路制御)と Cloud Armor(WAF と IP レート制限)を経由して Cloud Run サービスに届きます。データは Supabase Postgres(DB のみ利用)と Cloud Storage に保存し、サービス間の非同期連携は Cloud Pub/Sub で行います。すべて東京リージョン(asia-northeast1)に配置します。
読み方: Teal の実線は同期的な主経路、灰色の細線は非同期またはバッチ経由、点線はスケジュール起動を示します。docshare-web と docshare-api は常時稼働、docshare-sync は需要に応じて縮退します。
4 サービス + 1 ジョブの責務Service responsibilities
サービスはそれぞれ単一の関心事に集中させ、互いに HTTP / Pub/Sub / Eventarc を介して連携します。1 つのサービスの不具合が別のサービスに連鎖しにくい構成です。
| サービス | 採用技術 | 主な責務 | インスタンス |
|---|---|---|---|
docshare-web | Next.js 16 · React 19 · TypeScript | 画面表示と Server Components。proxy.ts(旧 middleware.ts)で Better Auth のセッション検証を行い、変更操作は API へ委譲します。 | 常時稼働 · 上限あり |
docshare-api | Hono 4 · Node.js 22 · Prisma 7 | REST と Hono RPC、Better Auth、署名 URL 発行、Cloud Tasks 経由の通知ワーカーを同居させた中核 API。 | 常時稼働 · 上限あり |
docshare-sync | Go 1.23+ · Pub/Sub Push | Drive 同期ワーカー。file.scan_clean イベントを購読し、Google Drive へ片方向で同期します。appProperties で突合します。 | 需要に応じて縮退 · 上限あり |
docshare-scanner | Go + ClamAV 1.4 | Eventarc で 未検査バケットの finalize 通知を受け、ClamAV でスキャンし、検査済みバケットまたは 隔離バケットへ書き換えます。 | 常時稼働 · 上限あり |
docshare-batch | Cloud Run Jobs · Cloud Scheduler | 定期バッチ 18 種を --args でジョブ種別に振り分け。retention 削除 / Drive 補修 / JWKS ローテ / EICAR テスト / admin メトリクス集計など。 | スケジューラ起動 |
分離デプロイの利点: サービスごとに独立してリリースできるため、ウイルススキャナの ClamAV シグネチャ更新と Web 画面の改修が衝突しません。docshare-batch も同一のコンテナイメージから生成するため、ライブラリのバージョン差は発生しません。
5 バケット構成Five-bucket storage
Cloud Storage は用途別に 5 つに分けます。アップロード直後のファイルと、ウイルススキャンを通過した利用可能ファイルを物理的に異なるバケットに置くことで、未スキャンファイルが業務動線に紛れ込むことを構造的に防ぎます。
| バケット | 用途 | ライフサイクル |
|---|---|---|
未検査バケット | UL 直送先。クライアントから Resumable Upload で書き込み、Eventarc 通知でスキャンへ。 | 一定期間で自動削除(スキャン完了済みのため不要) |
検査済みバケット | スキャン通過済みの利用可能ファイル。署名 URL は本バケットからのみ発行します。 | Object Versioning ON · noncurrent は一定期間で削除 |
隔離バケット | 感染検出・拒否ファイルの隔離。専用 SA でのみ取り出し可能。 | 一定期間で自動消去候補(手動承認制) |
ミラーバケット(ウイルス定義) | ClamAV シグネチャの社内ミラー。freshclam が 6 時間ごとに更新します。 | 一定の世代を保持、不正検出が続くと system_admin 通知 |
共有 ZIP バケット | 共有リンク・一括 DL の ZIP 一時配信用。受取人のアクセスごとに署名 URL を発行します。 | 一定期間で自動削除 |
主要シーケンスCore sequences
業務上もっとも頻度の高い「ファイルのアップロードからスキャン・Drive 同期までの 9 ステップ」と、長期運用を支える「保持期間による自動削除の 7 ステップ」を縦並びで図解します。
ポイント: ファイルが UI 上で利用可能になるのは ⑥ の scan_status=clean 反映後です。それ以前はアップロード者にしか「スキャン中」表示されません。Drive 同期は同期 ON の Box でのみ走り、失敗時は上限回数まで指数バックオフでリトライし、それでも失敗した場合は DLQ(Dead Letter Queue · 失敗メッセージの隔離先)に送られます。
2 段階削除の意図: ④ の論理削除と ⑦ の物理削除の間に 30 日のクッションを置き、当日通知メール(⑤)で利用者に気づきの機会を与えます。重要なファイルは「Hold(リーガルホールド)」フラグで自動削除の対象外にできます。Hold 中は本フローの抽出条件 hold = false でスキップされます。
信頼性パターンReliability patterns
イベント駆動のシステムで起こりがちな「DB 書き込みは成功したが Pub/Sub publish に失敗した」「同じイベントを 2 回処理してデータが二重になった」「リトライが終わらず復旧できない」を、3 つのパターンで構造的に防いでいます。
DB と Pub/Sub の整合性
UseCase で DB 書き込みと同時に outbox_events テーブルへ INSERT します。1 分間隔の outbox-dispatcher が未送信レコードを Pub/Sub に publish するため、「DB は更新できたが publish に失敗」の状態が発生しません。
冪等性で二重処理を防ぐ
各イベントには UUIDv7 形式の event_id を付与し、購読側は processed_events テーブルで重複を弾きます。同じイベントを 2 回処理しても結果が変わらないため、Pub/Sub の at-least-once 配送と整合します。
失敗を捕まえて運用に渡す
購読側で例外が起きると、Pub/Sub が指数バックオフで上限回数までリトライします。それでも失敗した場合は Dead Letter Queue に隔離され、/admin/sync 画面から可視化・手動再投入できます。失敗を見逃さない仕組みです。