SaaSを題材にしたオントロジーを作ってみようシリーズ #2 組織の単位など

SaaSを題材にしたオントロジーを作ってみようシリーズ #2 組織の単位など

こんにちは! 第1回目はいかがでしたでしょうか?!難しかったですかね?

だとしても、第2回を読もうとしていただいて、ありがとうございます!

第2回は、さらにモリモリの内容になっちゃいましたが、テナントをベースとしたさらなる深掘りと、オントロジーの活用例の第一歩として実際にクエリをしてみたいと思います!

クエリには、「SPARQL」というクエリ言語を使います。突然登場して混乱するかもなので、「SPARQL」を初めて聞いたという方は、

という別のブログを書いておりますので、まずこちらをざっと眺めてみてください!

そしてさらに!「SHACL」というものも登場してきます。これは、ざっくり言うと「RDFデータが期待する形になっているかを検証するための言語」なのですが、こちらについても

を眺めてみてください!

では、さっそく第1回目の復習も兼ねて、テナントについて改めて考えてみたいと思います。

0. Tenantは会社なのか?

Organization・Workspace・Teamを分けて考える

前回は、B2B SaaSの最小構造として、次のモデルを作りました。

Tenant
├── Membership
│   ├── User
│   └── Role
│       └── Permission
│
├── Subscription
│   └── Plan
│
└── Resource

そして、Tenantを次のように定義しました。

Tenant
= SaaS上でユーザー、権限、契約、データを分離する論理的な境界

この定義は、B2B SaaSを考えるうえで非常に重要ですが、実際のSaaSを設計し始めると、ほぼ確実に次の疑問が出てきます。

Tenantは会社なのか?

最初は多くの場合、Tenantを顧客企業と同じものとして扱います。

株式会社ABC
= Tenant ABC

小規模なSaaSであれば、これでも十分ですが、顧客企業が大きくなったり、複数部署や子会社で利用したりすると、会社とTenantを同一視することが難しくなります。


1. 会社とTenantが一致しない例

株式会社ABCという会社があるとします。

株式会社ABC
├── 営業本部
│   ├── 東日本営業部
│   └── 西日本営業部
├── 開発本部
│   ├── 製品開発部
│   └── 基盤開発部
└── 管理本部
    ├── 経理部
    └── 人事部

この会社が、あるプロジェクト管理SaaSを導入したとします。会社全体で1つの利用環境を使うのであれば、構造は単純です。

Tenant ABC

しかし、次のような要求が発生するかもしれません。

  • 営業部と開発部でデータを分離したい
  • 子会社ごとに管理者を分けたい
  • 部門ごとに利用料金を集計したい
  • 外部委託先には一部のプロジェクトだけ見せたい
  • 海外法人と国内法人でデータ保存地域を分けたい
  • 会社全体では契約を一本化したい

ここで、すべての部署を別Tenantにすると、今度は会社全体としての管理が分断されてしまいます。

営業本部Tenant
開発本部Tenant
管理本部Tenant

反対に、すべてを1つのTenantに押し込むと、今度はデータや権限の分離が難しくなります。つまり、現実世界の組織構造と、SaaS上の利用構造は、似ているようで異なるのです。


2. Tenantは現実世界の組織ではない

Tenantは、会社や部署そのものではなく、SaaS上で何かを分離するための境界です。

Tenant
= SaaS上の管理・契約・セキュリティ・データ分離の境界

SaaSにおいてTenantを分ける理由には、次のようなものがあります。

  • データを分離する
  • 管理者を分離する
  • 契約を分離する
  • 請求を分離する
  • 認証設定を分離する
  • データ保存地域を分離する
  • セキュリティポリシーを分離する
  • 監査ログを分離する

したがって、現実世界の会社が1つであっても、Tenantが複数になることがあります。

株式会社ABC
├── 国内事業Tenant
└── 海外事業Tenant

逆に、複数の会社が1つのTenantを共有することもあります。

ABCグループTenant
├── 株式会社ABC
├── ABC販売株式会社
└── ABCシステムズ株式会社

この場合、会社は3つあってもTenantは1つです。


3. Organizationを追加する

現実世界の会社や部署を表すため、Organizationという概念を導入します。

Organization
= 現実世界に存在する組織・団体

Organizationの例には、次のようなものがあります。

  • 法人
  • 会社
  • 官公庁
  • 自治体
  • 学校
  • 医療法人
  • 部署
  • 事業部
  • 支店
  • 子会社
  • プロジェクト組織

最初のモデルでは、会社も部署もOrganizationの一種として扱います。

株式会社ABC a Organization
営業本部 a Organization
開発本部 a Organization

Organization同士には、階層関係を持たせます。

株式会社ABC
hasSubOrganization
営業本部

全体としては、次のようになります。

graph TD
    A[株式会社ABC
Organization] B[営業本部
Organization] C[開発本部
Organization] D[管理本部
Organization] A -->|hasSubOrganization| B A -->|hasSubOrganization| C A -->|hasSubOrganization| D

ここで重要なのは、OrganizationとTenantを分離することです。

Organization
= 現実世界の組織

Tenant
= SaaS上の論理的な利用境界

4. OrganizationとTenantの関係

OrganizationとTenantの間には、利用関係を置きます。

Organization usesTenant Tenant

逆方向から表現するなら、次のようになります。

Tenant servesOrganization Organization

たとえば株式会社ABCがTenant ABCを使う場合は、次のように表現できます。

株式会社ABC
→ usesTenant
→ Tenant ABC
graph LR
    O[株式会社ABC
Organization] T[Tenant ABC
Tenant] O -->|usesTenant| T

ただし、OrganizationとTenantは1対1とは限りません。

  • 1つのOrganizationが複数Tenantを利用する
株式会社ABC
├── 国内事業Tenant
└── 海外事業Tenant
  • 複数Organizationが1つのTenantを利用する
ABCグループTenant
├── 株式会社ABC
├── ABC販売株式会社
└── ABCシステムズ株式会社

したがって、一般化すればOrganizationとTenantの関係は多対多ですが、初期実装では複雑さを避けるため、

