Skip to content

refactor(audit): AuditLogPort.record を Object detail 化し監査 detail のシリアライズを集約(手組み JSON 全廃) #702

@win2cot

Description

@win2cot

背景

Issue #663 の代表フロー自動走査中、PATCH /api/tasks/{id} の 500 調査で発見。直接原因は UpdateTaskUseCase の手組み JSON が LocalDate を引用符なし出力したこと(応急修正 PR #703)。だが調査の結果、根本原因は AuditLogPort.recordString detail を要求し、全 12 呼び出しが detail を文字列手組みしていることと判明した。当初の 3 ファイルは LocalDate 等で顕在化したサブセットにすぎない。

根本原因(監査の全 12 呼び出しが手組み JSON)

caller 現在の detail 生成 リスク
UpdateTaskUseCase buildAuditDetail(ヘルパ) LocalDate で 500(顕在)
UpdateTenantUseCase buildDiffDetail(ヘルパ) フィールド追加で同 500(潜在)
ListTenantsUseCase buildListDetail(ヘルパ) 同設計
CreateTaskUseCase / DeleteTaskUseCase / GetTenantUseCase "{\"taskId\":" + id + "}" 連結手組み
AddStakeholderUseCase / RemoveStakeholderUseCase "{\"taskId\":...,\"userId\":...}" 連結手組み
ChangeVisibilityUseCase(×2) "{\"taskId\":...,\"from\":\"" + ... 文字列値の素連結=破壊リスク
UpdateTenantStatusUseCase "{\"tenantId\":...,\"newStatus\":\"" + ... 同上
GetPlatformMetricsUseCase "{}" 固定値
CrossTenantViolationAuditService detail 組み立て 同様

方針(2026-06-20 確定)

  1. AuditLogPort.record の signature を record(AuditEventType, @Nullable Long tenantId, @Nullable Long userId, @Nullable Object detail) に変更。
  2. シリアライズを audit.adapter.persistence.AuditLogPersistenceAdapter に集約。Spring 自動構成の ObjectMapper(JavaTimeModule 登録済・LocalDate"2026-08-01")を注入し writeValueAsStringJsonProcessingException は unchecked に包んで fail-closed(監査整合性優先)。null detail の扱い("{}" 既定)を定める。
  3. 全 12 呼び出しを構造体渡し(Map / 小型 record)に置換し、手組み JSON・buildAuditDetailbuildDiffDetailbuildListDetailtoJsonValueescapeJsonString を全削除。
  4. detail キーは後方互換を維持(taskId/tenantId/old/new 等)。diff 系(TASK_UPDATED/TENANT_UPDATED)は old/new を保つため小型 record AuditFieldChange(String field, @JsonProperty("old") @Nullable Object oldValue, @JsonProperty("new") @Nullable Object newValue) を usecase/audit 層に置いて詰め替える。domain の FieldChange は POJO のまま(設計規約 §1.2.3 遵守)。Map.of は null 値を弾くため不可
  5. 再発防止として、コーディング規約に「監査 detail は構造体(Map/record)で渡し、シリアライズは AuditLogPersistenceAdapter に集約する。usecase での JSON 手組み禁止」を 1 項追記(設計規約 §1.2.3 に整合、ADR は不要)。

やること

受入条件

  • 手組み JSON が全廃、シリアライズは AuditLogPersistenceAdapter の 1 か所
  • AuditLogPort.record の detail が Object
  • PATCH /api/tasks/{id} の期限日変更で audit_logs.detail が valid JSON、既存キー名維持
  • LocalDate 回帰テストが存在
  • コーディング規約に手組み JSON 禁止が明記
  • CI グリーン

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/backendJava / Spring バックエンド実装変更area/docsdocs/ 配下のドキュメント(設計書・規約・ADR 等)変更priority/p2Medium。完了が望ましいが柔軟に判断可task-type:implコード実装が主体のタスク

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions