マルチアカウント管理システムでは、最初に決めるべきなのは画面ではなく、デバイス層のフィールドとライフサイクルです。抽象化を誤ると、アカウント増加やデバイス種別の変更に伴って上位機能まで連鎖的な改修が必要になります。
マルチアカウント管理システムを開発するとき、多くの人は最初のバージョンを画面から考えます。アカウント一覧を作り、各アカウントにブラウザープロファイルを紐づけ、APIを呼んで操作するという形です。実際に運用が始まるまでは、それで十分に見えます。
最初の実装はたいてい非常に直接的です。アカウントAはプロファイル001、アカウントBはプロファイル002を参照し、アカウントCにはクラウドスマートフォンを紐づけます。問題は主に3か所で表面化します。運用担当から「そのマシンが故障したので、アカウントAをクラウドスマートフォンに移せないか」と言われても、DB変更と手作業が必要でリスクもある。新しいデバイス提供元を接続しようとすると、アカウントモジュールのリファクタリングが必要になる。同じアカウントを午前中はブラウザー環境、午後はクラウドスマートフォンで時間帯ごとに動かそうとしても、ほとんど実現できません。
この3つは別々の問題に見えますが、根本原因は1つです。アカウントというエンティティに、本来そこへ置くべきでない情報を詰め込んでいます。現在ログインしているデバイス、過去に使ったデバイス、フィンガープリント設定、出口アドレスをすべてアカウント側に保存しているため、デバイス変更がそのままアカウント変更になり、一か所の変更が全体へ波及します。
デバイスを独立したオブジェクトとして扱う
分離した後は、アカウントとデバイスを多対多の関係にします。アカウントにはフィンガープリントを保存せず、現在どのデバイスに紐づいているかだけを記録します。デバイス切り替えはアトミックな操作にし、過去の紐づけ履歴は別テーブルで保持します。各デバイスには一意の識別子を持たせ、外部からはその識別子だけを参照し、状態はリアルタイムに確認できるようにします。
これはモデルをきれいに見せるためではありません。運用側が変更したい内容と、開発側がコードを直す必要との距離を縮めるためです。その距離はデバイス数が増えるほど大きくなります。
抽象化レイヤーで明確にすべき4つのフィールド群
拡張に耐えられるデバイス抽象化が外部に示すべきことは、4つだけです。
- 環境ID:一意かつ安定していること。上位レイヤーはこのIDだけで環境を参照し、内部の番号、コンテナ名、プロセスIDは外に出さない
- 出口バインディング:その環境がどのネットワーク出口を使うか、対応するタイムゾーン、言語、DNSが一式として整合しているか。出口を独立して抽象化すれば、環境本体を変えずに出口だけ交換できる
- 状態:作成中、起動可能、実行中、タスク使用中、異常、回収待ち。状態モデルがなければ、プール管理や回収は成立しない
- ライフサイクル:作成、起動、占有、解放、回収を誰がトリガーするか。タスクがタイムアウトした場合にどうするか。環境異常時に誰が後処理するか
状態とライフサイクルは、しばしば1つのフィールドにまとめられます。最も手軽ですが、同時に最も高くつく方法です。状態は「今どうなっているか」を答え、ライフサイクルは「次に誰が操作できるか」を答えます。本番環境で本当に問題になるのは、ほぼ後者です。タスクが落ちても誰も環境を解放しない、あるいは環境がまだ動いているのに回収され、次回起動時になって出口が変わっていることに気づく、といった事態が起こります。

抽象化を誤ると、拡張時にコストを払うことになる
環境が10個のときは何も問題に見えないかもしれません。数十、数百になると、問題がまとめて噴き出します。
- 新しいデバイス種別を追加するたびにアカウントモジュールを変更する必要があり、回帰テストの範囲がデバイス層からアカウント層まで広がる
- デバイス変更にDB更新が必要なため運用担当が触れなくなり、次第に開発者しか保守できないシステムになる
- 状態や占有記録がないため、異常終了後に残った環境が回収されず、ゾンビ環境が増え続ける
- 上位層のタスク、コンテンツ、自動化がアカウントとデバイスの1対1関係を前提にしているため、前提を変えると全体を作り直すことになる
提供元が異なるデバイスは実装も大きく違います。ローカルのブラウザー環境とクラウドスマートフォンでは、使うインターフェースがまったく異なります。抽象化レイヤーの役割は、それらを同じインターフェース群の後ろに置くことです。新しいデバイス種別を追加するときは、起動・停止・状態取得を実装するアダプターを1つ追加するだけで済み、上位ロジックは変更しない形が理想です。抽象化が適切かを判断する簡単な方法もあります。デバイス種別を1つ追加するとき、変更するコードは1ファイルだけに収まるでしょうか。
第1段階では、できるだけ少なく作る
技術モデルが固まれば、画面設計も自然になります。メニューは設定、パラメータ、ログではなく、アカウント、タスク、デバイスといった業務オブジェクト単位で構成します。最も目立たせるべきなのは状態と異常です。利用者が探しているのはレコード件数ではなく、問題があるかどうかだからです。
機能面では、第1段階はデバイス一覧、デバイス追加、そして最小限のタスク完結フローだけで十分です。メニューは可能な限り減らし、まず1つのことを端から端まで動かし、不要なものは後回しにします。
環境分離をゼロから実装したくない場合は、既存の機能を利用する方法もあります。PurpleMarkは独立環境と出口バインディングを提供し、グループ単位の一括作成と状態照会に対応しています。上位レイヤーではテンプレート、占有、タスクスケジューリングだけを実装すればよく、開発リソースを業務ロジックに集中できます。
マルチアカウントシステムの複雑さは、アカウント数そのものではなく、デバイスのライフサイクル管理にあります。まずデバイスを、識別子、出口、状態、ライフサイクルを持つリソースとして扱えば、何かを追加するたびに上位機能を作り直す必要はありません。


