
ドメイン分離は2つのフィールドで決まる(sys_domain)
ServiceNow には「ドメイン分離(Domain Separation)」という仕組みがあります。1つのインスタンスを論理的に区切り、複数の組織や顧客のデータ・処理・管理を混ざらないように分けます。MSP(マネージドサービスプロバイダ)が1インスタンスを複数顧客に貸す、グローバル企業が国・子会社ごとにデータを分ける、といった用途で使われます。
ただ、ドメイン分離は「機能を有効化すると魔法のように分離される」ものではありません。その土台は、ほぼ全テーブルにあらかじめ仕込まれた sys_domain(どのドメインに属すか)と sys_domain_path(階層上の位置)という2つのフィールドです。この記事では、ドメイン分離プラグインを有効化していない素の PDI(Zurich)を使い、「分離の器」がOOTBの時点でどこまで組み込まれているかを実測で読み解きます。
この記事は、ServiceNow の Incident や CMDB は触ったことがあるが、ドメイン分離がどのテーブルにどんなフィールドとして実装されているかをこれから理解したい人を想定しています。管理者・開発者が「このインスタンスはドメイン分離されているのか」「ドメインはどこに記録されるのか」を実機で確かめるときの入口として、フィールドの正体・継承・実レコードの値を扱います。
この記事で分かること
ドメイン分離の正体が、各テーブルの sys_domain / sys_domain_path という2フィールドであること
これらが通常の参照ではなく domain_id / domain_path という専用型で、OOTBの既定値が global であること
「分離の器」が、ドメイン分離を有効化していなくても1,700超のテーブルに既に組み込まれていること
sys_domain_path(ドメインパス)が階層を表し、前方一致で「自ドメイン+配下」を判定する設計であること
今回のPDIでは ドメイン分離プラグインが未有効で、全レコードが global(ルート)に属していること
実機で確認するときに見るべき一覧URL
この記事の内容は ServiceNow の PDI(Personal Developer Instance)Zurich(ズーリッヒ)リリースの OOTB(初期設定)で確認した個人メモです。この PDI ではドメイン分離プラグインが有効化されていません。本番環境やドメイン分離を有効化した環境では、ドメインの階層・件数・挙動が大きく異なります。
ドメイン分離とは
ドメイン分離は、データ・プロセス・管理タスクを「ドメイン」という論理的なグループに分け、1つのインスタンスの中に分離されたテナント環境を作る仕組みです。たとえば1つのServiceNowを、A社・B社という2つのドメインに分けるとします。A社の管理者にはA社のデータだけが見え、B社の管理者にはB社のデータだけが見える、という形にできます。
その分離を成り立たせている正体が、各レコードに付く2つのフィールドです。
sys_domain(Domain) … そのレコードがどのドメインに属するかを指す
sys_domain_path(Domain Path) … そのドメインが階層のどこにいるかを表す文字列
ServiceNow はこの2フィールドを使い、操作中の管理者が属するドメイン(とその配下)のレコードだけを自動的に絞り込みます。この器はプラットフォーム側にあらかじめ用意されており、ドメイン分離を有効化するかどうかは別の話です。今回のPDIは「器はあるが、分離は有効化していない」状態でした。

今回確認した PDI(Zurich / OOTB)では、sys_domain という物理テーブルは存在せず、ドメインの実体は global(ルート)ただ1つでした。にもかかわらず、後述するとおり sys_domain 列は1,700超のテーブルに組み込まれていました。
分離の器:sys_domain と sys_domain_path
ドメイン分離の中心にある2フィールドを、フィールド型の観点で見ます。これらは「ただの参照」ではなく、ドメイン分離専用に用意された特別な型です。

今回確認した PDI(Zurich / OOTB)では、ディクショナリ上のフィールド型は次のとおりでした。
sys_domain … 内部型 domain_id(Domain ID)。通常の Reference 型ではなく、ドメイン専用の型です。既定値(default)は global で、参照先テーブルを直接は持ちません(ドット指定の reference 列は空)。
sys_domain_path … 内部型 domain_path(Domain Path)。最大長 255 の文字列で、ドメインの階層上の位置を表します。
列名 sys_domain は Now プラットフォームで予約されています。テーブルに sys_domain という列を作ると、システムが自動的にこの domain_id 型と属性を割り当て、対応する sys_domain_path も自動生成します。この列は、開発者が普通の参照を作るのとは違う、プラットフォーム標準の仕掛けです。
フィールド定義は、次のURLで確認できます(先頭の <YourInstance> は自分のインスタンスに置き換えてください)。
https://<YourInstance>/sys_dictionary_list.do?sysparm_query=internal_type=domain_id^ORDERBYname
参考: What is domain separation?(Zurich)。ドメイン分離がデータ・プロセス・管理タスクをドメイン単位で分ける機能であることが説明されています。
器はOOTBで既に1,700超のテーブルに組み込まれている
ドメイン分離を有効化していなくても、「いつ有効化されてもいいように」多数のテーブルにあらかじめ sys_domain / sys_domain_path 列が用意されています。今回のPDIで件数を数えました。

