Newer
Older
client_dwatch / docs / architecture.md

ファイル・フォルダ構成と役割

実装を進めるときの地図。2026-09-01 時点の実態を記録し、未着手のものは「予定」として区別している。

構成そのものの規約は CLAUDE.md §9.1 が正。この文書は各ディレクトリが何を担うかと 依存の向きを補足するもの。両者が食い違ったら CLAUDE.md に従い、この文書を直す。


1. 依存の向き(最重要)

app/  (画面・APIルート)
  │  呼ぶ
  ▼
lib/db  lib/logger  lib/settings        ← 非純粋層(I/O・副作用あり)
  │  呼ぶ
  ▼
lib/judge  lib/config  lib/format       ← 純粋層(副作用なし・テストで固定)

下の層が上の層を import してはいけない。 特に:

  • lib/judge/ は lib/logger/ を import しない(CLAUDE.md §9.4 の純粋関数規約)
  • 判定結果のログは、判定関数の中ではなく呼び出し側(APIルート)で記録する
  • lib/judge/ は USE_FIXTURES の切替の外側。fixtures でも本番でも同じロジックが走る

2. ルート直下

パス 役割 状態
CLAUDE.md 全ルールの正。 仕様・禁止事項・実装規約。Claude Code と Cursor の両方が自動で読む 運用中
.cursorrules Cursor の legacy 形式。中身は CLAUDE.md へ統合済みのポインタ 縮小済み
d-watch-api.yaml API契約の正。 変更は実装より先に行う(CLAUDE.md §9) 一部に旧記述が残存※
d-watch-mock.jsx 画面レイアウト・文言・遷移の正。 フロント実装はこれを移植する 参照専用
instrumentation.ts 起動ログ(app.start)と捕捉漏れエラー(app.uncaught)の記録(D-047) 実装済み
proxy.ts 相関IDの発行+認証+IP制限。1プロジェクトに1ファイルしか置けないので、中身は lib/proxy/ へ分割し本体は順序制御だけにしてある 実装済み(指示書I / D-101)
next.config.ts serverExternalPackages: ["pino"] を設定 実装済み
eslint.config.mjs no-console を全体に適用。lib/logger/** のみ除外 実装済み
vitest.config.ts テスト設定。lib/** と app/** の *.test.ts を対象 運用中
.claude/launch.json 開発サーバーの起動設定。ブラウザ確認込みで npm run dev を回すために使う 運用中
prisma/schema.prisma db pull 実施後は生成物を正とし手で書き換えない(D-056、CLAUDE.md §7)。実施前は定義書ベースで手書き TeamWorks 領域

※ d-watch-api.yaml の info.description は却下済みの Laravel 構成(D-001)の記述を 現行構成(Next.js の API ルート、D-046)に書き直し済み。servers.url も /api/v1 という 実態と異なる記載になっていたため /api(app/api/clean-logs 等の実ルートと一致)に修正済み (D-050)。securitySchemes も cookie 方式(type: apiKey / in: cookie / name: dwatch_session)へ 書き換え済み(指示書I 段階0 / D-101)。bearerAuth と Laravel Sanctum への言及は残っていない。 401(未認証)/ 403(IP拒否)を components/responses に定義し、/auth/login は security: [] で 認証不要にしてある。


3. app/ — 画面とAPI

画面

パス 役割 状態
app/layout.tsx 全体のルートレイアウト 実装済み
app/(dashboard)/layout.tsx サイドバー付きの共通シェル 実装済み
app/(dashboard)/detail-log/ 清掃詳細ログ(F-02) 実装済み。他画面の見本
app/(dashboard)/dashboard/ ダッシュボード(F-01) 実装済み。 components/features/dashboard/DashboardScreen を描画する
app/(dashboard)/alerts/ アラート(F-03) 実装済み
app/(dashboard)/reports/ レポート(F-04) 実装済み(指示書F)
app/(dashboard)/inspections/ 点検記録(F-05) プレースホルダ。★デモまで一時的にメニューから隠している(D-079 項3)。D-031 のP1スコープは変えていない★
app/(dashboard)/ds-logs/ ds-alerts/ D-Security 系(F-06 / F-07) 実装済み
app/(dashboard)/facilities/ schedules/ system/ 施設・スケジュール・運用設定 実装済み(指示書L。systemは運用設定タブのみ・読み取り専用)
app/login/ 共有トークンの入力画面 実装済み(指示書I / D-101)。モックには無い画面(認証がモックの対象外だったため)

画面は (dashboard) ルートグループの中に置く。 ログイン画面だけはシェルを出さないため外に置く。

APIルート

パス 役割 状態
app/api/clean-logs/route.ts 一覧取得。「動く見本」。他の一覧APIはこれを真似る 実装済み
app/api/clean-logs/export/route.ts CSV等の書き出し 実装済み
app/api/clean-logs/[id]/advice/route.ts アドバイス取得 実装済み
app/api/clean-logs/unscheduled/[report_id]/advice/route.ts 予定なしの行のアドバイス取得 実装済み
app/api/clean-logs/[id]/areas/route.ts 清掃エリア内訳 実装済み
app/api/dashboard/route.ts ダッシュボードの集計取得(failure_breakdown は D-048 により未返却) 実装済み
app/api/settings/advice/route.ts アドバイス規則の設定 実装済み
app/api/auth/login logout cookie の発行・削除 実装済み(指示書I / D-101)。login のみ認証不要
app/api/prechecks/[id]/status 他 状態更新(PATCH系。M3) 予定

全APIルートの共通形:

export async function GET(request: Request) {
  return withRequestId(request, async () => {
    // ... 本体
    // catch 節では logApiError() を呼ぶ(console.error は禁止)
  });
}

.cursorrules にあった app/api/v1/ という記載は実態と異なる(v1 は無い)。 ログの route フィールドにも実際のパスを入れること。


4. lib/ — ロジック層

純粋層(副作用なし。テストで固定する)

パス 役割 注意
lib/judge/ 判定ロジック。 ◎○△×(grade.ts)、L1〜L3(escalation.ts)、アドバイス規則(advice.ts)、KPI計算(kpi.ts) 変更時は期待値表のテストを先に書く。 ロガーを import しない。DBアクセスを混ぜない
lib/dashboard/ ダッシュボード(F-01)の集計。 aggregate.ts。定義の正は D-049 lib/judge/kpi.ts とは別物。 kpi.ts は週次/月次の KPI 集計(完了率は1回ごとの完了率の単純平均。D-155)、こちらは日次の件数カウント。混同すると分母の意味が変わる。failure_breakdown は D-048 により未対応(M3送り)
lib/config/ 閾値の外出し。 完了率90/70/10、猶予30分、L2=2日/L3=3日、アドバイス規則の既定値 ハードコード禁止。 閾値は必ずここから読む
lib/format/ 表示整形(日付など)。丸めは表示層のみ ロジック層には生値を渡す
lib/types/ d-watch-api.yaml から起こした型。ErrorCode の union もここ YAML と乖離させない
lib/i18n.ts JP/EN 切替 器を壊さない
lib/theme.ts theme-status.ts ダーク/ライト切替 器を壊さない

非純粋層(I/O・副作用あり)

パス 役割 注意
lib/db/ USE_FIXTURES による切替の唯一の場所。 USE_FIXTURES を export しており、他所で process.env を再解釈しない 画面側は切替を意識しない
lib/fixtures/ サンプルデータ。境界値・異常系を含む DB接続後も削除しない(テスト・デモ用)。実在の店舗名・住所・電話番号を入れない。__tests__/schedules.test.ts が「有効スケジュールの曜日該当件数=当日の清掃ログ行数」という不変条件を守っている。この不変条件を壊すとダッシュボードの既定表示が異常系になる
lib/logger/ 動作ログ(D-047)。詳細は docs/logging-design.md lib/judge/ から import してはいけない
lib/settings/ 設定の永続化(アドバイス規則の上書き) メインDBには書かない
lib/clean-logs/ 清掃詳細ログのクエリ解析とフィルタ 画面固有のロジック
lib/auth/ 認証の純粋関数(token.ts 定数時間比較 / client-ip.ts XFF解決とIP許可判定 / cookie.ts cookie属性 / next-path.ts 戻り先の検証) 実装済み(指示書I / D-101)。DBもファイルも触らない
lib/proxy/ proxy.ts から呼ぶ横断処理(request-id.ts 相関ID発行 / authenticate.ts IP制限と認証の判定) 実装済み(指示書I / D-101)。判定だけを返し Response は組み立てない
lib/api/client.ts フロントからの fetch 集約点。 実装済み apiFetch() と ApiFetchError を提供し、F-01(DashboardScreen)・F-02(DetailLogScreen)の両方が利用している
lib/inspections/ 点検記録の独自ストア(SQLite + オブジェクトストレージ) 予定。 メインDBと完全分離

lib/logger/ の内訳

ファイル 役割
index.ts pino インスタンス。mixin() で request_id / data_source を自動付与。logEvent()
events.ts イベント型(allowlist)。 許可フィールドのみで構成し、禁止キーは型で弾く
redact.ts 実行時の禁止キー検査。個人情報13キー+認証情報8キー。大文字小文字を無視
context.ts AsyncLocalStorage。withRequestId() / normalizeRequestId()
timestamp.ts ISO 8601 JST(ミリ秒付き)。pino 既定の epoch ms を置き換える
api.ts logApiError()。クライアントに返す ApiError をそのまま受け取ることで、レスポンスとログのズレを構造的に防ぐ

5. components/

パス 役割
components/ui/ 共通部品。 Pill / GradeBadge / FilterButton / StatCard / ProgressBar(ProgressBar はF-04のKPI4列でも再利用予定)
components/features/{画面名}/ 画面固有のコンポーネント。 現在は detail-log/ と dashboard/ の2つ
components/layout/ AppShell(サイドバー)、PlaceholderScreen(未実装画面の仮表示)
components/theme/ ThemeProvider(ダーク/ライト)

判定結果(◎○△×、L1〜L3、ソート順)はAPIから受け取る。フロントで再計算しない。


6. docs/

ファイル 何の正か
spec.md 機能一覧と開発フェーズの正。 何をどの順で作るか
decisions.md 決定事項とその理由の正。 「なぜこの仕様か」を遡る時に読む
fixtures.md サンプルデータの正。 用意すべき境界値・異常系の一覧
logging-design.md ログ機構の実装の正。 マスキング規約・イベント定義・実装ステップ
architecture.md この文書。構成と役割の地図
getting-started.md 環境構築手順

7. 触ってはいけないもの

対象 理由
lib/judge/** とそのテスト 純粋関数を維持する。判定を変えるときは期待値表を先に直す
lib/dashboard/aggregate.ts とそのテスト lib/judge/** と同じ規律(D-041)で期待値表テストを先に直す。schedule_progress の定義は D-049 が正
lib/fixtures/schedules.ts の曜日設計 866fe4f で確定した曜日パターン(補完ペア+毎日)。__tests__/schedules.test.ts がこの不変条件を守っている。崩すと /dashboard の既定表示(当日・date省略)が異常系になる
prisma/schema.prisma db pull 実施後は手で書き換えない(D-056)。TeamWorks の領域
d-watch-api.yaml API契約の正。変更は実装より先に、合意の上で
CLAUDE.md の <!-- BEGIN:nextjs-agent-rules --> ブロック next dev が自動生成する
メインDBへの書き込み robot_error_reports / control_error_histories の4カラムのみ

8. 未確定(決まり次第この文書を更新する)

項目 影響範囲
★アプリのポートが nginx からのみ到達可能か★ ネットワーク側の設定。★偽装 XFF を本当に塞ぐのはここ★(アプリでは防げない)。D-101 の申し送り
VM のプロセス管理方式 ログの出力先とローテーション(アプリ側の実装には影響しない)
グループ・会社マスタの階層構造 施設・会社・グループの関係が未定。group_id/customer_id は現在API側で受け取るが無視している
PATCH /prechecks/{id}/status の memo と PrecheckItem.note 同一物か別物か契約上判断できない。PATCH実装時に解消する