企業向けのインポートでは、1行の不備を理由にバッチ全体を中止するか、疑わしいデータを取り込むかの二択を迫るべきではありません。PHPインポートにおけるデータ隔離は第三の選択肢を提供します。有効なレコードを受け入れ、対応が必要なレコードを分離し、安全に解決するための十分なコンテキストを保持します。
隔離は、単なるエラー用フォルダーでも、失敗した行を保存するテーブルでもありません。分類ルール、明示的な状態、管理された修正、そして処理を重複させない再試行を備えた運用フローです。設計にあたっては、まず業務上の「有効」の意味と、各ロールが実行できる操作を合意しておくとよいでしょう。
各行への対応を決める前にエラーを分類する

インポートでは通常、異なる種類のチェックが組み合わされます。これらを分けることで、結果を説明しやすくなり、レコードの処理を続行するか、拒否するか、人による確認を求めるかを判断できます。
- 構造の検証:形式と基本的な内容を確認します。列の有無、データ型、解釈可能な日付、必須フィールド、妥当な範囲などが対象です。読み取れないファイルはバッチ処理を妨げる可能性がありますが、1行だけの日付が無効でも、通常はバッチ全体を止めるべきではありません。
- 業務ルール:価格が負でないこと、顧客が有効であること、カテゴリが許可されていることなど、ドメインの条件を確認します。違反によっては拒否すべきですが、運用上の判断に委ねられるものもあります。
- 既存データとの競合:たとえば、外部IDが別のレコードにすでに紐付いている場合や、古いバージョンを基に更新しようとしている場合を検出します。ファイルを修正するだけでは解決できず、照合や確認が必要なこともあります。
エラーの種類ごとにポリシーを定めてください。任意フィールドが欠けている場合はデフォルト値を適用できるかもしれませんが、曖昧な識別情報を、任意のレコードを選ぶことで解決すべきではありません。寛容すぎるルールも、あらゆる不備を致命的エラーに分類することも避けてください。判断には、データを受け入れる影響とバッチを停止するコストを反映させる必要があります。
状態と遷移を明示的にモデル化する
空のフィールドやテキストメッセージから状況を推測するのではなく、運用上の意味を持つ状態を使います。初期モデルには、pending、accepted、rejected、needs_reviewを含められます。processingやresolvedなどの状態は、フローに実際の遷移がある場合にのみ追加してください。
各遷移で何ができるかを文書化します。たとえば、保留中の行を検証し、ルールを満たせば受け入れ、確認可能な競合があればレビュー待ちにします。修正した行は再検証できますが、受け入れ済みの行を新規データのように再処理すべきではありません。バッチの状態は別に記録してください。バッチ内に受け入れ済みの行と隔離中の行が混在することもあるため、単一の成功・失敗フラグより「一部完了」のほうが結果を適切に表します。
状態は検証可能な判断に対応させる必要があります。「拒否」は業務上の変更が適用されていないことを意味し、「レビューが必要」は人による判断を要することを示すべきです。既存データの上書きを許可する場合は、誰がどの条件で実行できるかを明確にしてください。
元データを保持し、各判断の理由を説明する
行の元の入力を保存し、正規化した値と検証結果は別に保持します。これにより、たとえば受信した日付とその解釈の違いを調査する際、変換後の値だけが唯一の証拠になることを防げます。
永続化するデータには、バッチID、行番号、ソース、ファイル参照、元の内容、状態、検出したエラー、作成日時と解決日時、担当者を含められます。理由は、安定したコードと読みやすいメッセージで記録してください。customer_id_ambiguousのようなコードがあれば、該当ケースの絞り込みや集計に役立ちます。メッセージでは確認すべきデータを説明します。分類ロジックを自由記述のテキストだけに依存させないでください。
分析を再現するために必要なコンテキストも保持してください。使用したルールのバージョンまたはID、外部ID、競合に関係するデータなどが該当します。技術ログには、秘密情報や不要な個人データを保存しないでください。機微性と適用される義務に応じてアクセス制御と保持期間を定めます。ファイル全体に、行の解決には不要な情報が含まれる可能性がある場合は、その情報への露出を制限してください。
効果を重複させずに修正・再試行する
安全な再試行には、行そのものと、その処理を試みた回を区別することが重要です。たとえば、バッチIDと行インデックスの組み合わせや、検証済みの外部キーなどを使い、適用範囲内で各行に安定したIDを割り当てます。繰り返し実行されるインポートでは、同じ操作を識別できる冪等性キーも定義してください。同じファイルを再読み込みしたときに、データを更新するのか、無視するのか、新しいバージョンを作成するのかによって選択は異なります。
行の処理では、可能な限り業務データの書き込みと状態変更をアトミックに実行します。つまり、両方をまとめて確定するか、どちらも確定しないようにします。PHPのデータベーストランザクションは、同じ接続を使う変更を保護できますが、それだけで外部APIの呼び出しまでアトミックになるわけではありません。外部への効果には、冪等性キー、トランザクショナルなアウトボックステーブル、または対象ケースに合わせて設計した補償処理など、受信側のシステムと互換性のある方法を使ってください。
修正後は、該当する検証を再実行し、過去の履歴を保持します。元のエラーは削除せず、結果とともに新しい試行を追加してください。ルールや参照データが変わった場合は、適用したバージョンを記録し、受け入れ済みの判断が再実行によって黙って変わらないようにします。再試行の対象は選択したレコードだけとし、バッチ全体を無差別に再実行しないでください。
運用レビューと監査を考慮して設計する
レビュー画面は、技術的な例外を表示するだけでなく、判断を支援するものであるべきです。受信値、理由、影響を受けるフィールド、関連するコンテキストを表示し、安全な場合は修正案も提示してください。状態、バッチ、エラーの種類、経過時間で絞り込めるようにし、すでに効果が適用された行と、まだ適用されていない行を明確に示します。
誰がいつケースをレビューしたか、どの値を変更したか、どの判断を下したか、その理由を記録してください。担当者による修正と自動変換は区別します。責任範囲に応じて権限を設定してください。ファイルをインポートできる人が、競合を承認したり、受け入れ済みのレコードを変更したりできるとは限りません。影響の大きい変更には、追加の承認を検討してください。
警告なしに情報を上書きしやすくするツールは避けてください。修正を受け入れる前に、一意性、権限、レコードの現在の状態を再確認します。競合の検出後に別の人がデータを変更していた場合は、古い更新を適用するのではなく、その状況を示して解決を促してください。
部分的な障害と復旧をテストする

テストでは、ルールだけでなくフローの動作も確認する必要があります。有効行と無効行が混在するファイル、想定外の形式、競合、一時的なデータベースエラー、繰り返しの再試行を含めてください。合意したポリシーであれば、拒否された行がほかの行の受け入れを妨げないこと、トランザクション内の障害によって部分的な効果が残らないことを確認します。
- 同じ冪等性キーで行を再処理しても、レコードや外部アクションが重複しない。
- フィールドを修正すると、元データや履歴を消さずに再検証できる。
- すでに受け入れ済みの行が、別の行の再試行によって再適用されない。
- レビューから解決までの間に検出された競合が、黙って上書きされない。
- 機微なデータを不必要に露出することなく、エラーメッセージから必要な対応を判断できる。
本番環境では、隔離されたレコードの件数と経過時間、よくある理由、解決率、再試行の失敗を監視してください。継続的な増加は、ソースシステムの変更、古くなったルール、不明瞭な読み込み手順を示している可能性があります。隔離が機能しているのは、こうした原因を可視化し、管理された形で解決できる場合です。例外をいつまでもため込む保管場所になっていてはなりません。