1つのTenantは1つの主Organizationに対応する

という制約を置いても構いません。


5. Workspaceを追加する

次に、Workspaceを導入します。

Workspace
= Tenantの内部に作られる共同作業の論理的な空間

TenantとWorkspaceは、どちらも境界に見えるため混同されやすい概念ですが、役割を分けると次のようになります。

Tenant
= 契約・請求・セキュリティ・データ分離の境界

Workspace
= 日常的な共同作業や業務データ整理の境界

たとえば、株式会社ABCのTenant内に、次のWorkspaceを作ることができます。

Tenant ABC
├── 営業Workspace
├── 開発Workspace
└── 管理Workspace
graph TD
    T[Tenant ABC]

    W1[営業Workspace]
    W2[開発Workspace]
    W3[管理Workspace]

    T -->|containsWorkspace| W1
    T -->|containsWorkspace| W2
    T -->|containsWorkspace| W3

この構造であれば、契約やSSO設定はTenantで共通化しながら、業務データをWorkspaceごとに整理できます。


6. TenantとWorkspaceの違い

Tenantを分けると、多くの場合、次のような設定も分離されます。

  • 契約
  • 請求
  • SSO
  • 管理者
  • セキュリティポリシー
  • APIキー
  • 監査ログ
  • データ保持期間
  • データ保存地域

一方、Workspaceは、そこまで強い境界ではありません。

契約は1つ
請求も1つ
SSO設定も共通
しかし業務データや作業場所は分けたい

このような要求にWorkspaceが向いています。

たとえるなら、

Tenant
= 建物全体

Workspace
= 建物内の用途別エリア

です。

ただし、このたとえも完全ではありません。Workspace間で一部のデータを共有するSaaSもあるためです。オントロジーのたとえ話は理解を助ける一方で、使いすぎると逆に概念を固定しすぎます。たとえは地図であって、現実そのものではありませんが、ややこしくなるので、いまのところはそういうものとして次に行きましょう!


7. ResourceとWorkspace

Workspaceを導入すると、ResourceはWorkspaceに格納されるようになります。

Tenant containsWorkspace Workspace
Workspace containsResource Resource
graph LR
    T[Tenant]
    W[Workspace]
    R[Resource]

    T -->|containsWorkspace| W
    W -->|containsResource| R

ResourceからTenantをたどる場合は、次の経路になります。

Resource
→ belongsToWorkspace
→ Workspace
→ belongsToTenant
→ Tenant

たとえば、CRMであれば次のようになります。

顧客(データ)「株式会社XYZ」
→ 営業Workspaceに属する
→ Tenant ABCに属する

実際のデータベースでは、Resourceにworkspace_idtenant_idの両方を持たせることがあります。

customer
├── customer_id
├── tenant_id
├── workspace_id
├── name
└── created_at

概念上はworkspace_idからTenantを特定できますが、物理設計では次の理由でtenant_idを重複して保持することがあります。

  • Tenant条件をすべてのクエリに適用するため
  • データ漏えいを防ぐため
  • パーティションキーに利用するため
  • 検索性能を高めるため
  • 不整合を検知するため

これは、概念モデルと物理データモデルが同一ではない例です。オントロジーは「何を意味するか」を表し、データベースは「どう安全かつ高速に保存するか」を考えます。


8. Teamを追加する

次に、Teamを追加します。

Team
= Tenant内で共同作業や権限付与に使われるメンバーの集合

Teamの例は次のとおりです。

  • 営業Team
  • 開発Team
  • 管理者Team
  • セキュリティTeam
  • プロジェクトATeam
  • 外部監査Team

TeamはOrganizationと似ていますが、同じものではありません。Organizationが現実世界の組織構造を表すのに対し、Teamは、SaaS内で人をまとめるための集合です。

Organization
= 現実世界の組織

Team
= SaaS上のメンバー集合

たとえば、プロジェクトATeamには、複数の部署からメンバーが参加できます。

プロジェクトATeam
├── 営業本部の山田さん
├── 開発本部の佐藤さん
├── 管理本部の鈴木さん
└── 外部委託先の高橋さん

このTeamは正式な組織図には存在しないかもしれませんが、SaaS上のアクセス制御では重要です。


9. TeamにはUserではなくMembershipを入れる

Teamのメンバーとして、Userを直接追加する設計もできます。

Team hasMember User

しかし、B2B SaaS共通モデルとしては、MembershipをTeamに所属させる方が自然です。

Team hasMember Membership

理由は、同じUserが複数Tenantに所属できるためです。

User: 山田太郎

Membership 1
├── Tenant A
└── Role: 管理者

Membership 2
├── Tenant B
└── Role: 閲覧者

Tenant AのTeamに所属させたいのは、単なる山田太郎というUserではありません。

正確には、

山田太郎のTenant AにおけるMembership

を所属させたいのです。

したがって、次の関係を使います。

Membership memberOfTeam Team

10. WorkspaceとTeamの違い

WorkspaceとTeamも混同されやすい概念で、違いは何をまとめるかにあります。

Workspace
= 作業場所やデータをまとめる

Team
= メンバーをまとめる

営業Workspaceには、次のようなデータがあります。

営業Workspace
├── 顧客
├── 商談
├── 営業資料
└── 活動履歴

営業Teamには、人が所属します。

営業Team
├── 山田さんのMembership
├── 佐藤さんのMembership
└── 鈴木さんのMembership

TeamがWorkspaceにアクセスします。

Team canAccessWorkspace Workspace

つまり、

Team
= 誰が

Workspace
= どこで

Resource
= 何を

と考えることができます。


11. Organization・Tenant・Workspace・Teamの整理

概念意味主に含むもの
Organization現実世界の組織法人、部署、支店
TenantSaaS上の管理・分離境界契約、セキュリティ、設定
WorkspaceTenant内の共同作業空間Resource、作業対象
TeamTenant内のメンバー集合Membership

簡単にまとめると、次のようになります。

Organization
= 現実世界では、どの組織なのか

Tenant
= SaaS上では、どこまでを1つの顧客環境として扱うのか