今回確認した PDI(Zurich / OOTB)の件数は次のとおりでした。
sys_domain 列のディクショナリ定義 … 1,753 件
sys_domain_path 列のディクショナリ定義 … 1,494 件
Domain ID 型のフィールド総数 … 1,758 件
Domain Path 型のフィールド総数 … 1,493 件
参考:テーブル定義の総数(sys_db_object)… 6,193 件
全テーブルの約3割にあたる1,700超のテーブルに、ドメインの器が最初から組み込まれている計算です。Incident(task)、CMDB、ユーザーなど主要な業務テーブルはほぼこの中に含まれます。分離されているかどうかと、分離の器があるかどうかは別で、器の方はOOTBで広く準備済みでした。
なお、sys_domain という名前の物理テーブルは存在しませんでした。sys_domain をテーブルとして読もうとすると Invalid table となり、テーブル定義(sys_db_object)にも該当はありません。ドメインの実体は、ドメイン分離が未有効のあいだは global ただ1つで、内部的には別テーブル(後述)を間借りしています。
一覧は次のURLで確認できます。
https://<YourInstance>/sys_dictionary_list.do?sysparm_query=internal_type=domain_path^ORDERBYname
参考: Segregating and securing data with domain separation(Zurich)。ドメイン分離でデータを分離・保護する考え方が説明されています。
器は「定義したテーブル」から子テーブルに継承される
sys_domain 列は、すべてのテーブルが個別に持っているわけではありません。基底テーブルが定義し、それを継承する子テーブルが共有します。Incident を例に確認しました。

今回確認した PDI(Zurich / OOTB)では、次の構造でした。
task テーブルが sys_domain / sys_domain_path を定義していました(ディクショナリに行があり、既定値は global)。
incident には sys_domain の固有定義がありません(name=incident^element=sys_domain の行は0件)。それでも incident レコードを読むと sys_domain が返るのは、task の定義を incident が継承しているからです。
一方 sys_user は自前で sys_domain を定義していました(task 系とは別系統)。
あるテーブルにドメイン列があるかを調べるときは、そのテーブル自身だけでなく親テーブルまで遡って見ます。Incident のように親(task)で定義されている場合、子のディクショナリだけ見ても見つかりません。この遡り方は、ドメインに限らず ServiceNow のテーブル継承全般に通じます。
実レコードの値:OOTBは全件 global(ルート)
実際のレコードに何が入っているか、Incident・CMDB・ユーザーのレコードを読み、sys_domain と sys_domain_path の値を確認しました。

今回確認した PDI(Zurich / OOTB)では、調べたレコードはすべて global(ルート)に属していました。
sys_domain の値は リテラル global(表示も global)。
その参照リンクは sys_user_group/global に解決されました。ドメイン分離が未有効のあいだ、ドメインの実体は sys_user_group テーブルを間借りした擬似的なもので、global という特別な sys_id を指します。
sys_domain_path は /(ルート)。task 配下(incident)や cmdb_ci は /、sys_user は空でしたが、いずれも上位ドメインを持たない=ルートという意味では同じです。
OOTBでは全レコードが1つの global ドメインに同居しており、分離は発生していません。これはドメイン分離を有効化していないからであって、欠陥ではありません。素のインスタンスは、器だけ持って中身は単一ドメインで動いている、と読むのが正確です。
実際に確認したレコード例
具体的には、incident(INC0000060 / INC0009002)は sys_domain=global(→ sys_user_group/global)でした。sys_domain_path は / です。cmdb_ci(MySQL FLX 他)も同じく sys_domain=global・sys_domain_path=/ でした。sys_user(admin)は sys_domain=global ですが、sys_domain_path は空という結果でした。
レコードのドメイン値は、リスト表示で列を追加すれば次のURLで確認できます。
https://<YourInstance>/incident_list.do
(リストの歯車から Domain / Domain Path 列を表示に追加)詳細は公式の Domain separation and Data Management(Zurich) を参照してください。データ管理の観点でドメイン分離がどう効くかが説明されています。
ドメインパス(sys_domain_path)が階層を表す
ドメイン分離を有効化して子ドメインを作ると、sys_domain_path が意味を持ちはじめます。ドメインパスは、3文字コードをスラッシュで区切って並べた、ドメインを一意に識別する文字列です(公式仕様)。

