Advanced (更新: 2026/7/19)

SaaSのCosmos DBスキーマをClaude Codeで見直す: テナント分離と問い合わせ履歴

SaaS向けにCosmos DBのtenantId、partition key、問い合わせ履歴、mask、保持期間を点検します。

SaaSのCosmos DBスキーマをClaude Codeで見直す: テナント分離と問い合わせ履歴

SaaSのサポート画面で、A社の問い合わせ履歴を開いたつもりなのに、検索結果にB社の請求メモが混ざる。画面には顧客名、メール、契約プラン、障害時のやり取り、担当者コメントが並びます。原因を追うと、Cosmos DBのcontainerは1つ、partition keyはid、queryはtypeだけで絞っていました。動くけれど、テナント境界が見えない作りです。

この記事では、SaaS運営者がAzure Cosmos DBで問い合わせ履歴を持つ前に、Claude Codeでスキーマ、partition key、document項目、query境界、保持期間をレビューする手順を書きます。既存のマルチテナント記事では一般的な考え方を扱いました。ここではCosmos DBのcontainerと問い合わせ履歴に絞ります。

この記事の要点

  • SaaSの問い合わせ履歴では、ticket、message、internal note、audit logを同じ感覚で保存しない。
  • Cosmos DBではpartition keyの選び方が後から効く。tenantIdだけでよいか、階層partition keyが要るかを先に検討する。
  • Claude Codeには、document例、query例、container設定、保持期間、maskルールを読ませ、cross-tenant事故の入口を探させる。
  • 人が見る範囲は、顧客別契約、個人情報、障害説明、返金、削除依頼、監査ログ、データ保持の判断。
  • 成果は、問い合わせ対応時間だけでなく、テナント混入検出件数、履歴検索の遅さ、削除依頼対応時間、面談相談率で見る。

現場で起きる失敗シーン

SaaSのサポート担当は、管理画面で問い合わせ一覧、顧客詳細、契約プラン、請求履歴、障害メモを見ます。最初はticketとmessageをJSONで保存するだけでも動きます。けれど、顧客が増えると、検索、並び替え、権限、保持期間、削除依頼、AI要約のためのエクスポートが増えます。

事故になりやすいのは、テナント境界がdocumentとqueryの両方に出ていない状態です。documentにtenantIdがない、queryにtenantId条件がない、partition keyがidだけ、internal noteと顧客向けmessageが混ざる。こうなると、たった1つのAPIで他社履歴が見える危険が出ます。

参考にした一次情報は、Azure Cosmos DB partitioningMultitenancy and Azure Cosmos DBHierarchical partition keysData modelingUnique keysCosmos DB securityです。公式情報でもpartition key、multitenancy、securityは分けて説明されているので、記事でも分けて見ます。

業務フロー: 問い合わせ履歴を4種類に分ける

SaaSの問い合わせ履歴をCosmos DBへ置く時、最初にcontainer名ではなく画面を見ます。サポート一覧、チケット詳細、担当者メモ、顧客向け返信、添付ファイル、監査ログ、AI要約用の下書き。どれを同じcontainerに置くか、どれを分けるかを決めます。

初心者向けには、ticket、message、internal note、audit logの4種類で分けると見通しがよくなります。ticketは一覧と状態管理、messageは顧客との会話、internal noteは社内メモ、audit logは誰が何を見たか・変えたかです。全部を1つのdocumentに詰めると、削除や権限の扱いがつらくなります。

種類主な項目Cosmos DBで見る点人が見る点
tickettenantId、ticketId、status、subjectpartition key、unique key、一覧query顧客別契約、優先度
messagetenantId、ticketId、sender、bodybodyのmask、保持期間個人情報、機密情報
internal notetenantId、ticketId、note、staffId顧客へ出さない境界返金、障害説明
audit logtenantId、actorId、action、createdAt追跡、TTL、改ざん防止監査、削除依頼

この表をClaude Codeへ渡すと、Cosmos DBのcontainer案が出ます。1つのcontainerにtypeで入れる案、ticketとmessageを分ける案、監査ログだけ分ける案などです。判断軸は、queryの多さ、tenantごとのデータ量、保持期間、権限です。

Claude Codeに任せる範囲と人が見る範囲

Claude Codeに任せる範囲は、document項目の抜け、partition key候補、queryのtenantId漏れ、unique key候補、maskルール、保持期間の下書きです。サンプルdocument、問い合わせ一覧API、検索query、サポート画面の項目表を読ませると、レビュー表を作れます。

人が見る範囲は、顧客データの扱いです。契約で物理分離が必要な顧客、障害時の説明、返金、削除依頼、個人情報、サポート本文のAI利用、監査ログの保存期間は、人が決めます。Claude Codeが「tenantIdで分ければよい」と書いても、大口tenantが大きく育つ場合や契約要件が違う場合は再検討します。