Workspace
= Tenant内で、どこを作業場所として使うのか

Team
= 誰と誰をひとまとまりにするのか

12. Roleの適用範囲という問題

Workspaceを導入すると、前回のRoleモデルに新しい問題が生まれます。前回は、MembershipにRoleを付与しました。

Membership assignedRole Role

しかし、Roleがどの範囲で有効なのか分かりません。

たとえば、山田太郎さんが次の権限を持っているとします。

Tenant ABCでは一般メンバー

営業Workspaceでは編集者

開発Workspaceでは閲覧者

この場合、単純な

Membership hasRole Editor

では不十分で、どのWorkspaceでEditorなのかを表現する必要があります。


13. RoleAssignmentを追加する

Roleの付与そのものを表す概念として、RoleAssignmentを追加します。

RoleAssignment
= ある主体に、ある適用範囲で、あるRoleを付与した事実

RoleAssignmentは、最低限次の3つを結びます。

assignee
= 誰に付与するか

role
= どのRoleを付与するか

scope
= どの範囲で有効か

営業Workspaceで山田太郎さんをEditorにする場合は、次のようになります。

RoleAssignment
├── assignee: 山田太郎のMembership
├── role: Editor
└── scope: 営業Workspace

Tenant全体の管理者にする場合は、次のようになります。

RoleAssignment
├── assignee: 山田太郎のMembership
├── role: Tenant Administrator
└── scope: Tenant ABC
graph TD
    M[Membership]
    RA[RoleAssignment]
    R[Role]
    S[Scope
Tenant or Workspace] M -->|hasRoleAssignment| RA RA -->|assignedRole| R RA -->|appliesTo| S

たとえばRoleAssignmentには、将来的に次の属性を持たせることができます。

  • 付与日時
  • 付与者
  • 有効期限
  • 付与理由
  • 手動付与か自動付与か
  • 一時的な権限か
  • 承認済みか

関係に属性が必要になったら、その関係を独立した概念にする。これはオントロジー設計において、とてもよく使われる考え方のようです。(たぶん)


14. 今回追加するクラス

今回のオントロジーでは、前回のクラスに次のものを追加します。

Organization
Workspace
Team
RoleAssignment

前回から引き継ぐ主なクラスは次のとおりです。

Tenant
User
Membership
Role
Permission
Subscription
Plan
Resource

今回までの全体像は、次のようになります。

Organization
└── usesTenant
    └── Tenant
        ├── Membership
        │   └── User
        │
        ├── Workspace
        │   └── Resource
        │
        ├── Team
        │   └── Membership
        │
        ├── RoleAssignment
        │   ├── Membership
        │   ├── Role
        │   │   └── Permission
        │   └── Scope
        │       ├── Tenant
        │       └── Workspace
        │
        └── Subscription
            └── Plan

15. オントロジー定義のTurtle

ここからは、今回追加した概念をTurtleで定義します。

最初は、できるだけ単純なOWL/RDFSモデルにします。

@prefix saas: <https://example.com/ontology/saas#> .
@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl:  <http://www.w3.org/2002/07/owl#> .
@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> .

saas:B2BSaaSOntology a owl:Ontology ;
    rdfs:label "B2B SaaS Core Ontology"@en ,
               "B2B SaaS共通オントロジー"@ja .

#
# Classes
#

saas:Organization a owl:Class ;
    rdfs:label "Organization"@en ,
               "組織"@ja ;
    rdfs:comment
        "現実世界に存在する法人、部署、支店、団体などを表す。"@ja .

saas:Tenant a owl:Class ;
    rdfs:label "Tenant"@en ,
               "テナント"@ja ;
    rdfs:comment
        "SaaS上で契約、管理、セキュリティ、データを分離する論理的な境界。"@ja .

saas:Workspace a owl:Class ;
    rdfs:label "Workspace"@en ,
               "ワークスペース"@ja ;
    rdfs:comment
        "Tenant内部に作られる共同作業の論理的な空間。"@ja .

saas:Team a owl:Class ;
    rdfs:label "Team"@en ,
               "チーム"@ja ;
    rdfs:comment
        "Tenant内で共同作業やアクセス制御に使用するMembershipの集合。"@ja .

saas:User a owl:Class ;
    rdfs:label "User"@en ,
               "ユーザー"@ja .

saas:Membership a owl:Class ;
    rdfs:label "Membership"@en ,
               "所属関係"@ja ;
    rdfs:comment
        "Userが特定のTenantに参加しているという関係を表す。"@ja .

saas:RoleAssignment a owl:Class ;
    rdfs:label "Role Assignment"@en ,
               "ロール割当て"@ja ;
    rdfs:comment
        "あるMembershipに、特定の適用範囲でRoleを付与した事実。"@ja .

saas:Role a owl:Class ;
    rdfs:label "Role"@en ,
               "ロール"@ja .

saas:Permission a owl:Class ;
    rdfs:label "Permission"@en ,
               "権限"@ja .

saas:Resource a owl:Class ;
    rdfs:label "Resource"@en ,
               "リソース"@ja ;
    rdfs:comment
        "SaaS上でTenantまたはWorkspaceが管理する業務オブジェクト。"@ja .

saas:Subscription a owl:Class .
saas:Plan a owl:Class .

#
# Organization properties
#

saas:hasSubOrganization a owl:ObjectProperty ;
    rdfs:domain saas:Organization ;
    rdfs:range saas:Organization ;
    rdfs:label "has sub-organization"@en ,
               "下位組織を持つ"@ja .

saas:parentOrganization a owl:ObjectProperty ;
    owl:inverseOf saas:hasSubOrganization ;
    rdfs:domain saas:Organization ;
    rdfs:range saas:Organization ;
    rdfs:label "parent organization"@en ,
               "上位組織"@ja .

saas:usesTenant a owl:ObjectProperty ;
    rdfs:domain saas:Organization ;
    rdfs:range saas:Tenant ;
    rdfs:label "uses tenant"@en ,
               "テナントを利用する"@ja .

