コンテンツへスキップ
DedicatedPHP 接触

PHPアプリケーションで不確かなデータのレビューステータスを設計する方法

PHPで追跡可能な人手レビューを設計しましょう。受信データと判断を分離し、状態遷移を定義して、権限・同時更新・修正を管理します。

PHPのレビューフローにおける受信レコード、提案、人による判断を示す概念図

アプリケーションが曖昧なデータを受け取ったとき、「有効」か「無効」のどちらかに無理に分類すると、問題を解決するどころか覆い隠してしまうことがあります。フォーム、連携処理、自動システムが提示した結果などでは、レコードを安全に承認または却下するための文脈が不足している場合があります。そのようなケースでは、PHPにおける不確かなデータの人手レビューは、非公式なラベルや急ごしらえの受け皿ではなく、明示的なルールを持つドメインの状態として扱う必要があります。

設計では、何を受け取ったのか、何が提案されているのか、誰が判断すべきか、その後どうなるのかという4つの問いに答えなければなりません。これらの責務を分けることで、フローを運用しやすくなり、誤りを修正し、なぜ特定の方法でデータを処理したのかを説明しやすくなります。

状態を作る前に問題を分類する

状態を作る前に問題を分類する — guía visual de DedicatedPHP

先に進められないデータのすべてに、人手レビューが必要なわけではありません。少なくとも、次の状況を区別してください。

  • 無効: 解釈できない形式など、検証可能なルールに違反している状態です。入力元やプロダクトに応じて、却下するか修正を依頼します。
  • 不完全: 必要なフィールドが欠けている状態です。データを要求する、下書きを保持する、追加の情報源を待つなどの対応が考えられます。
  • 不確実: 妥当な解釈が複数ある、または証拠が不足していて自動判断できない状態です。担当者による評価が必要な場合があります。

レビューをあらゆるエラーの汎用的な行き先にしてはいけません。値が明確な制約に違反しているなら、レビューキューに送るだけでは余計な作業が増えます。一方、判断が文脈や裁量に左右される場合は、レビュー可能な状態にすることで、データが正しいとも誤りとも早まって断定せずに処理を止められます。

このフローはインポート時の隔離とも区別するとよいでしょう。隔離は通常、技術的またはセキュリティ上のチェックに失敗したなど、システムに取り込んではならない入力を切り離すためのものです。レビューはビジネス上の曖昧さを解消します。同じレコードが両方の仕組みを通ることはありますが、目的、権限、終了条件は相互に置き換えられません。

小さく明示的なライフサイクルを定義する

将来あり得るすべての可能性を列挙するのではなく、まずプロセスの記述に必要な状態を定義しましょう。基本的なライフサイクルは次のようになります。

  • pending_review: 判断待ち。
  • confirmed: 担当者が利用可能な値を承認した状態。
  • rejected: 担当者が処理すべきでないと判断した状態。
  • reopenedまたは新たなレビュー項目: 以前の判断が再び検討対象になった状態。

許可する状態遷移を定義してください。たとえば、保留中のケースは承認または却下でき、解決済みのケースは許可された条件下でのみ再開できます。任意のリクエストが状態をどの値にでも直接変更できるようにしてはいけません。状態遷移では、遷移元、遷移先、許可される実行者、関連する副作用を指定します。

判断理由と担当者の識別情報を保存します。データを却下する場合は、単なる真偽値ではなく、役立つ理由を記録してください。解釈を承認する場合は、承認された値と、該当する場合は判断の根拠となった証拠を保持します。アクションに応じて理由を必須にするか決め、自由記述のメモに不要な個人情報を含めないようにしてください。

入力、提案、確定値を分離する

堅牢な設計では、受信したデータを自動処理の解釈や担当者の修正で上書きしません。次の概念を別々に保ちます。

  • 受信データ: 元の入力とその出所。適切なアクセス制御と保持管理を行います。
  • 提案された解釈: ルール、連携処理、支援システムが生成した提案と、その評価に必要な情報。
  • 確定値: 許可された判断の後にアプリケーションが使用する値。

この分離により、提案が誤って確定情報として扱われるのを防ぎ、現在の値に至った経緯を再構成できます。すべてのフィールドを無差別に複製する必要はありません。データ保持と保護の方針に従い、監査やサポートに必要なデータを保持してください。提案にスコアや自動生成の説明が含まれる場合は、判断ではなく文脈情報として扱います。

通常のアプリケーション処理では変更しないイベントまたは履歴レコードとして、ケースID、アクション、変更前後の状態、実行者、日時、理由を記録します。キューを簡単に検索できるよう、モデル側に現在の状態を保持してもかまいません。修正は、履歴に紐づく新たな判断または再オープンとして表現します。以前の判断を削除すると、追跡可能性が失われます。

