外部ID:ビジネスの世界で使われる識別子の真実

多くのビジネスシステムにおいて、エンティティを識別するためのユニークなIDが存在します。これは、部品番号、納税者番号、SNSのハンドル名、URL、あるいはチケット追跡システムのID「FOOBAR-123」など、多岐にわたります。これらを「外部ID(External ID)」と呼びます。

外部IDの最も重要な特性は、その名の通り外部で利用されることです。メールで送られたり、紙に印刷されたり、電話で伝えられたりする可能性があります。

外部IDには主に3つの定義属性があります。

  • エンティティの一意な識別: 各外部IDには、「ある特定の時点において」正確に1つのエンティティが対応します。これは非常に重要です。
  • エンティティと外部IDの関係: 1つのエンティティが複数の外部IDを持つこともあれば、外部IDを持たないこともあります。例えば、1つの部品に複数の部品番号が割り当てられるケースや、パスポートを持たない子どもなどが考えられます。
  • 外部IDの変更可能性: 外部IDは時間とともに変化する可能性があります。SNSのハンドル名が変更されたり、パスポートが再発行されたりするケースが代表的です。

外部IDはビジネスロジックに深く根ざしており、システムが外部の世界とやり取りする上で不可欠な要素です。

この概念は、自分の開発する個人プロダクトでも常に意識しています。ユーザーが入力するメールアドレスやユーザー名も外部IDの一種と捉えられますし、これらが変更された際の挙動は非常に慎重に設計する必要があります。

例えば、Amazonで商品を販売する際、ベンダーが割り当てる部品番号とAmazonが割り当てるASIN(Amazon Standard Identification Number)のように、複数の外部IDが存在することもありますね。(出典)

アンカーID:データベース内部の信頼できる識別子

外部IDがビジネス要件に強く紐づく一方で、データベース内部でエンティティを確実に識別するためのIDが必要になります。これを「アンカーID(Anchor ID)」と呼びます。

アンカーIDは、データベースの各インスタンスを一意かつ曖昧さなく識別するために不可欠です。例えば、100冊の本を管理するデータベースがあったとして、ISBNがない本や、タイトルが同じ複数の本が存在する場合、ISBNやタイトルをアンカーIDとして使うことはできません。

項目 外部ID アンカーID
目的 ビジネスの世界でエンティティを識別する データベース内部でエンティティを一意に識別する
変更可能性 あり(時間とともに変化する可能性) なし(原則不変)
種類 文字列、数字、複合など多様 整数(シーケンシャルID)、UUIDなどが一般的
用途例 部品番号、納税者番号、URL、SNSハンドル名 データベースの主キー、内部的な一意識別子

アンカーIDは、外部IDの変動性や曖昧さから独立し、データベース内でエンティティの存在を安定的に指し示す役割を担います。

この問題に対する一般的な解決策は、1, 2, 3...と連番の整数を使用することです。この整数は、実際のデータベーステーブルの主キーとして利用されます。

このように、ビジネス的な意味合いから完全に切り離されたIDを主キーとして採用する考え方は、多くのシステムで非常に有効です。私が普段開発しているGCPのCloud Run上のサービスでも、BigQueryやCloud SQLにデータを格納する際には、自動採番の整数IDを主キーとして利用することがほとんどです。

プライマリキーと一意性制約の役割

データベースの物理レベルでは、プライマリキー(主キー)がアンカーIDの役割を担います。主キーは、テーブル内の各レコードを一意に識別するための列または列の集合であり、そのビジネス的な意味合いから完全に独立していることが理想的です。

例えば、シンプルなコンテンツ管理システム(CMS)を設計する際、pages テーブルに id という整数型の主キーを設けることができます。この id が、そのページのアンカーIDとなります。

CREATE TABLE pages (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    external_id TEXT UNIQUE,
    title TEXT NOT NULL,
    content TEXT
);

上記のように、external_id (例: URLの一部、ユーザー指定の識別子) は別の一意性制約(UNIQUE制約)として定義されます。これにより、外部IDの変更可能性に対応しつつ、データベース内部ではidによってページを一意に識別できます。

もしCMSで「500ページもの社内文書が消えた」といった悲劇(出典)が起こったとしても、内部的なアンカーIDでデータが管理されていれば、復旧の足がかりが残る可能性が高まります。主キーと外部IDをこのように分離することで、データの整合性と柔軟性を両立できると感じます。

データベースの主キーは、そのビジネス的な意味合いから「完全に乖離」しているべきであり、一意性制約によって外部IDの変化に対応することが重要です。

TypeScriptでデータベースのスキーマ定義を記述する際も、この外部IDとアンカーIDの区別は意識すべきポイントです。特にORM(Object-Relational Mapping)を利用する際には、モデルのプロパティと実際のデータベースの物理的なID設計をどうマッピングするかが重要になってきますね。Claude Codeにこの辺りのスキーマ設計を相談してみたら、良いアドバイスをもらえそうな気がします。

外部システムからのIDとアンカーIDの統合

外部システムから生成されたIDを扱う場合、そのIDをアンカーIDとして直接利用できるかどうかの判断は慎重に行う必要があります。外部IDが十分に信頼でき、変更される可能性が極めて低い、あるいは変更された場合の対応が明確である場合に限り、アンカーIDとして採用することも検討できます。例えば、多くのクラウドサービスが提供するリソースIDは、外部システムによって生成されますが、それ自体がシステム内で一意かつ不変であることが保証されているため、アンカーIDとして利用可能です。

しかし、一般的には、外部IDは参照用とし、内部で独自のアンカーIDを生成する方が堅牢な設計と言えるでしょう。このアプローチは、異なる外部システム間でデータ連携を行う際にも、柔軟な対応を可能にします。例えば、ユーザーを識別するために、自社システムで生成したUUIDをアンカーIDとし、外部の決済システムや認証システムが発行するユーザーIDをそれぞれ外部IDとして管理する、といった形です。

これにより、片方の外部システムIDが変更されたり、別のシステムに切り替えたりする際に、自社システムのコアなデータモデルに大きな影響を与えることなく対応できるようになります。この設計は、マイクロサービスアーキテクチャや分散システムにおいて特に重要となるのではないでしょうか。