saas:servesOrganization a owl:ObjectProperty ;
    owl:inverseOf saas:usesTenant ;
    rdfs:domain saas:Tenant ;
    rdfs:range saas:Organization ;
    rdfs:label "serves organization"@en ,
               "組織に提供される"@ja .

#
# Workspace properties
#

saas:containsWorkspace a owl:ObjectProperty ;
    rdfs:domain saas:Tenant ;
    rdfs:range saas:Workspace ;
    rdfs:label "contains workspace"@en ,
               "ワークスペースを含む"@ja .

saas:belongsToTenant a owl:ObjectProperty ;
    rdfs:range saas:Tenant ;
    rdfs:label "belongs to tenant"@en ,
               "テナントに属する"@ja .

saas:workspaceBelongsToTenant a owl:ObjectProperty ;
    rdfs:subPropertyOf saas:belongsToTenant ;
    owl:inverseOf saas:containsWorkspace ;
    rdfs:domain saas:Workspace ;
    rdfs:range saas:Tenant .

saas:containsResource a owl:ObjectProperty ;
    rdfs:domain saas:Workspace ;
    rdfs:range saas:Resource ;
    rdfs:label "contains resource"@en ,
               "リソースを含む"@ja .

saas:belongsToWorkspace a owl:ObjectProperty ;
    owl:inverseOf saas:containsResource ;
    rdfs:domain saas:Resource ;
    rdfs:range saas:Workspace ;
    rdfs:label "belongs to workspace"@en ,
               "ワークスペースに属する"@ja .

#
# Membership and Team properties
#

saas:belongsToUser a owl:ObjectProperty ;
    rdfs:domain saas:Membership ;
    rdfs:range saas:User .

saas:membershipBelongsToTenant a owl:ObjectProperty ;
    rdfs:subPropertyOf saas:belongsToTenant ;
    rdfs:domain saas:Membership ;
    rdfs:range saas:Tenant .

saas:containsTeam a owl:ObjectProperty ;
    rdfs:domain saas:Tenant ;
    rdfs:range saas:Team ;
    rdfs:label "contains team"@en ,
               "チームを含む"@ja .

saas:teamBelongsToTenant a owl:ObjectProperty ;
    rdfs:subPropertyOf saas:belongsToTenant ;
    owl:inverseOf saas:containsTeam ;
    rdfs:domain saas:Team ;
    rdfs:range saas:Tenant .

saas:memberOfTeam a owl:ObjectProperty ;
    rdfs:domain saas:Membership ;
    rdfs:range saas:Team ;
    rdfs:label "member of team"@en ,
               "チームに所属する"@ja .

saas:hasTeamMember a owl:ObjectProperty ;
    owl:inverseOf saas:memberOfTeam ;
    rdfs:domain saas:Team ;
    rdfs:range saas:Membership .

saas:canAccessWorkspace a owl:ObjectProperty ;
    rdfs:domain saas:Team ;
    rdfs:range saas:Workspace ;
    rdfs:label "can access workspace"@en ,
               "ワークスペースにアクセスできる"@ja .

#
# Role assignment properties
#

saas:hasRoleAssignment a owl:ObjectProperty ;
    rdfs:domain saas:Membership ;
    rdfs:range saas:RoleAssignment .

saas:assignedTo a owl:ObjectProperty ;
    owl:inverseOf saas:hasRoleAssignment ;
    rdfs:domain saas:RoleAssignment ;
    rdfs:range saas:Membership ;
    rdfs:label "assigned to"@en ,
               "割当て対象"@ja .

saas:assignedRole a owl:ObjectProperty ;
    rdfs:domain saas:RoleAssignment ;
    rdfs:range saas:Role ;
    rdfs:label "assigned role"@en ,
               "割り当てるロール"@ja .

saas:appliesToTenant a owl:ObjectProperty ;
    rdfs:domain saas:RoleAssignment ;
    rdfs:range saas:Tenant ;
    rdfs:label "applies to tenant"@en ,
               "テナントに適用される"@ja .

saas:appliesToWorkspace a owl:ObjectProperty ;
    rdfs:domain saas:RoleAssignment ;
    rdfs:range saas:Workspace ;
    rdfs:label "applies to workspace"@en ,
               "ワークスペースに適用される"@ja .

saas:grantsPermission a owl:ObjectProperty ;
    rdfs:domain saas:Role ;
    rdfs:range saas:Permission .

#
# Datatype properties
#

saas:name a owl:DatatypeProperty ;
    rdfs:range xsd:string .

saas:status a owl:DatatypeProperty ;
    rdfs:range xsd:string .

saas:createdAt a owl:DatatypeProperty ;
    rdfs:range xsd:dateTime .

saas:assignedAt a owl:DatatypeProperty ;
    rdfs:domain saas:RoleAssignment ;
    rdfs:range xsd:dateTime .

saas:expiresAt a owl:DatatypeProperty ;
    rdfs:domain saas:RoleAssignment ;
    rdfs:range xsd:dateTime .

第一回に比べて、いきなりでっかくなりましたね。。。


16. belongsToTenantを共通化する理由

上のTurtleでは、次の3つのプロパティを用意しました。

workspaceBelongsToTenant
membershipBelongsToTenant
teamBelongsToTenant

これらは、すべて上位プロパティであるbelongsToTenantのサブプロパティです。

saas:workspaceBelongsToTenant
    rdfs:subPropertyOf saas:belongsToTenant .

saas:membershipBelongsToTenant
    rdfs:subPropertyOf saas:belongsToTenant .

saas:teamBelongsToTenant
    rdfs:subPropertyOf saas:belongsToTenant .

これにより、Workspace、Membership、Teamをまとめて、

?entity saas:belongsToTenant ?tenant .

という形で検索できます。

一方で、詳細を知りたい場合は、

?workspace saas:workspaceBelongsToTenant ?tenant .

のように、具体的な関係を指定できます。

このような上位プロパティは、検索や推論を簡単にするために役立ちます。


17. 株式会社ABCの具体的なインスタンス

ここからは、実際のデータをTurtleで表現します。

17.1 組織・Tenant・Workspace・Team