ドメイン層で判断を実装する

PHPでは、各コントローラーが同じルールを適用することに頼るのではなく、アプリケーションサービスまたはドメインエンティティに状態遷移をカプセル化します。概略例を示します。

final class ReviewDecisionService
{
    public function confirm(ReviewCase $case, User $actor, string $value, string $reason): void
    {
        if (!$actor->canReview($case)) {
            throw new DomainException('アクセスが許可されていません');
        }

        if (!$case->isPending()) {
            throw new DomainException('ケースは保留中ではありません');
        }

        $case->confirm($value, $actor->id(), $reason);
    }
}

このコード片はルールを置く場所を示すものであり、完全な実装ではありません。値の形式と範囲も検証し、実行者がそのケースに対する権限を持つことを確認してから判断を記録します。認証(ユーザーが誰か)と認可(そのレコードに対して何ができるか)を混同しないでください。ロールや職務分離がある場合は、それらの制約をプロダクトのルールとして明示します。

ケースの更新と履歴の書き込みに一貫性が必要な場合は、データベーストランザクション内で保存します。判断によって別のAPIの呼び出しなど外部への副作用が発生する場合、ローカルトランザクションがそのシステムもカバーすると想定してはいけません。アーキテクチャ上必要であれば、トランザクショナル・アウトボックスなどの信頼性の高い配信方式を使い、再試行に耐えられるようコンシューマーを設計してください。

判断に役立つキューを設計する

有用なキューは、文脈を隠すことなくケースの優先順位付けや検索を可能にします。状態、日付、種類、担当者など、適切なフィルターを設け、ケースの理解に必要な情報だけを表示します。提案がある場合は、それが提案であることを明確にしてください。出所、レビュー理由、利用可能なアクションを示し、ユーザーに実行権限のない操作は表示しないようにします。

担当者の割り当ては重複作業を減らせますが、権限管理や同時判断への保護の代わりにはなりません。ある担当者がケースを開いた後、回答を送信する前に別の担当者が解決することがあります。古い画面から最近の変更を上書きしないよう、楽観的同時実行制御を使います。保存時にバージョンまたはタイムスタンプを比較し、古い操作を拒否して、ユーザーにケースの再読み込みを促します。

プロセスで一時的な予約が必要なら、有効期限と期限切れ後の動作を定義します。恒久的な担当割り当てを、意図しないロックとして使ってはいけません。誰がケースをレビューしたかを説明できるよう、重要なアクションを記録し、運用上許容できる期間を超えて保留になっているレコードを検出できるようにします。

過去を消さずに修正・再オープンする

判断が誤っていることも、新しい証拠が届いて変更が必要になることもあります。誰が再オープンできるのか、どのような理由を求めるのか、再レビュー中に確定値を利用不可にするのかを定義してください。確定値が後続プロセスに及ぼす影響によって対応は異なります。新たな処理を止める必要がある場合はありますが、すでに発生した影響を自動的に取り消すべきとは限りません。

以前の判断を履歴に残し、新しいバージョンまたはレビューイベントを作成します。データの修正に依存情報の再計算が必要なら、その影響を明確に特定し、重複を避けるための冪等な手順を定めてください。状態の修正を、外部操作がすでに取り消されたかのように見せてはいけません。

ルール、権限、後続処理をテストする

ルール、権限、後続処理をテストする — guía visual de DedicatedPHP

テストではフォームが保存されることだけでなく、フロー全体とその境界を検証してください。最低限、次の項目を確認します。

  • 保留中のケースで、想定された状態遷移が許可され、それ以外は拒否されること。
  • 権限のない実行者が、判断、再オープン、制限対象データの参照を行えないこと。
  • 定義された方針に従って、各判断の実行者、理由、変更前後の値が保持されること。
  • 同時に行われた2つの変更によって、古い判断が新しい判断を上書きしないこと。
  • 履歴の記録に失敗したとき、追跡可能性のないまま状態だけが更新されないこと。
  • 後続処理を再試行しても、操作や通知が重複しないこと。

設計のチェックリストとして、不確実性の原因を特定できること、元データ・提案・確定値が区別されていること、各状態遷移に権限と理由が定められていること、キューに十分な文脈情報があること、再オープン時に履歴が保持され運用への影響が定義されていることを確認してください。これらのいずれかが非公式な慣習に依存しているなら、システムのルールがまだ不足しています。

これらのアイデアをあなたのプロジェクトに活用してみませんか?あなたのPHPプラットフォームについて話し合いましょう。
関連サービスを見る