考え方は次のとおりです(この階層の挙動は本PDIでは未有効のため、公式仕様に基づく説明です)。
ルート(global)のパスは /。
子ドメインを作ると /xxx/、孫は /xxx/yyy/ のようにパスが伸びます。
ServiceNow は sys_domain_path の前方一致(STARTSWITH)で「自ドメイン+その配下」をまとめて判定できます。パスが前方一致するということは、ドメインの祖先・子孫関係を表すからです。
ドメインの可視範囲は「パスの前方一致」というシンプルなルールで決まります。親ドメインの管理者は、自分のパスで始まる全レコード(自分と配下)を見られます。今回のPDIは全レコードが / なので分岐は起きませんが、有効化すればこのパスが分離の判定軸になります。
今回のPDIではドメイン分離は未有効だった
ここまで「器はあるが分離はしていない」と繰り返してきました。その根拠が、ドメイン分離関連プラグインの状態です。
プラグインの状態(OOTB)

今回確認した PDI(Zurich / OOTB)のプラグイン状態は次のとおりでした。
Domain Tracking(com.glide.domain_tracking)… Inactive
Performance Analytics - Domain Support(com.snc.pa.domain_support)… Inactive
SCIM v2 - Cross-domain Identity Management(com.snc.integration.scim2)… Inactive
Reliability Metrics for Data Management Domain(com.glide.dm_reliability_metrics)… Active
このうち Reliability Metrics for Data Management Domain は、ドメイン分離本体のプラグインではありません。

ドメイン分離の本体は有効化されておらず、ドメインは global(ルート)のみの最小構成でした。だからこそ sys_domain 物理テーブルも存在しません(次の実画面)。

