移行を「登録」と「付替え」に分ける一括処理の設計
組織再編に合わせた財産・備品の一括移行。新しい所属コードが登録作業に間に合わないという制約から、処理を「登録」と「付替え」の二工程に分けた設計を綴る。
問題設定:コードは、作業に間に合わない
ある団体の組織再編に合わせ、財産・備品を一括で登録・移転する機能を作った。課や係が新しく編成され、それに伴って何を持っているか、どこに帰属するかを整理し直す。対象は約千件規模。手作業で一件ずつ入力していては現場が回らないので、一括で処理する仕組みが要る。
作り始めてすぐ、素朴な想定が崩れた。「新しい所属で登録すればいい」——ところが、新しい所属コードの整備が、登録作業に間に合うとは限らないのだ。組織図は先に固まるが、それをシステム上のコードとして採番し、確定させる作業は別の工程で、しかも後ろにずれる。一方で現場は「今のうちに備品の棚卸しを済ませたい」と待っている。コードの確定を待って入力を始めるのでは、段取りが破綻する。
この「作業の前提が、作業中にまだ決まっていない」という現実の制約が、設計全体を規定した。
なぜ二工程に分けるのか
答えは、工程の分離だった。処理を一気通貫の「移行」として組まず、二つのフェーズに割る。
まず、現行の所属で「登録」する。今この備品が誰の管理下にあるかは、既に確定している事実だ。だからコード整備を待たず、現場は現在の所属を使って先に入力を済ませられる。次に、後日、新しい所属コードが固まってから「付替え(移転)」する。登録済みのデータに対して、帰属先だけを新コードへ移す。
ひとつに見える「移行」を、確定している情報だけで進められる部分(登録)と、まだ確定していない情報に依存する部分(付替え)に切り分ける。すると、前者を後者の完成を待たずに走らせられる。依存関係のない工程を先に流すという、ごく普通のスケジューリングの発想を、データ処理の設計に持ち込んだだけとも言える。だが「移行」という言葉が一枚岩に見えるせいで、この分割は案外気づきにくい。
実装の骨格:作業テーブルという下書き
では、この二工程をどう安全に実装するか。鍵は、本番テーブルへ直接書かないことだ。あいだに専用の作業テーブル(WORK)を一枚挟む。
作業テーブルの各行には、「新規/移転/廃棄」の区分、元データへの参照、検証エラーの記録、確定フラグ、そしてバッチIDを持たせる。画面が編集するのは、あくまでこの作業テーブルだ。本番はまだ一切動かない。いわば下書きである。ユーザーは区分を選び、対象を並べ、内容を組み立てる。この段階で矛盾があっても、それは下書き上の矛盾であって、本番のデータは無傷のまま保たれる。
確定は、次の順序で行う。
1. 対象バッチの全行を検証する
2. 1件でもNGがあれば、全体を中断(本番は一切変更しない)
3. 全件OKなら、トランザクション内で本番へ一括反映
4. 反映した作業行に確定フラグを立てる
ポイントは、部分的に適用しないこと。検証は全件まとめて行い、一件でも通らなければ何も書かない。半分だけ登録されて残り半分がエラーで止まる、という中途半端な状態を作らない。InnoDB を使っているのでトランザクションが効き、途中で失敗すれば自動的に巻き戻る。「全部成功か、全部なかったことにするか」——原子性を、DBの機能にそのまま担わせる。
取消も、同じ枠組みで実現できる。バッチID単位で本番を元へ戻し、作業行の確定フラグを下ろして復元する。やり直しが利く。人間が扱う一括処理では「間違えた、戻したい」が必ず起きるので、可逆であることは機能というより前提だ。
設計のポイント
振り返ると、効いたのは三つの原則だった。
ひとつ、本番を直接いじらず、作業領域で組み立てる。下書きと確定を分けることで、編集中の不完全な状態が本番へ漏れない。二つ、全件検証してから一括確定する。部分適用による中途半端な状態を作らない——原子性の確保だ。三つ、取消可能にする。バッチ単位で可逆にしておけば、現場は安心して先へ進める。
AIとの協働作業での学び
大量データの一括処理は、「下書き用の作業テーブル + 全件検証 + トランザクション確定 + 可逆な取消」という型に落とすと、たいてい安全に着地する。個別の要件が違っても、この骨格は流用が利く。
そしてもうひとつ。現実の制約——今回で言えば「コードが後から決まる」——は、たいてい設計を歪める邪魔者に見える。だが正面から受け止めて工程を分離できる形にしておくと、その制約こそが、段取りを破綻させないための分割線になる。制約に逆らって一気通貫を貫くより、制約に沿って工程を割るほうが、結局は速い。