@prefix saas: <https://example.com/ontology/saas#> .
@prefix ex:   <https://example.com/data/> .
@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> .

#
# Organizations
#

ex:organization-abc a saas:Organization ;
    saas:name "株式会社ABC" ;
    saas:hasSubOrganization
        ex:organization-abc-sales,
        ex:organization-abc-development,
        ex:organization-abc-administration ;
    saas:usesTenant ex:tenant-abc .

ex:organization-abc-sales a saas:Organization ;
    saas:name "営業本部" ;
    saas:parentOrganization ex:organization-abc .

ex:organization-abc-development a saas:Organization ;
    saas:name "開発本部" ;
    saas:parentOrganization ex:organization-abc .

ex:organization-abc-administration a saas:Organization ;
    saas:name "管理本部" ;
    saas:parentOrganization ex:organization-abc .

#
# Tenant
#

ex:tenant-abc a saas:Tenant ;
    saas:name "株式会社ABCテナント" ;
    saas:status "active" ;
    saas:containsWorkspace
        ex:workspace-sales,
        ex:workspace-development,
        ex:workspace-administration ;
    saas:containsTeam
        ex:team-sales,
        ex:team-development,
        ex:team-project-alpha .

#
# Workspaces
#

ex:workspace-sales a saas:Workspace ;
    saas:name "営業Workspace" ;
    saas:workspaceBelongsToTenant ex:tenant-abc .

ex:workspace-development a saas:Workspace ;
    saas:name "開発Workspace" ;
    saas:workspaceBelongsToTenant ex:tenant-abc .

ex:workspace-administration a saas:Workspace ;
    saas:name "管理Workspace" ;
    saas:workspaceBelongsToTenant ex:tenant-abc .

#
# Teams
#

ex:team-sales a saas:Team ;
    saas:name "営業Team" ;
    saas:teamBelongsToTenant ex:tenant-abc ;
    saas:canAccessWorkspace ex:workspace-sales .

ex:team-development a saas:Team ;
    saas:name "開発Team" ;
    saas:teamBelongsToTenant ex:tenant-abc ;
    saas:canAccessWorkspace ex:workspace-development .

ex:team-project-alpha a saas:Team ;
    saas:name "プロジェクトAlpha Team" ;
    saas:teamBelongsToTenant ex:tenant-abc ;
    saas:canAccessWorkspace
        ex:workspace-sales,
        ex:workspace-development .


18. UserとMembershipの具体例

#
# Users
#

ex:user-yamada a saas:User ;
    saas:name "山田太郎" ;
    saas:status "active" .

ex:user-sato a saas:User ;
    saas:name "佐藤花子" ;
    saas:status "active" .

ex:user-suzuki a saas:User ;
    saas:name "鈴木一郎" ;
    saas:status "active" .

#
# Memberships
#

ex:membership-yamada-abc a saas:Membership ;
    saas:belongsToUser ex:user-yamada ;
    saas:membershipBelongsToTenant ex:tenant-abc ;
    saas:status "active" ;
    saas:memberOfTeam
        ex:team-sales,
        ex:team-project-alpha .

ex:membership-sato-abc a saas:Membership ;
    saas:belongsToUser ex:user-sato ;
    saas:membershipBelongsToTenant ex:tenant-abc ;
    saas:status "active" ;
    saas:memberOfTeam
        ex:team-development,
        ex:team-project-alpha .

ex:membership-suzuki-abc a saas:Membership ;
    saas:belongsToUser ex:user-suzuki ;
    saas:membershipBelongsToTenant ex:tenant-abc ;
    saas:status "active" ;
    saas:memberOfTeam ex:team-sales .

ここで、UserではなくMembershipがTeamに所属していることが重要です。

山田太郎という人

ではなく、

山田太郎のTenant ABCにおける所属

が営業Teamに参加しています。


19. RoleとPermissionの具体例

#
# Permissions
#

ex:permission-tenant-manage a saas:Permission ;
    saas:name "tenant.manage" .

ex:permission-member-invite a saas:Permission ;
    saas:name "member.invite" .

ex:permission-workspace-read a saas:Permission ;
    saas:name "workspace.read" .

ex:permission-resource-read a saas:Permission ;
    saas:name "resource.read" .

ex:permission-resource-create a saas:Permission ;
    saas:name "resource.create" .

ex:permission-resource-update a saas:Permission ;
    saas:name "resource.update" .

ex:permission-resource-delete a saas:Permission ;
    saas:name "resource.delete" .

#
# Roles
#

ex:role-tenant-admin a saas:Role ;
    saas:name "Tenant Administrator" ;
    saas:grantsPermission
        ex:permission-tenant-manage,
        ex:permission-member-invite,
        ex:permission-workspace-read,
        ex:permission-resource-read,
        ex:permission-resource-create,
        ex:permission-resource-update,
        ex:permission-resource-delete .

ex:role-workspace-editor a saas:Role ;
    saas:name "Workspace Editor" ;
    saas:grantsPermission
        ex:permission-workspace-read,
        ex:permission-resource-read,
        ex:permission-resource-create,
        ex:permission-resource-update .

ex:role-workspace-viewer a saas:Role ;
    saas:name "Workspace Viewer" ;
    saas:grantsPermission
        ex:permission-workspace-read,
        ex:permission-resource-read .

20. RoleAssignmentの具体例

山田太郎さんには、次の権限を付与します。

Tenant ABC全体
→ Tenant Administrator

営業Workspace
→ Workspace Editor

開発Workspace
→ Workspace Viewer

これをTurtleで表現すると、次のようになります。

#
# Tenant-level role assignment
#

ex:role-assignment-yamada-tenant-admin a saas:RoleAssignment ;
    saas:assignedTo ex:membership-yamada-abc ;
    saas:assignedRole ex:role-tenant-admin ;
    saas:appliesToTenant ex:tenant-abc ;
    saas:assignedAt "2026-08-01T09:00:00+09:00"^^xsd:dateTime .

#
# Workspace-level role assignments
#