Cosmos DBのpartition keyは、containerを作る時の大事な選択です。Microsoft Learnでは、partition keyがデータ分散やqueryに影響し、後から変えるには移行が必要になる点が説明されています。SaaSでは、tenantIdを軸にしつつ、userId、ticketId、createdMonthなどを組み合わせる候補を実データ量で見ます。

3つのUse case

Use case 1: 問い合わせticketのpartition keyを決める

  • 入力: 顧客数、1社あたりのticket件数、検索画面、一覧API、契約で分離が必要な顧客。
  • 出力: partition key候補、query例、hot tenantのリスク、container分割案。
  • 人の確認: 大口顧客、契約要件、保存期間、コスト、移行のしやすさを見る。

tenantIdだけで始めると、tenant単位のqueryは書きやすいです。一方で、特定tenantだけ問い合わせが極端に多いSaaSでは、hot partitionやサイズ上限の懸念が出ます。Claude Codeには、実データの件数レンジとqueryを渡し、候補を比較させます。

Use case 2: サポート本文と社内メモを分ける

  • 入力: message document、internal note、添付ファイル項目、AI要約の対象、サポート担当の画面。
  • 出力: 顧客向け本文、社内メモ、AI要約用コピー、mask対象、ログへ出さない項目の表。
  • 人の確認: 個人情報、API token、契約条件、障害説明、返金や法務表現を確認する。

サポート本文には、顧客が貼ったtoken、請求番号、メール、担当者名、障害詳細が入ります。Cosmos DBに保存する前にmaskするか、保存後に検索用copyを作るか、AI要約へ渡す前に消すかを決めます。

Use case 3: cross-tenant queryをテストする

  • 入力: 問い合わせ一覧API、検索query、管理者画面、テストtenant、権限ロール。
  • 出力: tenantId条件が抜けたquery、別tenantのticketIdを渡した時の期待結果、拒否テスト。
  • 人の確認: 管理者の横断検索、サポート代理ログイン、大口顧客の専用環境をどう扱うかを見る。

Cosmos DBはqueryが柔軟なので、画面を作る人はtypeやstatusだけで絞りがちです。SaaSのcustomer-facing画面では、tenantIdをqueryと認可の両方で見ます。管理者向け横断検索は別APIにして、監査ログを残します。

コピペで使えるプロンプト

あなたはSaaSのCosmos DBスキーマレビュー担当です。
目的は、問い合わせ履歴のテナント混入、partition keyミス、個人情報ログ、削除依頼対応漏れを防ぐことです。

入力:
- support ticket / message / internal note / audit log のdocument例
- container設定案
- partition key候補
- 問い合わせ一覧APIのquery
- 顧客数と大口tenantの件数レンジ
- 保持期間と削除依頼の運用

確認してほしいこと:
1. 全documentにtenantIdがあるか
2. partition key候補がqueryとデータ量に合うか
3. tenantId条件が抜けたqueryがないか
4. support本文、internal note、audit logの境界
5. unique keyや外部ticket idの重複対策
6. token、password、secret、個人情報のmask
7. 保持期間、削除依頼、監査ログの扱い

制約:
- 顧客名、メール、token、契約条件をサンプルに入れない
- 会社導入向けなので主CTAは /training/ に寄せる
- 最後に、初心者が今日見るべきqueryとdocument項目を5つに絞る

動く確認コード

次のコードは、SaaSのCosmos DB問い合わせ履歴で止めたい項目を検査する小さなレビュー用コードです。わざと危ないcontainerとdocumentを入れているため、実行すると止めるべき項目が表で出ます。

// verify-cosmos-saas-schema.mjs
// No dependencies. Run with: node verify-cosmos-saas-schema.mjs
const container = {
  name: "supportHistory",
  partitionKey: "/id",
  uniqueKeys: [],
  ttl: null
};

const documents = [
  {
    id: "ticket-001",
    tenantId: "tenant-a",
    type: "ticket",
    subject: "billing question",
    customerEmail: "[email protected]",
    createdAt: "2026-07-19T09:00:00Z"
  },
  {
    id: "msg-001",
    type: "message",
    ticketId: "ticket-001",
    body: "please check token sk_live_example and invoice",
    createdAt: "2026-07-19T09:02:00Z"
  }
];

const queries = [
  "SELECT * FROM c WHERE c.type = 'ticket' ORDER BY c.createdAt DESC",
  "SELECT * FROM c WHERE c.tenantId = @tenantId AND c.type = 'ticket'"
];

const problems = [];