カスタムテーブルをドメイン分離する場合
ドメイン分離を有効化した場合に何が起きるかは、本PDIでは確認できないため検証範囲外とします。公式によれば、カスタムテーブルをドメイン分離するには列名 sys_domain(domain_id 型)を作ります。あわせて、値を自動設定するビジネスルール(Domain - Set Domain - Task など)が必要です。設定に失敗したレコードを既定ドメインへ寄せる Domain - Default - Task も必要とされています。
たとえば自社で u_contract(契約)というカスタムテーブルを新規にドメイン分離するとします。Table 定義(sys_db_object)からそのテーブルを開き、Columns に sys_domain という名前の列を1つ追加します。列名を sys_domain にした時点で、システムが domain_id 型と既定値 global を自動で付けます。sys_domain_path も連動して生成されます(本記事で確認した予約列の挙動)。あとは有効化済み環境で値設定のビジネスルールが配られれば、A社のユーザーが作った契約レコードには A社ドメインが自動で入る、という流れです(この一連の実走は本PDIでは未有効のため検証範囲外)。
詳細は公式の Exploring domain separation(Zurich) を参照してください。ドメイン分離の各設定(ドット指定フィールドへの適用など)が説明されています。
ServiceNow におけるドメイン分離のポイント
ドメイン分離の正体は、各テーブルの2フィールド sys_domain(domain_id 型・既定 global)と sys_domain_path(domain_path 型・最大255)です。どのドメインに属し、階層のどこにいるかを記録します。
器は、ドメイン分離を有効化していなくても1,700超のテーブルに組み込み済みでした(Domain ID 型 1,758 / Domain Path 型 1,493、テーブル総数 6,193)。
sys_domain 列は基底テーブルが定義し子テーブルが継承します(task が定義し incident が継承)。調べるときは親まで遡るのが定石です。
今回のPDIはドメイン分離プラグインが未有効で、sys_domain 物理テーブルは存在せず、全レコードが global(ルート・path /)に同居していました。ドメインの参照は sys_user_group/global に解決されます。
sys_domain_path は前方一致で「自ドメイン+配下」を判定する設計です(有効化後に効きます)。
ドメイン分離を調べるときは、次の順に確認すると「分離されているのか・していないのか」がぶれずに判断できます。まず、プラグインが有効かをプラグイン状態で確認します。次に、対象テーブル(と親)の sys_domain 列の有無を見ます。最後に、実レコードの sys_domain / sys_domain_path の値を確認します。
実機を見て気づいたこと
sys_domain 列を持つテーブルは1,753件でしたが、sys_domain_path 列を持つテーブルは1,494件と、259件少ない結果でした。列名を sys_domain にすれば sys_domain_path も自動でセット生成されると思っていましたが、実測の件数を見る限り必ず1対1というわけではなさそうです(この差の原因までは本PDIでは確認できておらず推測です)。
sys_domain という列名で数えたディクショナリ定義は1,753件、internal_type=domain_id で数えたフィールド総数は1,758件と、こちらも一致しませんでした。列名の一致検索だけでは domain_id 型のフィールドを取りこぼす可能性がある、という点は、自分のインスタンスでドメイン列を棚卸しするときに気をつけたいポイントです。
次に読むなら
次に確認したいのは、ドメイン分離を有効化した世界です。子ドメインの作成、ドメイン階層の管理画面、Domain - Set/Default Domain ビジネスルールによる値の自動設定、ドメイン分離下でのACL評価が対象になります。sys_domain_path の前方一致でデータ可視範囲が決まるルールは、ACL やレポートの見え方に直結します。これらは本記事で扱った「器」と「未有効」の先にあたり、有効化済み環境を用意して別記事で扱う予定です。権限の土台であるユーザー・グループ・ロールの構造も押さえると、可視性の全体像がつかみやすくなります。
用語
PDI:Personal Developer Instance(無料の検証用インスタンス)
OOTB:Out of the Box(初期設定のまま)
ドメイン分離(Domain Separation):1インスタンスをドメイン単位でデータ・処理・管理に分ける仕組み
sys_domain(Domain):レコードがどのドメインに属すかを指すフィールド(domain_id 型)
sys_domain_path(Domain Path):ドメインの階層上の位置を表す文字列(domain_path 型)
global:ドメインの最上位(ルート)。OOTB では全レコードがここに属する
domain_id / domain_path:ドメイン分離専用のフィールド型(通常の参照とは別物)
自分の環境で確認するときのURL
先頭に自分のインスタンスを付けると、同じ画面が開けます。
Domain ID 型の列 : sys_dictionary_list.do?sysparm_query=internal_type=domain_id^ORDERBYname
Domain Path 型の列 : sys_dictionary_list.do?sysparm_query=internal_type=domain_path^ORDERBYname
ドメイン分離プラグイン : v_plugin_list.do?sysparm_query=nameLIKEdomain^ORidLIKEdomain^ORDERBYactive
sys_domainを含む表名 : sys_db_object_list.do?sysparm_query=nameLIKEdomain
Incident一覧 : incident_list.do(歯車から Domain / Domain Path 列を表示)検証範囲とおことわり
確認した範囲は次のとおりです。
sys_domain(domain_id・既定 global)/ sys_domain_path(domain_path・最大255)のフィールド型
ディクショナリ件数(sys_domain 1,753 / sys_domain_path 1,494)
型別件数とテーブル総数(Domain ID 型 1,758 / Domain Path 型 1,493、テーブル総数 6,193)
sys_domain 物理テーブルが存在しないこと
task→incident の継承と sys_user の自前定義
実レコードが全て global・path /(参照は sys_user_group/global へ解決)であること
ドメイン分離プラグインが未有効であること
すべて PDI(Zurich / 2026-06-22 確認)の OOTB で取得しています。
確認していない範囲(断定しません)は次のとおりです。
ドメイン分離を有効化した後の挙動(子ドメイン作成、ドメイン階層 UI、Domain - Set/Default Domain ビジネスルールの実走、ドメインパスの実例コード)
ドメイン分離下での ACL / Business Rule の評価順序
ドメイン間の可視性(visibility)・上書き(override)レコード
MSP・マルチテナント本番での実運用
SCIM/SSO 連携
これらは本PDIがドメイン分離未有効のため取得できず、捏造していません。別記事で有効化済み環境を用意して扱います。
今回は PDI の OOTB(ドメイン分離未有効)を前提に、ドメイン分離の「器」である sys_domain / sys_domain_path を確認しました。実環境では、ドメイン分離の有効化やカスタマイズによって、ドメイン階層・件数・挙動が変わります。