ex:role-assignment-yamada-sales-editor a saas:RoleAssignment ;
    saas:assignedTo ex:membership-yamada-abc ;
    saas:assignedRole ex:role-workspace-editor ;
    saas:appliesToWorkspace ex:workspace-sales ;
    saas:assignedAt "2026-08-01T09:05:00+09:00"^^xsd:dateTime .

ex:role-assignment-yamada-development-viewer a saas:RoleAssignment ;
    saas:assignedTo ex:membership-yamada-abc ;
    saas:assignedRole ex:role-workspace-viewer ;
    saas:appliesToWorkspace ex:workspace-development ;
    saas:assignedAt "2026-08-01T09:10:00+09:00"^^xsd:dateTime .

ex:role-assignment-sato-development-editor a saas:RoleAssignment ;
    saas:assignedTo ex:membership-sato-abc ;
    saas:assignedRole ex:role-workspace-editor ;
    saas:appliesToWorkspace ex:workspace-development ;
    saas:assignedAt "2026-08-01T10:00:00+09:00"^^xsd:dateTime .

21. Resourceの具体例

営業Workspaceには顧客と商談があり、開発Workspaceには設計書があるとします。

ex:customer-xyz a saas:Resource ;
    saas:name "株式会社XYZ" ;
    saas:belongsToWorkspace ex:workspace-sales .

ex:deal-core-system-renewal a saas:Resource ;
    saas:name "基幹システム刷新案件" ;
    saas:belongsToWorkspace ex:workspace-sales .

ex:document-system-architecture a saas:Resource ;
    saas:name "システム構成設計書" ;
    saas:belongsToWorkspace ex:workspace-development .

このデータから、

株式会社XYZ
→ 営業Workspace
→ Tenant ABC

という関係をたどれます。


22. 全インスタンスの関係図

graph TD
    O[株式会社ABC
Organization] T[Tenant ABC] W1[営業Workspace] W2[開発Workspace] TM1[営業Team] TM2[開発Team] TM3[プロジェクトAlpha Team] U1[山田太郎
User] U2[佐藤花子
User] M1[山田Membership] M2[佐藤Membership] RA1[Tenant Admin Assignment] RA2[Sales Editor Assignment] RA3[Development Viewer Assignment] R1[Tenant Administrator] R2[Workspace Editor] R3[Workspace Viewer] C[株式会社XYZ
Resource] D[システム構成設計書
Resource] O -->|usesTenant| T T -->|containsWorkspace| W1 T -->|containsWorkspace| W2 T -->|containsTeam| TM1 T -->|containsTeam| TM2 T -->|containsTeam| TM3 U1 --> M1 U2 --> M2 M1 --> T M2 --> T M1 --> TM1 M1 --> TM3 M2 --> TM2 M2 --> TM3 TM1 -->|canAccess| W1 TM2 -->|canAccess| W2 TM3 -->|canAccess| W1 TM3 -->|canAccess| W2 M1 --> RA1 M1 --> RA2 M1 --> RA3 RA1 --> R1 RA1 --> T RA2 --> R2 RA2 --> W1 RA3 --> R3 RA3 --> W2 W1 --> C W2 --> D


23. SPARQLで問い合わせる

オントロジーができたら、どのような質問に答えられるかを確認します。

23.1 Tenantが持つWorkspaceを取得する

PREFIX saas: <https://example.com/ontology/saas#>

SELECT ?workspace ?workspaceName
WHERE {
    <https://example.com/data/tenant-abc>
        saas:containsWorkspace ?workspace .

    ?workspace saas:name ?workspaceName .
}

結果のイメージは次のとおりです。

営業Workspace
開発Workspace
管理Workspace

23.2 山田太郎が所属するTeamを取得する

PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex:   <https://example.com/data/>

SELECT ?team ?teamName
WHERE {
    ex:membership-yamada-abc
        saas:memberOfTeam ?team .

    ?team saas:name ?teamName .
}

結果は次のようになります。

営業Team
プロジェクトAlpha Team

23.3 山田太郎がTeam経由でアクセスできるWorkspace

PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex:   <https://example.com/data/>

SELECT DISTINCT ?workspace ?workspaceName
WHERE {
    ex:membership-yamada-abc
        saas:memberOfTeam ?team .

    ?team saas:canAccessWorkspace ?workspace .

    ?workspace saas:name ?workspaceName .
}

結果のイメージは次のとおりです。

営業Workspace
開発Workspace

営業Teamから営業Workspaceへアクセスでき、プロジェクトAlpha Teamから営業・開発の両Workspaceへアクセスできるためです。


23.4 山田太郎が営業Workspaceで持つRole

PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex:   <https://example.com/data/>

SELECT ?role ?roleName
WHERE {
    ?assignment a saas:RoleAssignment ;
        saas:assignedTo ex:membership-yamada-abc ;
        saas:appliesToWorkspace ex:workspace-sales ;
        saas:assignedRole ?role .

    ?role saas:name ?roleName .
}

結果は次のようになります。

Workspace Editor

23.5 山田太郎が営業Workspaceで利用できるPermission

PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex:   <https://example.com/data/>

SELECT DISTINCT ?permission ?permissionName
WHERE {
    ?assignment a saas:RoleAssignment ;
        saas:assignedTo ex:membership-yamada-abc ;
        saas:appliesToWorkspace ex:workspace-sales ;
        saas:assignedRole ?role .

    ?role saas:grantsPermission ?permission .

    ?permission saas:name ?permissionName .
}
ORDER BY ?permissionName

結果のイメージは次のとおりです。

resource.create
resource.read
resource.update
workspace.read

23.6 Resourceが属するTenantを取得する

Resourceは直接Tenantを参照せず、Workspaceを経由しています。

PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex:   <https://example.com/data/>

SELECT ?tenant ?tenantName
WHERE {
    ex:customer-xyz
        saas:belongsToWorkspace ?workspace .

    ?workspace
        saas:workspaceBelongsToTenant ?tenant .

    ?tenant saas:name ?tenantName .
}

結果は次のようになります。

株式会社ABCテナント

24. プロパティチェーンによる推論

OWLには、複数の関係をつなぎ、新しい関係を推論するプロパティチェーンがあります。