if (container.partitionKey === "/id") {
  problems.push({ item: "partition key", fix: "use a tenant-aware key such as /tenantId or a tested hierarchical key" });
}
if (!container.uniqueKeys.some((key) => key.paths?.includes("/tenantId") && key.paths?.includes("/externalId"))) {
  problems.push({ item: "unique key", fix: "protect duplicate external ticket ids within a tenant" });
}
if (container.ttl === null) {
  problems.push({ item: "retention", fix: "define retention for support message copies and audit exports" });
}
for (const doc of documents) {
  if (!doc.tenantId) {
    problems.push({ item: "document tenantId", fix: "every support ticket and message needs tenantId" });
  }
  if (/sk_live|token|password|secret/i.test(JSON.stringify(doc))) {
    problems.push({ item: "sensitive text", fix: "mask tokens and secrets before storing support history" });
  }
}
for (const query of queries) {
  if (!/tenantId\s*=\s*@tenantId/.test(query)) {
    problems.push({ item: "query boundary", fix: "include tenantId in customer-facing support queries" });
  }
}

if (problems.length > 0) {
  console.table(problems);
  process.exitCode = 1;
} else {
  console.log("Cosmos DB SaaS schema checklist passed.");
}

このコードで見ているのは、partition key、unique key、保持期間、tenantId、sensitive text、query境界です。実運用では、Azure Portal、Bicep、Terraform、SDK、CI、負荷テストも合わせて確認します。まずはこの6項目をレビュー表へ移してください。

Pitfall: よくある落とし穴

SaaSのCosmos DBで一番多い落とし穴は、tenantIdをdocumentに入れたから安全だと思う流れです。原因は、保存時の項目と読み取り時のqueryを別々に見ている点です。直し方は、document、query、API認可、テストを1枚の表で見ることです。

二つ目は、partition keyをidにしてしまうことです。idだけではtenant単位の一覧や削除、問い合わせ履歴の追跡が難しくなります。Cosmos DBではpartition keyがqueryや分散に影響するため、問い合わせ画面のqueryから逆算します。

三つ目は、support本文をAI要約や検索用indexへそのまま渡すことです。顧客が貼ったsecret、token、個人情報、契約条件が混ざります。mask、保持期間、要約対象、社内メモの扱いを分けます。

四つ目は、大口tenantを見ないことです。最初はtenantIdだけで扱えても、一部tenantだけデータ量やアクセスが極端に増える場合があります。階層partition keyやcontainer分割、専用accountの候補を早めに比較します。

よくある質問

Q. SaaSならpartition keyはtenantIdで決まりですか。

A. 決まりではありません。tenant単位のqueryが多いなら候補になりますが、大口tenant、query種類、保存期間、削除依頼、コストで変わります。実データの件数レンジを見て決めます。

Q. 問い合わせ本文は同じcontainerでよいですか。

A. 小さなSaaSならticketとmessageを同じcontainerに置く案もあります。ただし、internal note、audit log、AI要約用copyは保持期間や権限が違うため、分けた方がレビューしやすい場面があります。

Q. unique keyは必要ですか。

A. 外部ヘルプデスクやメール連携からticketを取り込むなら、tenantIdとexternalIdの重複防止が役に立ちます。Cosmos DBのunique keyはcontainer作成時の設計に関わるため、後から慌てないように候補を出します。

Q. まず何を見ればよいですか。

A. 今日まず、support ticket documentのtenantId、partition key、一覧query、本文mask、保持期間の5つを確認してください。この5つで、混入事故の入口がかなり見えます。

研修・相談につなげるなら何を見るか

SaaSのCosmos DB記事から相談へ進めるなら、PVだけでは判断しません。問い合わせ検索の秒数、テナント混入テストの失敗件数、削除依頼対応時間、サポート本文のmask漏れ件数、面談予約率を見ます。ここが改善すれば、スキーマレビューやクラウド設計相談に進む理由があります。

Cosmos DBのcontainer設計、partition key、問い合わせ履歴、AI要約前のmask、権限レビューまでまとめて整えたい場合は、ClaudeCodeLabの研修・相談で相談できます。まずはこの記事の確認コードを走らせ、自社の問い合わせ履歴で止めたい条件を5つ書き出してください。

実際に試した結果

この記事では、Microsoft LearnのCosmos DB partitioning、multitenancy、hierarchical partition keys、data modeling、unique keys、securityの説明を確認しました。さらに、slug、frontmatter、内部リンク、外部リンク、CTA、コードブロック、10言語ファイルの有無、記事キューの削除対象を確認しています。今日まず見る1手は、問い合わせticket documentのtenantId、partition key、一覧query、本文mask、保持期間をチェックする作業です。

#claude-code #SaaS #Cosmos DB #テナント分離 #問い合わせ履歴
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

まず無料PDFで基本を固め、繰り返し使う作業はGumroad教材へ、チーム導入や権限設計は導入相談へ進めます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。