
SaaSを題材にしたオントロジーを作ってみようシリーズ #3 どの機能が使えるのか?
オッス!おらやぎぃ!
第3回目になりました!
ここまで読んでいただいている方は、きっとSaaSというものの構造についてかなり詳しくなっているのではないでしょうか?!
第2回は、テナントをベースとしたさらなる深掘りをしてみました。
第3回は、そのテナントがどんな機能を使えるのか?使っても良いのか?というのを考えていきたいと思います。
よくあるSaaSは、料金プランやオプションなどがあって、契約しているプランによって、テナントごとに使える機能や量が決まっていたりすると思います。それをどのように表現していくのか?を考えていきたいと思います。
0. 権限があっても使えない?
FeatureとEntitlementで「契約上使える機能」を表現する
前回までに、B2B SaaSの利用構造と権限構造を作りました。
Organization
└── usesTenant
└── Tenant
├── Membership
│ └── User
│
├── Workspace
│ └── Resource
│
├── Team
│ └── Membership
│
├── RoleAssignment
│ ├── Membership
│ ├── Role
│ │ └── Permission
│ └── Scope
│ ├── Tenant
│ └── Workspace
│
└── Subscription
└── Plan
このモデルを使えば、次のようなことを表現できます。
山田太郎は、株式会社ABCテナントに所属している。
山田太郎は、営業WorkspaceのEditorである。
Editor Roleは、resource.updateというPermissionを持つ。
ここまでを見ると、山田太郎さんがResourceを更新できるかどうかは、Permissionだけで判定できそうに見えますが、実際のB2B SaaSでは、それだけでは足りません。
たとえば、SaaSに「AIによる文章生成機能」があるとします。
山田太郎さんのRoleには、次のPermissionが含まれています。
ai.generate
それでも、株式会社ABCがAI機能を含まないStandard Planを契約していたら、AI文章生成は利用できないでしょう。
山田太郎はAI生成を操作してよい。
しかし、
株式会社ABCにはAI生成機能が提供されていない。
反対の状況もあります。
株式会社ABCがAI機能を含むEnterprise Planを契約していたとしても、山田太郎さんにai.generateというPermissionがなければ、山田太郎さんはその機能を利用できません。
株式会社ABCにはAI生成機能が提供されている。
しかし、
山田太郎にはAI生成を実行する権限がない。
ここには、異なる2種類の「できる」があります。
Permission
= その人が操作してよいか
Entitlement
= そのTenantに、その機能が提供されているか
今回は、この違いを表現するために、次の2つの概念を追加します。
Feature
Entitlement
1. まず、何が問題なのか
多くのSaaSでは、料金プランによって利用できる機能が変わります。
たとえば、次のような料金体系があるとします。
| Feature | Free | Standard | Enterprise |
|---|---|---|---|
| 基本的なデータ管理 | ○ | ○ | ○ |
| CSVエクスポート | × | ○ | ○ |
| APIアクセス | × | ○ | ○ |
| SAML SSO | × | × | ○ |
| 監査ログ | × | × | ○ |
| AI文章生成 | × | オプション | ○ |
この表には、少なくとも次の3種類の概念が含まれています。
Plan
Feature
Tenantごとの利用可否
Planは、SaaS提供者が販売する標準商品です。
Standard Plan
Enterprise Plan
Featureは、SaaSが提供する機能です。
CSVエクスポート
APIアクセス
SAML SSO
監査ログ
AI文章生成
そして、実際に特定のTenantがそのFeatureを利用できるかどうかを表すのがEntitlementです。
株式会社ABCテナントは、
AI文章生成Featureを利用できる。
この「利用できる」という関係を、単純なプロパティではなく、独立した概念として扱います。
2. Featureとは何か
Featureを、次のように定義します。
Feature
= SaaSが提供可能な、識別可能な機能・能力
Featureの例には、次のようなものがあります。
- CSVエクスポート
- PDF出力
- APIアクセス
- SAML SSO
- SCIM連携
- 監査ログ
- AI文章生成
- 高度な検索
- ワークフロー承認
- カスタムRole
- 外部共有
- データ保持期間の設定
- IPアドレス制限
- 独自ドメイン
- ホワイトラベル
- 優先サポート
Featureは、「画面上のボタン」と同じとは限りません。たとえば、「SAML SSO」というFeatureには、複数の画面やAPIが関係するかもしれません。
SAML SSO Feature
├── IdPメタデータの登録
├── SPメタデータの取得
├── SSOテスト
├── SSO強制設定
└── 証明書の更新
逆に、画面上に1つのボタンがあっても、それが独立したFeatureとして管理されるとは限りません。
Featureとは、単なるUI部品ではなく、
商品・契約・提供制御の単位として識別したい能力
です。
3. FeatureはResourceなのか
ここで、少し立ち止まって考えてみます。
前回までに、SaaS上の業務対象をResourceとして表現しました。
顧客
商談
タスク
設計書
監査イベント
では、FeatureもResourceなのでしょうか。
この連載では、FeatureとResourceは別の概念として扱います。
Resource
= TenantやWorkspaceが管理する業務データ
Feature
= SaaS提供者が提供する機能・能力
たとえば、「顧客CSVエクスポート」というFeatureがあったとしても、CSVエクスポートそのものは顧客業務データではありません。
Customer
= Resource
CSV Export
= Feature
FeatureはResourceに対して何らかの操作を提供することがあります。
CSV Export Feature
→ Customer Resourceを出力する
しかし、FeatureとResourceは役割が異なります。
| 概念 | 例 | 主な所有・定義主体 |
|---|---|---|
| Resource | 顧客、商談、タスク | Tenant、Workspace |
| Feature | CSV出力、SSO、AI生成 | SaaS提供者 |
4. FeatureはPermissionなのか
FeatureとPermissionも、よく混同されます。
たとえば、
Feature: CSVエクスポート
Permission: customer.export
は、とても似ています。
しかし、意味は異なります。
Feature
= SaaSとして、その能力を提供できるか
Permission
= その利用者が、その操作をしてよいか
Featureは商品・契約側の概念であり、Permissionは認可・アクセス制御側の概念です。
たとえば、株式会社ABCがCSVエクスポートFeatureを利用できるとしても、すべてのUserがCSVを出力できるとは限りません。
株式会社ABCテナント
→ CSVエクスポートFeatureが有効
山田太郎
→ customer.export Permissionを持つ
佐藤花子
→ customer.export Permissionを持たない
この場合、山田太郎さんはCSVを出力できますが、佐藤花子さんは出力できません。
つまり、一般的な操作可否は次のように考えられます。
操作可能
=
TenantがFeatureを利用可能
かつ
利用者がPermissionを持つ
より厳密には、MembershipやRoleAssignmentの有効状態、適用Scopeなども確認します。
操作可能
=
有効なMembershipがある
かつ
対象Scopeで必要なPermissionを持つ
かつ
対象TenantにFeatureの有効なEntitlementがある
5. FeatureはPlanに直接結び付ければよいのか
単純な料金体系であれば、PlanとFeatureを直接結び付けることもできます。
Enterprise Plan
→ includesFeature
→ SAML SSO
この構造は自然です。
graph LR
P[Enterprise Plan]
F[SAML SSO Feature]
P -->|includesFeature| F
しかし、これだけでは、実際のTenantごとの状態を十分に表現できません。
たとえば、次のようなケースがあります。
- Standard Planだが、AI機能を追加契約している
- Enterprise Planだが、監査ログだけ一時的に停止されている
- 特定Tenantだけβ機能を提供している
- 障害対応として一時的に上位機能を開放した
- 契約交渉により、通常のPlanにない機能を個別提供した
- 無料トライアル期間中だけSAML SSOを使える
- 旧料金プランのTenantだけ、廃止済み機能を継続利用できる
- 契約は有効だが、法務確認が終わるまでAI機能を無効化している
こうした状態は、
Plan includesFeature Feature
だけでは表現できません。
Planは標準商品ですが、実際にTenantへ何が提供されているかは、個別契約や運用状況によって変わります。
そこで、Entitlementという概念を導入します。
6. Entitlementとは何か
Entitlementは、日本語に訳しにくい言葉です。直訳すると、
- 権利
- 資格
- 利用権
- 受給資格
- 正当な権原
などになります。
B2B SaaSの文脈では、次のように定義すると分かりやすいでしょう。
Entitlement
= 特定のTenantに、特定のFeatureを提供する権利・資格・状態
もう少し平たく言えば、
このTenantは、このFeatureを使える
という事実です。
たとえば、
株式会社ABCテナントは、
CSVエクスポートFeatureを利用できる。
という事実を、Entitlementとして表します。
Entitlement
├── beneficiary: 株式会社ABCテナント
├── feature: CSVエクスポート
└── status: active
ここではbeneficiaryを「権利を受ける主体」という意味で使っています。
この連載では、最初はEntitlementの対象をTenantに限定します。
Entitlementの対象
= Tenant
将来的には、Organization、Workspace、User、API Clientなどを対象にする可能性もありますが、最初から広げると意味が曖昧になります。
まずは、
Tenantに対するFeature提供
として固定します。
7. なぜEntitlementを中間概念にするのか
単純に次の関係を使うこともできます。
Tenant canUseFeature Feature
たとえば、
ex:tenant-abc
saas:canUseFeature ex:feature-csv-export .
これは簡単で、分かりやすい表現です。
しかし、実際のFeature提供には、さまざまな付随情報があります。
- 有効か無効か
- いつから有効か
- いつまで有効か
- どの契約に基づくか
- どのPlanから継承されたか
- 追加オプションとして付与されたか
- 手動で付与されたか
- 無料トライアルなのか
- β提供なのか
- 誰が有効化したか
- なぜ停止されたか
単純な、
Tenant canUseFeature Feature
という関係だけでは、これらを表現できません。
そこで、関係そのものを独立した概念にします。
Tenant
→ Entitlement
→ Feature
これは、前回までに登場したMembershipやRoleAssignmentと同じ考え方です。
User
→ Membership
→ Tenant
Membership
→ RoleAssignment
→ Role
Tenant
→ Entitlement
→ Feature
関係に属性や履歴が必要になったら、関係を独立した概念にする。これは、B2B SaaSオントロジーでは何度も登場する重要なパターンです。
8. FeatureとEntitlementの関係
Featureは、SaaS提供者が定義します。
Entitlementは、Tenantごとに発生します。
Feature
= 提供可能な機能の定義
Entitlement
= 特定Tenantへの提供状態
たとえば、SaaS全体には1つの「SAML SSO Feature」が存在します。
Feature:
SAML SSO
このFeatureに対して、Tenantごとに別々のEntitlementが作られます。
Entitlement 1
├── Tenant: 株式会社ABC
├── Feature: SAML SSO
└── status: active
Entitlement 2
├── Tenant: 株式会社XYZ
├── Feature: SAML SSO
└── status: inactive
Entitlement 3
├── Tenant: 株式会社DEF
├── Feature: SAML SSO
└── status: trial
Featureは1つですが、EntitlementはTenantごとに異なります。
graph TD
F[SAML SSO
Feature]
E1[ABC SSO Entitlement
active]
E2[XYZ SSO Entitlement
inactive]
E3[DEF SSO Entitlement
trial]
T1[株式会社ABC
Tenant]
T2[株式会社XYZ
Tenant]
T3[株式会社DEF
Tenant]
T1 --> E1
E1 --> F
T2 --> E2
E2 --> F
T3 --> E3
E3 --> F
9. Plan・Subscription・Entitlementの違い
ここで、Plan、Subscription、Entitlementの違いを整理します。
9.1 Plan
Plan
= SaaS提供者が定義する標準商品
例:
Free
Standard
Professional
Enterprise
9.2 Subscription
Subscription
= TenantによるPlanの個別契約
例:
株式会社ABCのEnterprise Plan契約
9.3 Entitlement
Entitlement
= Tenantに対する個別Featureの提供状態
例:
株式会社ABCテナントに、
SAML SSO Featureが提供されている。
図にすると、次のようになります。
graph LR
T[Tenant]
S[Subscription]
P[Plan]
E[Entitlement]
F[Feature]
T -->|hasSubscription| S
S -->|basedOnPlan| P
T -->|hasEntitlement| E
E -->|entitlesFeature| F
E -->|derivedFrom| S
PlanとFeatureを商品定義として結び付け、SubscriptionとEntitlementをTenantごとの実態として結び付けます。
商品定義側
Plan → Feature
Tenant個別側
Tenant → Subscription → Plan
Tenant → Entitlement → Feature
10. PlanからFeatureを直接推論しない理由
ここは少し重要な設計判断です。
たとえば、Enterprise PlanがSAML SSOを含むなら、
株式会社ABC
→ Enterprise Planを契約
→ SAML SSOを利用可能
と自動的に推論したくなります。
概念的には自然ですが、実運用では注意が必要です。
SubscriptionがEnterprise Planに基づいていたとしても、次のようなケースがあります。
- Subscriptionがまだ開始前である
- Subscriptionが停止中である
- 支払い遅延により一部機能が停止されている
- SAML SSOの初期設定が未完了である
- 特定地域では提供対象外である
- セキュリティ審査が完了していない
- 管理者が明示的に機能を無効化した
- 一時的な障害対応で停止している
したがって、
Planに含まれる
ことと、
現在そのTenantで利用可能
であることは、完全には同じではありません。
この連載では、次のように分けます。
Plan includesFeature Feature
= 商品設計上、そのPlanにFeatureが含まれる
Entitlement status active
= 特定Tenantに、現在そのFeatureが提供されている
最終的な利用可否では、Entitlementを参照します。PlanはEntitlementを生成する根拠にはなりますが、Entitlementそのものではありません。
11. 小休止:プランに含まれる朝食は実際に食べられるのか?
少し横道にそれますが、PlanとEntitlementの違いは、ホテルの宿泊プランにたとえることができます。
たとえば、「朝食付きプラン」という商品があります。
Plan
= 朝食付き宿泊プラン
このプランには、朝食というサービスが含まれています。
Plan includesFeature 朝食
しかし、実際の宿泊者が今朝、朝食を利用できるかどうかは、もう少し具体的な状態に依存します。
- 宿泊日は今日なのか
- 朝食券は有効か
- 朝食時間内か
- 予約がキャンセルされていないか
- すでにチェックアウトしていないか
メニューや商品説明に書かれていることと、個別の顧客が現時点で利用できることは違います。
Plan
= メニューや商品設計
Entitlement
= 個別顧客に発生している利用資格
もちろん、SaaSはホテルではありませんが、
商品に含まれていること
と、
個別顧客が現在利用できること
を分ける感覚は、よく似ています。
12. Featureの粒度をどう決めるか
Featureを設計するとき、難しいのは粒度です。
たとえば、「監査ログ」を1つのFeatureにすることもできます。
audit-log
一方で、細かく分けることもできます。
audit-log.view
audit-log.export
audit-log.retention
audit-log.external-streaming
どちらが正しいのでしょうか。絶対的な正解はありません。
Featureは、
商品や提供条件として、独立して制御したい単位
で切るのが基本です。
たとえば、次の条件なら別Featureにする意味があります。
- 別料金で販売する
- Planによって提供可否が異なる
- Tenantごとに有効・無効を切り替える
- 段階的にリリースする
- 法的・地域的な提供制限がある
- β機能として一部顧客だけに提供する
- 障害時に単独で停止したい
逆に、常に一緒に提供され、単独で制御する必要がないものを細かく分けすぎると、Feature管理が複雑になります。
細かすぎるFeature
→ Entitlementが大量に増える
→ 商品設計が読みにくくなる
→ 判定ロジックが複雑になる
Featureは、画面の数やAPIの数ではなく、
契約・商品・提供制御上の意味
で切るのがよいでしょう。
13. Featureに階層を持たせる
Featureには、親子関係を持たせられます。
たとえば、「AI機能」という大きなFeatureの下に、個別機能があるとします。
AI機能
├── AI文章生成
├── AI要約
├── AI分類
└── AI検索
これを次のように表現します。
AI文章生成
subFeatureOf
AI機能
graph TD
AI[AI機能
Feature]
G[AI文章生成
Feature]
S[AI要約
Feature]
C[AI分類
Feature]
R[AI検索
Feature]
AI --> G
AI --> S
AI --> C
AI --> R
ただし、親FeatureへのEntitlementが、すべての子Featureを自動的に有効にするかどうかは、別途ルールを決める必要があります。
AI機能を利用可能
→ AI文章生成も利用可能
と推論したい場合もあります。
しかし、
AI機能というカテゴリは有効だが、
AI検索は別契約
という商品設計もあり得ます。
そのため、Feature階層は最初から利用可否の継承を意味するとは限りません。
subFeatureOf
= 機能分類・構成上の親子関係
Entitlementの継承
= 別途定義するビジネスルール
この2つは分けて考えます。
14. Entitlementの状態
Entitlementには、状態を持たせます。最小限の例として、次の状態を考えます。
active
inactive
trial
suspended
expired
それぞれの意味は、次のようになります。
| 状態 | 意味 |
|---|---|
| active | 通常利用可能 |
| inactive | 付与情報はあるが利用不可 |
| trial | 試用として利用可能 |
| suspended | 一時停止中 |
| expired | 有効期限切れ |
ただし、trialを状態として扱うか、付与理由として扱うかには議論があります。
たとえば、
status: active
grantType: trial
と分ける設計も可能です。
こちらの方が、状態と付与経路を明確に分離できます。
status
= 現在有効かどうか
grantType
= どのような理由・経路で付与されたか
本格的なモデルでは、次のように分ける方が扱いやすいでしょう。
status:
active
inactive
suspended
expired
grantType:
plan
add_on
trial
manual
promotion
beta
migration
今回はFeatureとEntitlementに集中するため、grantTypeは簡単な文字列属性として扱います。
以降、この記事では分離後の方を採用します。 つまり、statusの取りうる値はactive・inactive・suspended・expiredの4つで、trialはgrantType側の値です。第8節でEntitlement 3の状態をtrialと書きましたが、あれはstatus: active / grantType: trialと読み替えてください。第34節のSHACLも、この4値を前提にしています。
15. Entitlementの有効期間
Entitlementには、有効期間を持たせられます。
validFrom
validUntil
たとえば、AI文章生成Featureを1か月だけ試用できるとします。
Entitlement
├── Tenant: 株式会社ABC
├── Feature: AI文章生成
├── status: active
├── grantType: trial
├── validFrom: 2026-08-01
└── validUntil: 2026-08-31
ここで注意したいのは、
statusがactiveである
ことと、
現在日時が有効期間内である
ことの両方を確認する必要がある点です。
Entitlementが利用可能かどうかは、概念的には次のように考えられます。
利用可能なEntitlement
=
statusがactive
かつ
現在日時がvalidFrom以降
かつ
validUntilがない、または現在日時がvalidUntil以前
この日時判定は、OWLの推論だけで扱うより、アプリケーションやルールエンジンで判定する方が現実的です。
16. Entitlementはどこから来たのか
Entitlementには、付与根拠があります。たとえば、次のような経路があります。
Planに含まれていた
追加契約された
無料トライアルとして付与された
営業担当者が個別に付与した
βプログラム参加Tenantとして付与された
旧契約から移行された
そこで、EntitlementからSubscriptionを参照できるようにします。
Entitlement
→ derivedFromSubscription
→ Subscription
Enterprise Plan契約に基づくSAML SSO Entitlementなら、次のようになります。
Entitlement
├── beneficiary: 株式会社ABCテナント
├── feature: SAML SSO
├── status: active
└── derivedFromSubscription:
株式会社ABCのEnterprise Subscription
ただし、すべてのEntitlementがSubscriptionに由来するとは限りません。β提供や手動付与では、Subscriptionを参照しないこともあるため、derivedFromSubscriptionは必須にはしません。
17. 今回追加するクラスと関係
今回、新たに追加するクラスは2つです。
Feature
Entitlement
中心となる関係は次のとおりです。
| 主語 | 関係 | 目的語 | 意味 |
|---|---|---|---|
| Plan | includesFeature | Feature | 標準商品にFeatureが含まれる |
| Feature | includedInPlan | Plan | includesFeatureの逆 |
| Tenant | hasEntitlement | Entitlement | TenantがFeature利用資格を持つ |
| Entitlement | entitledTenant | Tenant | Entitlementの対象Tenant |
| Entitlement | entitlesFeature | Feature | 利用可能になるFeature |
| Feature | hasFeatureEntitlement | Entitlement | entitlesFeatureの逆 |
| Entitlement | derivedFromSubscription | Subscription | Entitlementの契約上の根拠 |
| Feature | hasSubFeature | Feature | 下位Featureを持つ |
| Feature | subFeatureOf | Feature | 上位Featureに属する |
全体像は次のようになります。
graph TD
T[Tenant]
S[Subscription]
P[Plan]
E[Entitlement]
F[Feature]
SF[Sub Feature]
T -->|hasSubscription| S
S -->|basedOnPlan| P
P -->|includesFeature| F
T -->|hasEntitlement| E
E -->|entitlesFeature| F
E -->|derivedFromSubscription| S
F -->|hasSubFeature| SF
18. オントロジー定義のTurtle
ここから、FeatureとEntitlementを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#> .
#
# Classes
#
saas:Feature a owl:Class ;
rdfs:label "Feature"@en ,
"機能"@ja ;
rdfs:comment
"SaaS提供者が提供可能な、識別可能な機能または能力。"@ja .
saas:Entitlement a owl:Class ;
rdfs:label "Entitlement"@en ,
"機能利用資格"@ja ;
rdfs:comment
"特定のTenantに、特定のFeatureを提供する権利、資格または状態。"@ja .
#
# Plan and Feature
#
saas:includesFeature a owl:ObjectProperty ;
rdfs:domain saas:Plan ;
rdfs:range saas:Feature ;
rdfs:label "includes feature"@en ,
"機能を含む"@ja ;
rdfs:comment
"商品設計上、PlanにFeatureが含まれていることを表す。"@ja .
saas:includedInPlan a owl:ObjectProperty ;
owl:inverseOf saas:includesFeature ;
rdfs:domain saas:Feature ;
rdfs:range saas:Plan ;
rdfs:label "included in plan"@en ,
"プランに含まれる"@ja .
#
# Tenant and Subscription
#
# 第1回で定義したプロパティですが、今回のEntitlementから参照するため、
# また第37節・第38節のSHACLが依存するため、ここで再掲します。
#
saas:hasSubscription a owl:ObjectProperty ;
rdfs:domain saas:Tenant ;
rdfs:range saas:Subscription ;
rdfs:label "has subscription"@en ,
"契約を持つ"@ja .
saas:subscriptionBelongsToTenant a owl:ObjectProperty ;
owl:inverseOf saas:hasSubscription ;
rdfs:domain saas:Subscription ;
rdfs:range saas:Tenant ;
rdfs:label "subscription belongs to tenant"@en ,
"契約が属するテナント"@ja .
saas:basedOnPlan a owl:ObjectProperty ;
rdfs:domain saas:Subscription ;
rdfs:range saas:Plan ;
rdfs:label "based on plan"@en ,
"プランに基づく"@ja .
#
# Tenant and Entitlement
#
saas:hasEntitlement a owl:ObjectProperty ;
rdfs:domain saas:Tenant ;
rdfs:range saas:Entitlement ;
rdfs:label "has entitlement"@en ,
"機能利用資格を持つ"@ja .
saas:entitledTenant a owl:ObjectProperty ;
owl:inverseOf saas:hasEntitlement ;
rdfs:domain saas:Entitlement ;
rdfs:range saas:Tenant ;
rdfs:label "entitled tenant"@en ,
"利用資格の対象テナント"@ja .
saas:entitlesFeature a owl:ObjectProperty ;
rdfs:domain saas:Entitlement ;
rdfs:range saas:Feature ;
rdfs:label "entitles feature"@en ,
"利用可能にする機能"@ja .
saas:hasFeatureEntitlement a owl:ObjectProperty ;
owl:inverseOf saas:entitlesFeature ;
rdfs:domain saas:Feature ;
rdfs:range saas:Entitlement ;
rdfs:label "has feature entitlement"@en ,
"この機能に対する利用資格"@ja .
#
# Entitlement source
#
saas:derivedFromSubscription a owl:ObjectProperty ;
rdfs:domain saas:Entitlement ;
rdfs:range saas:Subscription ;
rdfs:label "derived from subscription"@en ,
"契約に基づく"@ja ;
rdfs:comment
"EntitlementがどのSubscriptionを根拠として付与されたかを表す。"@ja .
#
# Feature hierarchy
#
saas:hasSubFeature a owl:ObjectProperty ;
rdfs:domain saas:Feature ;
rdfs:range saas:Feature ;
rdfs:label "has sub-feature"@en ,
"下位機能を持つ"@ja .
saas:subFeatureOf a owl:ObjectProperty ;
owl:inverseOf saas:hasSubFeature ;
rdfs:domain saas:Feature ;
rdfs:range saas:Feature ;
rdfs:label "sub-feature of"@en ,
"上位機能に属する"@ja .
#
# Datatype properties
#
saas:featureCode a owl:DatatypeProperty ;
rdfs:domain saas:Feature ;
rdfs:range xsd:string ;
rdfs:label "feature code"@en ,
"機能コード"@ja .
saas:entitlementStatus a owl:DatatypeProperty ;
rdfs:domain saas:Entitlement ;
rdfs:range xsd:string ;
rdfs:label "entitlement status"@en ,
"利用資格状態"@ja .
saas:grantType a owl:DatatypeProperty ;
rdfs:domain saas:Entitlement ;
rdfs:range xsd:string ;
rdfs:label "grant type"@en ,
"付与種別"@ja .
saas:validFrom a owl:DatatypeProperty ;
rdfs:domain saas:Entitlement ;
rdfs:range xsd:dateTime ;
rdfs:label "valid from"@en ,
"有効開始日時"@ja .
saas:validUntil a owl:DatatypeProperty ;
rdfs:domain saas:Entitlement ;
rdfs:range xsd:dateTime ;
rdfs:label "valid until"@en ,
"有効終了日時"@ja .
saas:grantedAt a owl:DatatypeProperty ;
rdfs:domain saas:Entitlement ;
rdfs:range xsd:dateTime ;
rdfs:label "granted at"@en ,
"付与日時"@ja .
saas:grantReason a owl:DatatypeProperty ;
rdfs:domain saas:Entitlement ;
rdfs:range xsd:string ;
rdfs:label "grant reason"@en ,
"付与理由"@ja .
validFromとvalidUntilには、rdfs:domain saas:Entitlementを付けています。名前は汎用的ですが、このオントロジーではEntitlement専用です。
RDFSのrdfs:domainは「このプロパティを持つものは、そのクラスである」という推論規則なので、うっかり他のクラスに使うと、推論器がそれをEntitlementだと判定してしまいます。
ex:subscription-x a saas:Subscription ;
saas:validFrom "2026-01-01T00:00:00+09:00"^^xsd:dateTime .
↓ OWL推論後
ex:subscription-x は Subscription かつ Entitlement
ContractやSubscriptionなど、他のクラスにも有効期間を持たせたくなったときは、validFromを使い回さず、contractStartやsubscriptionStartのような別名を用意してください。
19. Featureコードを持たせる理由
Featureには、人間向けの名称とは別に、安定したFeatureコードを持たせます。
ex:feature-saml-sso
saas:name "SAML SSO" ;
saas:featureCode "security.saml-sso" .
名称は、後から変更される可能性があります。
旧名称:
AI文章生成
新名称:
AIライティングアシスタント
また、多言語化されることもあります。
日本語:
AI文章生成
英語:
AI Writing Assistant
一方、アプリケーションの判定に使う識別子は、簡単には変えたくありません。
ai.text-generation
そのため、
name
= 人間が読む表示名
featureCode
= システムが参照する安定識別子
として分けます。
URIそのものを安定識別子として使う方法もありますが、アプリケーションコードや設定ファイルでは、短いFeatureコードが便利な場合があります。
20. Featureの具体的なインスタンス
株式会社ABCが利用するプロジェクト管理SaaSを考えます。このSaaSには、次のFeatureがあります。
基本プロジェクト管理
CSVエクスポート
APIアクセス
SAML SSO
監査ログ
AI文章生成
AI要約
Turtleでは、次のように表現します。
@prefix saas: <https://example.com/ontology/saas#> .
@prefix ex: <https://example.com/data/> .
#
# Core features
#
ex:feature-project-management a saas:Feature ;
saas:name "基本プロジェクト管理" ;
saas:featureCode "core.project-management" .
ex:feature-csv-export a saas:Feature ;
saas:name "CSVエクスポート" ;
saas:featureCode "data.csv-export" .
ex:feature-api-access a saas:Feature ;
saas:name "APIアクセス" ;
saas:featureCode "integration.api-access" .
ex:feature-saml-sso a saas:Feature ;
saas:name "SAML SSO" ;
saas:featureCode "security.saml-sso" .
ex:feature-audit-log a saas:Feature ;
saas:name "監査ログ" ;
saas:featureCode "security.audit-log" .
#
# AI feature hierarchy
#
ex:feature-ai a saas:Feature ;
saas:name "AI機能" ;
saas:featureCode "ai" ;
saas:hasSubFeature
ex:feature-ai-text-generation,
ex:feature-ai-summarization .
ex:feature-ai-text-generation a saas:Feature ;
saas:name "AI文章生成" ;
saas:featureCode "ai.text-generation" ;
saas:subFeatureOf ex:feature-ai .
ex:feature-ai-summarization a saas:Feature ;
saas:name "AI要約" ;
saas:featureCode "ai.summarization" ;
saas:subFeatureOf ex:feature-ai .
21. Planに含まれるFeatureの具体例
次に、Standard PlanとEnterprise Planを定義します。
#
# Plans
#
ex:plan-standard a saas:Plan ;
saas:name "Standard Plan" ;
saas:includesFeature
ex:feature-project-management,
ex:feature-csv-export,
ex:feature-api-access .
ex:plan-enterprise a saas:Plan ;
saas:name "Enterprise Plan" ;
saas:includesFeature
ex:feature-project-management,
ex:feature-csv-export,
ex:feature-api-access,
ex:feature-saml-sso,
ex:feature-audit-log,
ex:feature-ai-text-generation,
ex:feature-ai-summarization .
これにより、商品定義として次のことを表現できます。
Standard Planには、
基本プロジェクト管理、
CSVエクスポート、
APIアクセスが含まれる。
Enterprise Planには、
それらに加えて、
SAML SSO、
監査ログ、
AI文章生成、
AI要約が含まれる。
ただし、これはまだ商品定義であり、特定Tenantで実際に利用可能かどうかは、Entitlementで表現します。
22. 株式会社ABCのSubscription
株式会社ABCテナントは、Standard Planを契約しているとします。
第1回の例では、同じテナントがProfessional Planを契約している想定でしたが、あれは第1回だけの最小例です。以降はこのStandard契約で読み進めてください(第1回のTurtleをそのまま残していると、テナントに2つのSubscriptionがぶら下がった状態になります)。
ex:subscription-abc-standard a saas:Subscription ;
saas:name "株式会社ABC Standard契約" ;
saas:status "active" ;
saas:basedOnPlan ex:plan-standard .
ex:tenant-abc a saas:Tenant ;
saas:name "株式会社ABCテナント" ;
saas:hasSubscription ex:subscription-abc-standard .
Standard Planには、次のFeatureが含まれています。
基本プロジェクト管理
CSVエクスポート
APIアクセス
そのため、株式会社ABCテナントには、これらに対応するEntitlementが作られます。
23. 株式会社ABCのEntitlement
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
#
# Entitlements derived from Standard Plan
#
ex:entitlement-abc-project-management a saas:Entitlement ;
saas:entitledTenant ex:tenant-abc ;
saas:entitlesFeature ex:feature-project-management ;
saas:derivedFromSubscription ex:subscription-abc-standard ;
saas:entitlementStatus "active" ;
saas:grantType "plan" ;
saas:grantedAt "2026-08-01T00:00:00+09:00"^^xsd:dateTime ;
saas:validFrom "2026-08-01T00:00:00+09:00"^^xsd:dateTime .
ex:entitlement-abc-csv-export a saas:Entitlement ;
saas:entitledTenant ex:tenant-abc ;
saas:entitlesFeature ex:feature-csv-export ;
saas:derivedFromSubscription ex:subscription-abc-standard ;
saas:entitlementStatus "active" ;
saas:grantType "plan" ;
saas:grantedAt "2026-08-01T00:00:00+09:00"^^xsd:dateTime ;
saas:validFrom "2026-08-01T00:00:00+09:00"^^xsd:dateTime .
ex:entitlement-abc-api-access a saas:Entitlement ;
saas:entitledTenant ex:tenant-abc ;
saas:entitlesFeature ex:feature-api-access ;
saas:derivedFromSubscription ex:subscription-abc-standard ;
saas:entitlementStatus "active" ;
saas:grantType "plan" ;
saas:grantedAt "2026-08-01T00:00:00+09:00"^^xsd:dateTime ;
saas:validFrom "2026-08-01T00:00:00+09:00"^^xsd:dateTime .
Tenant側からもEntitlementを明示できます。
ex:tenant-abc
saas:hasEntitlement
ex:entitlement-abc-project-management,
ex:entitlement-abc-csv-export,
ex:entitlement-abc-api-access .
hasEntitlementとentitledTenantは逆プロパティとして定義しているため、推論環境では片方だけを書いても、もう片方を導出できます。
ただし、「推論環境」は自動的には用意されません。たとえばpyshaclで検証する場合、オントロジーを-eで渡し、-i owlrlでOWL推論を有効にする必要があります。
pyshacl -s shapes.ttl -e ontology.ttl -i owlrl data.ttl
-iを付けない、あるいは-i rdfsにした場合、owl:inverseOfは効きません。hasEntitlementだけを書いたデータは、第34節のShape(entitledTenantが必須)で違反として報告されます。
24. Standard PlanだがAI機能を個別提供する
ここで、株式会社ABCだけにAI文章生成を無料トライアルとして提供するとします。Standard PlanにはAI文章生成は含まれていませんが、Tenant個別のEntitlementを作ることで提供できます。
ex:entitlement-abc-ai-text-generation-trial
a saas:Entitlement ;
saas:entitledTenant ex:tenant-abc ;
saas:entitlesFeature
ex:feature-ai-text-generation ;
saas:entitlementStatus "active" ;
saas:grantType "trial" ;
saas:grantReason
"Enterprise Plan導入検討のためのトライアル" ;
saas:grantedAt
"2026-08-10T09:00:00+09:00"^^xsd:dateTime ;
saas:validFrom
"2026-08-10T09:00:00+09:00"^^xsd:dateTime ;
saas:validUntil
"2099-12-31T23:59:59+09:00"^^xsd:dateTime .
このEntitlementはStandard Subscriptionに由来するものではないため、derivedFromSubscriptionを持たせていません。
これにより、次の状態を表現できます。
株式会社ABCはStandard Planを契約している。
Standard PlanにはAI文章生成は含まれない。
しかし、株式会社ABCには、
AI文章生成のTrial Entitlementがある。
したがって、トライアル期間中は利用可能である。
PlanとEntitlementを分ける大きな利点です。
25. Featureが一時停止されている例
株式会社ABCはAPIアクセスFeatureを契約上利用できますが、セキュリティ上の理由で一時的に停止されたとします。
RDFグラフは同じ主語・述語へ新しい値を書くだけでは古い値を上書きしません。activeを残したままsuspendedを追加すると、状態が2つあるデータになります。ここではSPARQL Updateで旧状態を削除してから、新状態を追加します。
PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex: <https://example.com/data/>
DELETE {
ex:entitlement-abc-api-access
saas:entitlementStatus ?oldStatus .
}
INSERT {
ex:entitlement-abc-api-access
saas:entitlementStatus "suspended" ;
saas:grantReason
"APIキー漏えいの疑いにより一時停止" .
}
WHERE {
OPTIONAL {
ex:entitlement-abc-api-access
saas:entitlementStatus ?oldStatus .
}
}
このクエリはSELECTではなくUPDATEなので、sparqlqueryコマンドでは実行できません。
$ sparqlquery data.ttl -qf update.rq -f csv
pyparsing.exceptions.ParseException: Expected {SelectQuery | ConstructQuery |
DescribeQuery | AskQuery}, found 'DELETE'
RDFLibから実行し、結果をファイルに書き戻します。
# update.py
import rdflib
g = rdflib.Graph()
g.parse("data.ttl", format="turtle")
g.update(open("update.rq").read())
g.serialize("data.ttl", format="turtle")
python update.py
この場合、
Standard PlanにはAPIアクセスが含まれる。
Subscriptionも有効である。
しかし、APIアクセスEntitlementはsuspendedである。
したがって、現在はAPIアクセスを許可しません。
このように、Entitlementを明示的に持つことで、
商品上は提供対象
しかし
運用上は一時停止
という状態を表現できます。
26. PermissionとEntitlementを組み合わせた具体例
AI文章生成を実行するには、次の2条件が必要だとします。
Tenant側:
ai.text-generation Featureの有効なEntitlement
User側:
ai.generate Permission
RoleとPermissionを次のように定義します。
ex:permission-ai-generate a saas:Permission ;
saas:name "ai.generate" .
ex:role-ai-user a saas:Role ;
saas:name "AI User" ;
saas:grantsPermission
ex:permission-ai-generate .
山田太郎さんに、営業Workspaceの範囲でAI User Roleを付与します。
ex:role-assignment-yamada-ai-user
a saas:RoleAssignment ;
saas:assignedTo
ex:membership-yamada-abc ;
saas:assignedRole
ex:role-ai-user ;
saas:appliesToWorkspace
ex:workspace-sales ;
saas:assignedAt
"2026-08-10T09:10:00+09:00"^^xsd:dateTime .
さらに、株式会社ABCテナントにはAI文章生成のTrial Entitlementがあります。
Tenant:
AI文章生成を利用可能
山田太郎:
営業Workspaceでai.generateを実行可能
したがって、山田太郎さんは営業WorkspaceでAI文章生成を利用できますが、佐藤花子さんにAI User Roleがなければ、Tenantとしては利用可能でも、佐藤花子さんは利用できません。
27. 利用可否の判定フロー
AI文章生成のようなFeatureを利用できるかは、次の順番で確認できます。
1. Userを特定する
2. 対象Tenantに対する
有効なMembershipを取得する
3. 対象WorkspaceまたはTenant Scopeで、
必要なRoleAssignmentを確認する
4. Roleが必要なPermissionを持つか確認する
5. Tenantに対象Featureの
有効なEntitlementがあるか確認する
6. Entitlementの有効期間内か確認する
7. すべて満たせば操作を許可する
概念図は次のようになります。
graph TD
U[User]
M[Membership]
RA[RoleAssignment]
R[Role]
P[Permission]
T[Tenant]
E[Entitlement]
F[Feature]
U --> M
M --> RA
RA --> R
R --> P
M --> T
T --> E
E --> F
P --> D{Permissionあり?}
F --> G{Entitlement有効?}
D -->|Yes| A{両方満たす?}
G -->|Yes| A
A -->|Yes| OK[操作許可]
最終的な判断は、次の形です。
利用者に許可されている
かつ
Tenantに提供されている
28. 全インスタンスの関係図
graph TD
T[株式会社ABC
Tenant]
S[Standard契約
Subscription]
P[Standard Plan]
E1[CSV Export Entitlement
active]
E2[API Access Entitlement
suspended]
E3[AI Text Trial Entitlement
active]
F1[CSV Export
Feature]
F2[API Access
Feature]
F3[AI Text Generation
Feature]
M[山田Membership]
RA[AI User RoleAssignment]
R[AI User Role]
PM[ai.generate Permission]
T --> S
S --> P
P -->|includesFeature| F1
P -->|includesFeature| F2
T --> E1
E1 --> F1
E1 --> S
T --> E2
E2 --> F2
E2 --> S
T --> E3
E3 --> F3
M --> T
M --> RA
RA --> R
R --> PM
この図から、次のことが分かります。
CSVエクスポート
→ Planに含まれる
→ Entitlementもactive
APIアクセス
→ Planに含まれる
→ しかしEntitlementはsuspended
AI文章生成
→ Planには含まれない
→ しかしTrial Entitlementがactive
29. SPARQLで問い合わせる
この節以降の問い合わせは、第25節の更新を適用したあとのグラフを前提にします。つまり、APIアクセスのEntitlementはsuspendedになっている状態です。まだ適用していない場合は、先に第25節のupdate.pyを実行してください。
29.1 Planに含まれるFeatureを取得する
PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex: <https://example.com/data/>
SELECT ?feature ?featureName ?featureCode
WHERE {
ex:plan-standard
saas:includesFeature ?feature .
?feature
saas:name ?featureName ;
saas:featureCode ?featureCode .
}
ORDER BY ?featureCode
結果は次のとおりです。
feature,featureName,featureCode
https://example.com/data/feature-project-management,基本プロジェクト管理,core.project-management
https://example.com/data/feature-csv-export,CSVエクスポート,data.csv-export
https://example.com/data/feature-api-access,APIアクセス,integration.api-access
以降の節では、見やすさのために主要な列だけを抜き出して示します。
これは商品定義を問い合わせています。まだ、株式会社ABCで現在利用可能かどうかを調べているわけではありません。
29.2 Tenantが持つすべてのEntitlementを取得する
PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex: <https://example.com/data/>
SELECT
?entitlement
?feature
?featureName
?status
?grantType
WHERE {
?entitlement
a saas:Entitlement ;
saas:entitledTenant ex:tenant-abc ;
saas:entitlesFeature ?feature ;
saas:entitlementStatus ?status ;
saas:grantType ?grantType .
?feature saas:name ?featureName .
}
ORDER BY ?featureName
結果のイメージは次のようになります。
| Feature | status | grantType |
|---|---|---|
| AI文章生成 | active | trial |
| APIアクセス | suspended | plan |
| CSVエクスポート | active | plan |
| 基本プロジェクト管理 | active | plan |
29.3 Tenantで有効なFeatureを取得する
まずは日時を考慮せず、状態だけで判定します。
PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex: <https://example.com/data/>
SELECT DISTINCT
?feature
?featureName
?featureCode
WHERE {
?entitlement
a saas:Entitlement ;
saas:entitledTenant ex:tenant-abc ;
saas:entitlesFeature ?feature ;
saas:entitlementStatus "active" .
?feature
saas:name ?featureName ;
saas:featureCode ?featureCode .
}
ORDER BY ?featureCode
この問い合わせでは、Entitlementがsuspendedのため、APIアクセスは取得されません。
29.4 現在時刻を考慮して有効なFeatureを取得する
SPARQLのNOW()を使って、有効期間も確認します。
PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex: <https://example.com/data/>
SELECT DISTINCT
?feature
?featureName
?featureCode
WHERE {
?entitlement
a saas:Entitlement ;
saas:entitledTenant ex:tenant-abc ;
saas:entitlesFeature ?feature ;
saas:entitlementStatus "active" .
OPTIONAL {
?entitlement saas:validFrom ?validFrom .
}
OPTIONAL {
?entitlement saas:validUntil ?validUntil .
}
FILTER (
!BOUND(?validFrom) ||
?validFrom <= NOW()
)
FILTER (
!BOUND(?validUntil) ||
NOW() <= ?validUntil
)
?feature
saas:name ?featureName ;
saas:featureCode ?featureCode .
}
ORDER BY ?featureCode
これにより、次の条件を満たすEntitlementだけを取得します。
statusがactive
validFromがない、
または現在時刻がvalidFrom以降
validUntilがない、
または現在時刻がvalidUntil以前
29.5 特定FeatureをTenantが利用できるか確認する
株式会社ABCがAI文章生成を利用可能か確認します。
PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex: <https://example.com/data/>
ASK {
?entitlement
a saas:Entitlement ;
saas:entitledTenant ex:tenant-abc ;
saas:entitlesFeature
ex:feature-ai-text-generation ;
saas:entitlementStatus "active" .
OPTIONAL {
?entitlement saas:validFrom ?validFrom .
}
OPTIONAL {
?entitlement saas:validUntil ?validUntil .
}
FILTER (
!BOUND(?validFrom) ||
?validFrom <= NOW()
)
FILTER (
!BOUND(?validUntil) ||
NOW() <= ?validUntil
)
}
利用可能であれば、結果はtrueになります。
29.6 Entitlementの付与根拠を確認する
PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex: <https://example.com/data/>
SELECT
?featureName
?grantType
?subscriptionName
?reason
WHERE {
?entitlement
a saas:Entitlement ;
saas:entitledTenant ex:tenant-abc ;
saas:entitlesFeature ?feature ;
saas:grantType ?grantType .
?feature saas:name ?featureName .
OPTIONAL {
?entitlement
saas:derivedFromSubscription ?subscription .
?subscription
saas:name ?subscriptionName .
}
OPTIONAL {
?entitlement
saas:grantReason ?reason .
}
}
ORDER BY ?featureName
この問い合わせにより、次の違いを確認できます。
CSVエクスポート
→ Standard Subscriptionに基づく
AI文章生成
→ Trialとして個別付与
30. PermissionとEntitlementの両方を確認するSPARQL
山田太郎さんが営業WorkspaceでAI文章生成を利用できるか確認します。必要な条件は、次のとおりです。
山田太郎のMembershipが、
株式会社ABCテナントに対して有効である。
そのMembershipに、
営業WorkspaceをScopeとするRoleAssignmentがある。
そのRoleがai.generate Permissionを持つ。
株式会社ABCテナントに、
AI文章生成Featureの有効なEntitlementがある。
第27節の判定フローのうち、「有効なMembershipを取得する」という手順を省略しないことが重要です。RoleAssignmentのURIを指定するだけでは、そのMembershipが停止されていないか、そもそも対象Tenantに属しているかを確認できません。
なお、ex:membership-yamada-abcやex:workspace-salesは前回のデータで定義したものなので、この問い合わせは前回のデータと今回のデータを合わせたグラフに対して実行します。
SPARQLでは次のようになります。
PREFIX saas: <https://example.com/ontology/saas#>
PREFIX ex: <https://example.com/data/>
ASK {
#
# Membership check
#
ex:membership-yamada-abc
a saas:Membership ;
saas:membershipBelongsToTenant ex:tenant-abc ;
saas:status "active" .
#
# Permission check
#
?roleAssignment
a saas:RoleAssignment ;
saas:assignedTo
ex:membership-yamada-abc ;
saas:appliesToWorkspace
ex:workspace-sales ;
saas:assignedRole ?role .
?role
saas:grantsPermission
ex:permission-ai-generate .
#
# Entitlement check
#
?entitlement
a saas:Entitlement ;
saas:entitledTenant
ex:tenant-abc ;
saas:entitlesFeature
ex:feature-ai-text-generation ;
saas:entitlementStatus "active" .
OPTIONAL {
?entitlement saas:validFrom ?validFrom .
}
OPTIONAL {
?entitlement saas:validUntil ?validUntil .
}
FILTER (
!BOUND(?validFrom) ||
?validFrom <= NOW()
)
FILTER (
!BOUND(?validUntil) ||
NOW() <= ?validUntil
)
}
この問い合わせは、PermissionとEntitlementを明確に分けています。
Role・Permission側
= 山田太郎が操作してよいか
Entitlement側
= 株式会社ABCに機能が提供されているか
両方がそろったときだけ、trueになります。
31. プロパティチェーンによる利用可能Featureの推論
Entitlementの状態や有効期間をいったん無視すれば、次の関係から、
Tenant
→ hasEntitlement
→ Entitlement
→ entitlesFeature
→ Feature
TenantとFeatureの直接関係を推論できます。
saas:hasEntitledFeature a owl:ObjectProperty ;
rdfs:domain saas:Tenant ;
rdfs:range saas:Feature ;
rdfs:label "has entitled feature"@en ,
"利用資格のある機能を持つ"@ja ;
owl:propertyChainAxiom (
saas:hasEntitlement
saas:entitlesFeature
) .
これにより、次の関係を導出できます。
ex:tenant-abc
saas:hasEntitledFeature
ex:feature-ai-text-generation .
ただし、owl:propertyChainAxiomはOWLの推論規則なので、SPARQLで問い合わせるだけでは何も導出されません。第23節と同じく、OWL-RL推論器を通す必要があります。
pyshacl -s shapes.ttl -e ontology.ttl -i owlrl data.ttl
RDFLibから直接推論する場合は、owlrlパッケージを使います。
import rdflib, owlrl
g = rdflib.Graph()
g.parse("ontology.ttl", format="turtle")
g.parse("data.ttl", format="turtle")
owlrl.DeductiveClosure(owlrl.OWLRL_Semantics).expand(g)
さらに、この推論には注意が必要です。
プロパティチェーンは、
Entitlementが存在する
ことだけを見ています。
次の条件は考慮していません。
entitlementStatusがactiveか- 有効期間内か
- Subscriptionが有効か
- Entitlementが停止されていないか
したがって、hasEntitledFeatureという名前を、
現在利用可能なFeature
という意味で使うのは危険です。
より正確には、
FeatureへのEntitlement関係が存在する
程度の意味です。
現在利用可能かどうかは、SPARQL、SHACL Rules、アプリケーションロジック、ポリシーエンジンなどで判定する方が安全です。
32. 小休止:推論は「真実」ではなく「ルールの結果」である
オントロジーの推論はとても便利ですが、推論された事実は、絶対的な真実ではありません。
与えられたデータと、
定義したルールから導かれた結果
です。
たとえば、
Tenant
→ Entitlement
→ Feature
から、
TenantはFeatureを利用可能
と推論するルールを作ったとします。
しかし、Entitlementが停止中なら、その推論は実務上正しくありません。これは推論エンジンが間違えたのではありません。
こちらが、
Entitlementが存在すれば利用可能
という粗いルールを与えたためです。
オントロジーを作るときは、
何を推論できるか
だけでなく、
その推論がどの条件を無視しているか
を意識する必要があります。
特に認可、課金、契約、セキュリティでは、単純なプロパティチェーンだけを最終判断に使わない方がよいでしょう。
33. SHACLによるFeatureの検証
Featureには、名称とFeatureコードが必要だとします。
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix saas: <https://example.com/ontology/saas#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
saas:FeatureShape
a sh:NodeShape ;
sh:targetClass saas:Feature ;
sh:property [
sh:path saas:name ;
sh:minCount 1 ;
sh:or (
[ sh:datatype xsd:string ]
[ sh:datatype rdf:langString ]
) ;
sh:message
"Featureには名称が必要です。"@ja
] ;
sh:property [
sh:path saas:featureCode ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:datatype xsd:string ;
sh:pattern "^[a-z0-9]+([._-][a-z0-9]+)*$" ;
sh:message
"Featureには有効なFeatureコードが1つ必要です。"@ja
] .
このパターンでは、たとえば次のコードを許可します。
ai
ai.text-generation
security.saml-sso
data.csv-export
大文字や空白を含むコードは許可しません。
AI Text Generation
SAML SSO
表示名ではなく、システム識別子として安定させるためです。
nameの方にsh:orを使っているのは、第19節で触れた多言語化のためです。sh:datatype xsd:stringだけにすると、言語タグ付きのリテラルが違反になります。
saas:name "AI文章生成"@ja # sh:datatype xsd:string だけだと違反になる
featureCodeは表示名ではないので、言語タグ付きを弾くsh:datatype xsd:stringのままにしています。
34. SHACLによるEntitlementの基本検証
Entitlementには、必ず1つのTenantと1つのFeatureが必要です。statusとgrantTypeは、第14節で分離した値だけを許可します(trialはstatusではなくgrantTypeの値である点に注意してください)。
saas:EntitlementShape
a sh:NodeShape ;
sh:targetClass saas:Entitlement ;
sh:property [
sh:path saas:entitledTenant ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:class saas:Tenant ;
sh:message
"Entitlementには対象Tenantが1つ必要です。"@ja
] ;
sh:property [
sh:path saas:entitlesFeature ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:class saas:Feature ;
sh:message
"Entitlementには対象Featureが1つ必要です。"@ja
] ;
sh:property [
sh:path saas:entitlementStatus ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:in (
"active"
"inactive"
"suspended"
"expired"
) ;
sh:message
"Entitlementのstatusは定義済みの値である必要があります。"@ja
] ;
sh:property [
sh:path saas:grantType ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:in (
"plan"
"add_on"
"trial"
"manual"
"promotion"
"beta"
"migration"
) ;
sh:message
"EntitlementのgrantTypeは定義済みの値である必要があります。"@ja
] ;
sh:property [
sh:path saas:validFrom ;
sh:maxCount 1 ;
sh:datatype xsd:dateTime
] ;
sh:property [
sh:path saas:validUntil ;
sh:maxCount 1 ;
sh:datatype xsd:dateTime
] .
35. 有効開始日時と終了日時の整合性
validUntilがvalidFromより前になってはいけません。誤った例は次のとおりです。
validFrom:
2026-09-01
validUntil:
2026-08-31
SHACL-SPARQLで検証できます。
なお、以下ではsh:selectの文字列の中にPREFIXを直接書いています。pyshaclはこの書き方をそのまま受け付けますが、SHACLの仕様が用意している正式な仕組みはsh:prefixesとsh:declareです。別の実装に移すときは、そちらに書き換えが必要になることがあります。
saas:EntitlementValidityPeriodShape
a sh:NodeShape ;
sh:targetClass saas:Entitlement ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message
"EntitlementのvalidUntilはvalidFrom以降でなければなりません。"@ja ;
sh:select """
PREFIX saas:
<https://example.com/ontology/saas#>
SELECT $this
WHERE {
$this
saas:validFrom ?validFrom ;
saas:validUntil ?validUntil .
FILTER (?validUntil < ?validFrom)
}
"""
] .
36. Plan由来Entitlementの契約整合性
grantTypeがplanである場合、derivedFromSubscriptionを必須にしたいとします。
grantType = plan
ならば、
derivedFromSubscriptionが必要
SHACL-SPARQLでは、次のように書けます。
saas:PlanEntitlementSubscriptionShape
a sh:NodeShape ;
sh:targetClass saas:Entitlement ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message
"grantTypeがplanのEntitlementにはderivedFromSubscriptionが必要です。"@ja ;
sh:select """
PREFIX saas:
<https://example.com/ontology/saas#>
SELECT $this
WHERE {
$this saas:grantType "plan" .
FILTER NOT EXISTS {
$this
saas:derivedFromSubscription
?subscription .
}
}
"""
] .
反対に、trialやmanualではSubscriptionがなくても構いません。
37. EntitlementとSubscriptionのTenant整合性
Entitlementの対象Tenantと、根拠となるSubscriptionのTenantは一致する必要があります。誤った状態は次のようなものです。
Entitlementの対象:
Tenant A
根拠Subscriptionの所有者:
Tenant B
これはクロステナント不整合であり、前回のTeamとWorkspaceのTenant整合性と同じように、非常に重要な制約です。
saas:EntitlementSubscriptionTenantConsistencyShape
a sh:NodeShape ;
sh:targetClass saas:Entitlement ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message
"Entitlementの対象Tenantと、根拠SubscriptionのTenantは一致する必要があります。"@ja ;
sh:select """
PREFIX saas:
<https://example.com/ontology/saas#>
SELECT $this
WHERE {
$this
saas:entitledTenant
?entitledTenant ;
saas:derivedFromSubscription
?subscription .
OPTIONAL {
?subscription
( ^saas:hasSubscription
| saas:subscriptionBelongsToTenant )
?subscriptionTenant .
}
FILTER (
!BOUND(?subscriptionTenant) ||
?entitledTenant !=
?subscriptionTenant
)
}
"""
] .
ここでは逆向きのパスを使っています。
?subscription
^saas:hasSubscription
?subscriptionTenant .
これは、次の関係を逆向きにたどっています。
Tenant
→ hasSubscription
→ Subscription
OPTIONALと!BOUNDを使っているのには理由があります。
逆向きのパスだけで書くと、Subscription側からTenantを辿れないデータが、違反として検出されません。 たとえば、Tenant hasSubscription Subscriptionが1本も書かれていない状態で、別TenantのSubscriptionを根拠にしたEntitlementを作っても、パターンが一致しないので何も報告されません。
検出したい違反が、
「そもそもパターンに当たらない」ために
黙って素通りする
これは、制約としては最悪の失敗の仕方です。そこで、OPTIONALでTenantを取りに行き、取れなかった場合も違反にします。
また、選択パス(|)で^saas:hasSubscriptionとsaas:subscriptionBelongsToTenantの両方を見ているのは、どちら向きに書かれていても辿れるようにするためです。第18節で定義したとおり、この2つは逆プロパティですが、第23節で書いたとおり、推論器を通していなければ片方からもう片方は導出されません。
38. Plan由来EntitlementのFeature整合性
grantTypeがplanの場合、Entitlement対象Featureは、根拠SubscriptionのPlanに含まれているべきです。
Entitlement
→ derivedFromSubscription
→ Subscription
→ basedOnPlan
→ Plan
→ includesFeature
→ Feature
この経路が成立するかを検証します。
saas:PlanEntitlementFeatureConsistencyShape
a sh:NodeShape ;
sh:targetClass saas:Entitlement ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message
"Plan由来のEntitlement対象Featureは、SubscriptionのPlanに含まれている必要があります。"@ja ;
sh:select """
PREFIX saas:
<https://example.com/ontology/saas#>
SELECT $this
WHERE {
$this
saas:grantType "plan" ;
saas:entitlesFeature ?feature ;
saas:derivedFromSubscription
?subscription .
?subscription
saas:basedOnPlan ?plan .
FILTER NOT EXISTS {
?plan
saas:includesFeature
?feature .
}
}
"""
] .
ただし、この制約は、自社の商品設計に合わせて採用する必要があります。
たとえば、Subscriptionに個別契約Featureを含められる場合は、
Planには含まれないが、
Subscriptionの個別契約条件には含まれる
ことがあり得ます。
その場合、grantTypeをplanではなくadd_onやmanualにするか、契約明細の概念を追加する必要があります。
今回はFeatureとEntitlementに限定するため、そこまでは進みません。
39. 同一Tenant・同一Featureの重複Entitlement
同じTenantとFeatureに対して、複数のEntitlementが存在することがあります。
Plan由来のEntitlement
追加契約由来のEntitlement
期間限定Trial Entitlement
これは、必ずしも不正ではありません。たとえば、Enterprise Plan由来のAI Featureと、追加のβ機能Entitlementが並存することがあります。しかし、同じFeatureに対して、複数のactive Entitlementがあると、どれを採用すべきか曖昧になる場合があります。
Entitlement A
├── Feature: AI文章生成
├── status: active
└── validUntil: 2026-09-01
Entitlement B
├── Feature: AI文章生成
├── status: active
└── validUntil: 2027-03-31
この場合、次の設計方針が考えられます。
方針A:重複を禁止する
Tenant × Featureごとに、
active Entitlementは最大1つ
単純で扱いやすい方法です。
方針B:重複を許可し、どれか1つが有効なら利用可能
有効なEntitlementが1つ以上あれば利用可能
複数の付与経路を履歴として残しやすくなります。
方針C:優先順位を決める
manual
add_on
plan
trial
などの優先順位を定義します。
共通オントロジーとしては、重複を許容する方が柔軟です。
最終的な利用可否は、
有効なEntitlementが1件以上存在するか
で判定できます。
ただし、Entitlementの停止方法には注意が必要で、1つを停止しても、別のactive Entitlementが残っていれば、Featureは利用可能なままです。
40. Featureの廃止
SaaSでは、Featureが廃止されることもあります。Featureそのものを削除すると、過去の契約や監査履歴が読めなくなる可能性があるため、Featureにも状態を持たせる方法があります。
active
deprecated
retired
たとえば、
saas:featureStatus a owl:DatatypeProperty ;
rdfs:domain saas:Feature ;
rdfs:range xsd:string .
ex:feature-legacy-report
a saas:Feature ;
saas:name "旧レポート機能" ;
saas:featureCode "report.legacy" ;
saas:featureStatus "retired" .
Featureがretiredであっても、過去のEntitlementは履歴として残せます。
Featureは廃止済み
しかし、
過去にどのTenantが利用できたかは残る
ただし、今回はFeatureとEntitlementの最小モデルに集中するため、Feature状態は必須要素にはしません。
41. Feature Flagとの違い
Featureという言葉から、Feature Flagを思い浮かべる人もいるでしょう。Feature Flagは、一般にソフトウェアの挙動を動的に切り替える仕組みです。
新UIを有効化する
新しい検索エンジンを使う
β版アルゴリズムを一部Userに公開する
Entitlementと似ていますが、同じではありません。
Entitlement
= 契約・商品・提供資格の観点
Feature Flag
= リリース・運用・技術制御の観点
たとえば、TenantにAI FeatureのEntitlementがあっても、障害対応でFeature FlagがOFFになっていれば、実際には利用できない場合があります。
Entitlement: active
Feature Flag: off
反対に、開発環境でFeature FlagがONでも、TenantにEntitlementがなければ、本番利用を許可すべきではありません。
概念的には、最終利用可否は次のようになることがあります。
Permission
かつ
Entitlement
かつ
Feature Flag
かつ
運用条件
今回のモデルではFeature Flagを扱いませんが、
Entitlementだけで、
あらゆる実行可否を表現しようとしない
ことは重要です。
42. 小休止:契約上使えることと、技術的に動くこと
SaaSでは、ときどき次の2つが混同されます。
契約上使ってよい
技術的に使える
本来使えないPlanなのに、APIエンドポイントへ直接アクセスすると動いてしまう。管理画面では非表示だが、URLを知っていれば開けてしまう。Feature Flagだけで画面を隠し、バックエンドではEntitlementを検証していない。
こうした実装は、単なるUI上の不具合ではありません。契約違反、情報漏えい、請求漏れ、セキュリティ事故につながる可能性があります。
オントロジー上でFeatureとEntitlementを分けておくことは、概念整理だけでなく、
どの層で何を確認すべきか
を明確にする効果があります。
フロントエンド
→ 表示制御
バックエンド
→ EntitlementとPermissionの最終検証
契約管理
→ SubscriptionとEntitlementの生成
運用管理
→ 停止・再開・個別付与
画面にボタンが見えないことは、認可ではありません。同じように、Feature FlagがOFFであることも、契約管理ではありません。
43. 今回の最小関係一覧
| 主語 | 関係 | 目的語 | 意味 |
|---|---|---|---|
| Plan | includesFeature | Feature | Planの商品構成にFeatureが含まれる |
| Feature | includedInPlan | Plan | FeatureがPlanに含まれる |
| Tenant | hasEntitlement | Entitlement | TenantがFeature利用資格を持つ |
| Entitlement | entitledTenant | Tenant | Entitlementの対象Tenant |
| Entitlement | entitlesFeature | Feature | Entitlementが利用可能にするFeature |
| Feature | hasFeatureEntitlement | Entitlement | Featureに対するEntitlement |
| Entitlement | derivedFromSubscription | Subscription | Entitlementの契約上の根拠 |
| Feature | hasSubFeature | Feature | Featureが下位Featureを持つ |
| Feature | subFeatureOf | Feature | Featureが上位Featureに属する |
| Tenant | hasEntitledFeature | Feature | hasEntitlement+entitlesFeatureから推論(第31節) |
Entitlementの根拠をたどるために、第1回で定義した次の関係も使います(第18節で再掲しました)。
| 主語 | 関係 | 目的語 | 意味 |
|---|---|---|---|
| Tenant | hasSubscription | Subscription | TenantがPlanを契約する |
| Subscription | subscriptionBelongsToTenant | Tenant | hasSubscriptionの逆 |
| Subscription | basedOnPlan | Plan | 契約が基づくPlan |
主な属性は次のとおりです。
| クラス | 属性 | 意味 |
|---|---|---|
| Feature | featureCode | 安定した機能識別子 |
| Feature | featureStatus | 機能の提供状態(第40節) |
| Entitlement | entitlementStatus | 現在の状態 |
| Entitlement | grantType | 付与経路・種別 |
| Entitlement | validFrom | 有効開始日時 |
| Entitlement | validUntil | 有効終了日時 |
| Entitlement | grantedAt | 付与日時 |
| Entitlement | grantReason | 付与理由 |
44. 今回のモデルで答えられる質問
Featureに関する質問
このSaaSには、どのFeatureが存在するか。
AI機能の下位Featureは何か。
このFeatureは、どのPlanに含まれるか。
Enterprise Planに含まれ、
Standard Planには含まれないFeatureは何か。
Entitlementに関する質問
このTenantには、
どのFeatureのEntitlementがあるか。
現在activeなEntitlementはどれか。
Trialとして付与されているFeatureは何か。
このEntitlementは、
どのSubscriptionに基づいているか。
1か月以内に期限切れになるEntitlementはどれか。
Permissionと組み合わせた質問
山田太郎は、
営業WorkspaceでAI文章生成を利用できるか。
Featureは利用可能だが、
操作Permissionを持たないUserは誰か。
Permissionは持っているが、
TenantにEntitlementがないため
利用できない機能は何か。
商品・契約差分に関する質問
Planには含まれていないが、
個別に付与されているFeatureは何か。
Planには含まれるが、
現在suspendedのFeatureは何か。
同じPlanを契約しているTenant間で、
Entitlementに差があるFeatureは何か。
45. 今回のモデルのまとめ
今回の内容を最も単純にまとめると、次のようになります。
Featureは、
SaaSが提供可能な機能・能力である。
Planは、
標準商品としてFeatureを含む。
Subscriptionは、
TenantによるPlanの個別契約である。
Entitlementは、
特定Tenantに特定Featureを提供する
権利・資格・状態である。
Permissionは、
利用者が操作してよいかを表す。
Entitlementは、
Tenantに機能が提供されているかを表す。
図にすると、次の形です。
graph LR
T[Tenant]
S[Subscription]
P[Plan]
E[Entitlement]
F[Feature]
M[Membership]
RA[RoleAssignment]
R[Role]
PM[Permission]
T --> S
S --> P
P --> F
T --> E
E --> F
E --> S
M --> T
M --> RA
RA --> R
R --> PM
そして、利用者がFeatureを操作できるかどうかは、基本的に次の組み合わせで決まります。
Tenant側:
有効なEntitlementがある
User側:
必要なPermissionがある
つまり、
利用可能
=
Entitlement
かつ
Permission
です。
46. ここまでのB2B SaaSオントロジー
第1回から今回までのモデルをまとめると、次のようになります。
Organization
└── usesTenant
└── Tenant
├── Membership
│ └── User
│
├── Workspace
│ └── Resource
│
├── Team
│ └── Membership
│
├── RoleAssignment
│ ├── Membership
│ ├── Role
│ │ └── Permission
│ └── Scope
│ ├── Tenant
│ └── Workspace
│
├── Subscription
│ └── Plan
│ └── Feature
│
└── Entitlement
├── Feature
└── Subscription
より意味を意識して読むと、次のようになります。
現実世界のOrganizationが、
SaaS上のTenantを利用する。
Userは、
Membershipを通じてTenantに参加する。
Tenantには、
WorkspaceとTeamがある。
Membershipには、
Scope付きのRoleAssignmentがある。
Roleは、
Permissionを付与する。
Tenantは、
Subscriptionを通じてPlanを契約する。
Planは、
標準商品としてFeatureを含む。
Tenantは、
Entitlementを通じて、
実際に利用可能なFeatureを持つ。
47. 次回予告!
ここまでで、
このTenantに、
このFeatureが提供されているか
を表現できるようになりました。
しかし、Featureが利用可能だとしても、まだ次の問題が残っています。
何回まで使えるのか。
何人まで使えるのか。
何GBまで保存できるのか。
APIを月に何回実行できるのか。
AI生成を月に何回使えるのか。
FeatureとEntitlementが表すのは、基本的に、
利用できるか、できないか
です。
しかし、現実のSaaS契約には、
利用できるが、上限がある
という状態が大量にあります。
その先では、Limit、Quota、Usageなどの概念が必要になりますが、それらはFeatureやEntitlementとは別の問題です。
まず今回の段階では、次の区別を確実にします。
Feature
= どのような機能が存在するか
Entitlement
= そのTenantに機能が提供されているか
Permission
= その人が機能を操作してよいか
この3つを分けて表現できれば、B2B SaaSの料金プラン、オプション契約、トライアル、β提供、個別開放、機能停止を、かなり自然に扱えるようになります。
今回も巨大な記事になってしまいましたが、まだまだ続きます! げんなりしないで、一緒に頑張って学んでいきましょう!
次回もまた絶対見てくれよな!