たとえば、

Resource belongsToWorkspace Workspace
Workspace workspaceBelongsToTenant Tenant

という2つの関係から、

Resource resourceBelongsToTenant Tenant

を推論できます。

Turtleでは、次のように定義できます。

saas:resourceBelongsToTenant a owl:ObjectProperty ;
    rdfs:domain saas:Resource ;
    rdfs:range saas:Tenant ;
    rdfs:label "resource belongs to tenant"@en ,
               "リソースがテナントに属する"@ja ;
    owl:propertyChainAxiom (
        saas:belongsToWorkspace
        saas:workspaceBelongsToTenant
    ) .

これにより、推論エンジンが動作する環境では、明示的に次を書かなくても、

ex:customer-xyz
    saas:resourceBelongsToTenant ex:tenant-abc .

という関係を導出できます。

同じように、Membershipが所属するTeamから、そのMembershipがアクセス可能なWorkspaceを推論することもできます。

saas:membershipCanAccessWorkspace a owl:ObjectProperty ;
    rdfs:domain saas:Membership ;
    rdfs:range saas:Workspace ;
    owl:propertyChainAxiom (
        saas:memberOfTeam
        saas:canAccessWorkspace
    ) .

すると、

Membership
→ memberOfTeam
→ Team
→ canAccessWorkspace
→ Workspace

から、

Membership
→ membershipCanAccessWorkspace
→ Workspace

を導出できます。


25. 推論できることと、許可してよいことは違う

ここで注意が必要です。オントロジー上、

山田太郎のMembership
→ Team経由で営業Workspaceにアクセス可能

と推論できたとしても、それだけで実際のアクセスを許可してよいとは限りません。

実際の認可では、さらに次の条件が必要になることがあります。

  • Membershipが有効である
  • Teamが有効である
  • Workspaceが停止されていない
  • RoleAssignmentの有効期限が切れていない
  • 必要なPermissionを持っている
  • 契約上その機能が利用可能である
  • Resource自体に追加制限がない
  • IPアドレス制限に違反していない
  • MFAを完了している

つまり、

関係が存在する

ことと、

その操作を今この瞬間に許可する

ことは別です。

オントロジーは認可判断の材料を整理できますが、認可エンジンそのものとは限りません。この境界を曖昧にすると、便利そうな知識グラフが、いつの間にか危険なアクセス制御システムになってしまいます。


26. SHACLによる最低限の検証

OWLは意味や推論を表現するのに適していますが、入力データの妥当性検証にはSHACLが向いています。

たとえば、Workspaceには必ず1つのTenantが必要だとします。

@prefix sh:   <http://www.w3.org/ns/shacl#> .
@prefix saas: <https://example.com/ontology/saas#> .
@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> .

saas:WorkspaceShape a sh:NodeShape ;
    sh:targetClass saas:Workspace ;

    sh:property [
        sh:path saas:workspaceBelongsToTenant ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class saas:Tenant ;
        sh:message
            "Workspaceは必ず1つのTenantに所属しなければなりません。"@ja
    ] ;

    sh:property [
        sh:path saas:name ;
        sh:minCount 1 ;
        sh:datatype xsd:string ;
        sh:message
            "Workspaceには名称が必要です。"@ja
    ] .

Membershipにも、UserとTenantがそれぞれ1つ必要です。

saas:MembershipShape a sh:NodeShape ;
    sh:targetClass saas:Membership ;

    sh:property [
        sh:path saas:belongsToUser ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class saas:User ;
        sh:message
            "Membershipは必ず1つのUserに属する必要があります。"@ja
    ] ;

    sh:property [
        sh:path saas:membershipBelongsToTenant ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class saas:Tenant ;
        sh:message
            "Membershipは必ず1つのTenantに属する必要があります。"@ja
    ] .

RoleAssignmentには、割当て対象とRoleが必要です。

saas:RoleAssignmentShape a sh:NodeShape ;
    sh:targetClass saas:RoleAssignment ;

    sh:property [
        sh:path saas:assignedTo ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class saas:Membership
    ] ;

    sh:property [
        sh:path saas:assignedRole ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class saas:Role
    ] ;

    sh:xone (
        [
            sh:property [
                sh:path saas:appliesToTenant ;
                sh:minCount 1 ;
                sh:maxCount 1
            ] ;
            sh:property [
                sh:path saas:appliesToWorkspace ;
                sh:maxCount 0
            ]
        ]
        [
            sh:property [
                sh:path saas:appliesToWorkspace ;
                sh:minCount 1 ;
                sh:maxCount 1
            ] ;
            sh:property [
                sh:path saas:appliesToTenant ;
                sh:maxCount 0
            ]
        ]
    ) .

このShapeでは、RoleAssignmentの適用先を、

TenantまたはWorkspaceのどちらか一方

に限定しています。

両方を同時に指定したり、どちらも指定しなかったりすると、検証エラーになります。


27. TeamとWorkspaceが同じTenantに属するかを検証する

TeamからWorkspaceへのアクセスを設定する場合、両者が同じTenantに所属していることを確認したくなります。たとえば、Tenant AのTeamが、誤ってTenant BのWorkspaceへアクセスできてはいけません。

Tenant A
└── Team A

Tenant B
└── Workspace B

Team A canAccess Workspace B

これは、典型的なクロステナント不整合であり、単純なRDFSだけでは十分に検出できません。SHACL-SPARQLを使うと、次のような制約を書けます。

saas:TeamWorkspaceTenantConsistencyShape
    a sh:NodeShape ;
    sh:targetClass saas:Team ;

    sh:sparql [
        a sh:SPARQLConstraint ;

        sh:message
            "TeamがアクセスするWorkspaceは、Teamと同じTenantに所属する必要があります。"@ja ;

        sh:select """
            PREFIX saas: <https://example.com/ontology/saas#>

            SELECT $this
            WHERE {
                $this
                    saas:teamBelongsToTenant ?teamTenant ;
                    saas:canAccessWorkspace ?workspace .

                ?workspace
                    saas:workspaceBelongsToTenant ?workspaceTenant .

                FILTER (?teamTenant != ?workspaceTenant)
            }
        """
    ] .

この制約は、B2B SaaSでは非常に重要です。マルチテナントシステムで最も避けるべき問題の一つが、他TenantのデータやWorkspaceを誤って参照することだからです。


28. MembershipとTeamのTenant整合性

Membershipが所属するTeamも、同じTenantに属している必要があります。

saas:MembershipTeamTenantConsistencyShape
    a sh:NodeShape ;
    sh:targetClass saas:Membership ;

    sh:sparql [
        a sh:SPARQLConstraint ;

        sh:message
            "Membershipが所属するTeamは、Membershipと同じTenantに属する必要があります。"@ja ;

        sh:select """
            PREFIX saas: <https://example.com/ontology/saas#>

            SELECT $this
            WHERE {
                $this
                    saas:membershipBelongsToTenant ?membershipTenant ;
                    saas:memberOfTeam ?team .

                ?team
                    saas:teamBelongsToTenant ?teamTenant .

                FILTER (?membershipTenant != ?teamTenant)
            }
        """
    ] .

このようなTenant整合性制約は、今後追加するほぼすべての概念で必要になります。

  • Membership
  • Team
  • Workspace
  • Resource
  • RoleAssignment
  • API Client
  • Audit Event
  • Usage Record
  • Entitlement

B2B SaaSのオントロジーでは、各概念の意味だけでなく、

そのインスタンスがどのTenant文脈に属するか

を常に意識する必要があります。


29. 今回の最小関係一覧

主語関係目的語意味
OrganizationhasSubOrganizationOrganization下位組織を持つ
OrganizationusesTenantTenant組織がTenantを利用する
TenantcontainsWorkspaceWorkspaceTenantがWorkspaceを含む
WorkspaceworkspaceBelongsToTenantTenantWorkspaceの所属Tenant
TenantcontainsTeamTeamTenantがTeamを含む
TeamteamBelongsToTenantTenantTeamの所属Tenant
MembershipmemberOfTeamTeamMembershipがTeamに参加する
TeamcanAccessWorkspaceWorkspaceTeamがWorkspaceにアクセスする
WorkspacecontainsResourceResourceWorkspaceがResourceを含む
ResourcebelongsToWorkspaceWorkspaceResourceの所属Workspace
MembershiphasRoleAssignmentRoleAssignmentMembershipにRole付与がある
RoleAssignmentassignedRoleRole付与されるRole
RoleAssignmentappliesToTenantTenantRoleのTenant適用範囲
RoleAssignmentappliesToWorkspaceWorkspaceRoleのWorkspace適用範囲
RolegrantsPermissionPermissionRoleがPermissionを付与する

最小関係なのに、すごい量になっちゃいましたね。。。


30. 今回のモデルで答えられる質問

Organization、Workspace、Team、RoleAssignmentを追加したことで、次のような質問に答えられます。

Organizationに関する質問

株式会社ABCは、どのTenantを利用しているか。

このTenantを利用するOrganizationはどれか。

営業本部の上位Organizationは何か。

株式会社ABCの下位Organizationは何か。

Workspaceに関する質問

Tenant ABCには、どのWorkspaceがあるか。

営業Workspaceには、どのResourceがあるか。

このResourceは、最終的にどのTenantに属するか。

Teamに関する質問

山田太郎は、どのTeamに参加しているか。

プロジェクトAlpha Teamには、誰が所属しているか。

このTeamは、どのWorkspaceへアクセスできるか。

Roleに関する質問

山田太郎は、営業WorkspaceでどのRoleを持つか。

山田太郎は、開発WorkspaceでどのPermissionを持つか。

Tenant全体の管理者は誰か。

このRoleAssignmentは、いつ付与されたか。

この、「質問に答えられるようになる」というところが、オントロジーのすごいところですね!


31. 今回のモデルのまとめ

今回の内容を最も単純にまとめると、次のようになります。

Organizationは、現実世界の組織である。

Tenantは、SaaS上の契約・管理・分離境界である。

Workspaceは、Tenant内の共同作業空間である。

Teamは、Tenant内のMembershipの集合である。

Resourceは、Workspaceに属する。

RoleAssignmentは、誰に、どのRoleを、
どの範囲で付与したかを表す。

図にすると、次の形です。

graph LR
    Organization --> Tenant
    Tenant --> Workspace
    Workspace --> Resource

    User --> Membership
    Membership --> Tenant
    Membership --> Team
    Team --> Workspace

    Membership --> RoleAssignment
    RoleAssignment --> Role
    RoleAssignment --> Tenant
    RoleAssignment --> Workspace
    Role --> Permission


次回予告

ここまでで、

誰がTenantに所属しているか

誰がどのWorkspaceへアクセスできるか

誰がどのRoleとPermissionを持つか

を表現できるようになりました。

しかし、まだ別の問題が残っています。

たとえば、山田太郎さんがanalytics.executeというPermissionを持っているとしても、契約しているPlanがAnalytics機能を含んでいなければ、その機能は使えません。

Permissionがある
しかし
契約上、その機能が提供されていない

反対に、Tenantの契約上は機能が提供されていても、山田太郎さんにPermissionがなければ利用できません。

契約上は利用可能
しかし
そのUserには操作権限がない

ここには、異なる2種類の「できる」があります。

Permission
= そのUserが操作してよいか

Entitlement
= そのTenantに機能が提供されているか

次回は、次の概念を追加します。

Feature
Entitlement
Limit
Quota
Usage

第3回テーマは、

権限があっても使えない? FeatureとEntitlementで「契約上使える機能」を表現する

にしようかなと思っております。

RoleとPermissionが人を中心とした認可を表すのに対し、FeatureとEntitlementはTenantを中心とした提供条件を表します。この2つを分けることで、料金プラン、オプション機能、利用上限、無料トライアル、β機能、個別契約をきれいに表現できるようになります。

いきなり第2回から、モリモリの内容ですが、ぜひこの後も一緒に勉強していきましょう!

まだまだ、第10回くらいまで続く予定ですので、お楽しみに!

アディオス!