masterdata-storage-strategy について
Master Data storage strategy は、Master Data エンティティを設計または監査する VTEX 開発者向けのスキルです。Master Data の適合性を判断し、JSON Schema の設計や、インデックス、キャッシュ、セキュリティ、トリガー、容量、ライフサイクルの検討を支援します。
主な機能
- 01Master Data と他の保存先を比較
- 02JSON Schema とエンティティ境界を設計
- 03インデックスと既定フィールドを選定
- 04キャッシュ、セキュリティ、トリガーを設定
- 05Schema の容量と整理を計画
活用シーン
- 01新しいデータエンティティの保存先を選定
- 02クエリパターンに沿ってインデックスを確認
- 03公開フィールドとアクセス規則を監査
- 04アプリ開発における Schema のライフサイクルを計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ vtex/ skills --skill masterdata-storage-strategyインストール後、Claude Code で/masterdata-storage-strategyを入力して読み込まれたか確認します。
詳細
Master Data のストレージ戦略
このスキルを使用するタイミング
新しい Master Data エンティティを作成する前、または既存の使用状況を監査するときに、このスキルを使用してください。次の点を判断するのに役立ちます。
- このデータの保存先として Master Data は適切でしょうか。それとも Catalog、OMS、VBase、外部データベースの方が適しているでしょうか?
- パフォーマンスとセキュリティを考慮して、JSON Schema をどのように設計すればよいでしょうか?
- どのフィールドにインデックス(
v-indexed)を設定し、どのフィールドには設定しないべきでしょうか? - キャッシュ(
v-cache)を有効にするべきでしょうか、それとも無効にするべきでしょうか? - トリガー(
v-triggers)は必要でしょうか。それともイベント駆動の IO アプローチの方が適切でしょうか? - スキーマとドキュメントの容量やライフサイクルをどのように計画すればよいでしょうか?
次の用途では、このスキルを使用しないでください。
- VTEX IO アプリの統合パターン(MasterDataClient、
masterdatabuilder、コード内の CRUD)—vtex-io-masterdataを使用してください - IO サービスのパフォーマンスパターン(LRU、VBase のキャッシュレイヤー)—
vtex-io-application-performanceを使用してください
判断ルール
Master Data を使用する場合
次の条件をすべて満たす場合、Master Data が適しています。
- ドキュメント指向のアクセス — データが自然なキーと値の形式、またはドキュメント形式(スキーマが可変の JSON ドキュメント)です。インデックス付きフィールドでクエリを実行し、ドキュメント全体または一部を取得します。
- プラットフォームとの統合 — VTEX ネイティブの機能(自動化されたワークフロー向けの
v-triggers、フィールド単位の公開アクセス制御向けのv-security、検索やフィルタリング向けのv-indexed、スキーマをコードとして管理するためのmasterdatabuilder)のメリットを得られます。 - 中程度のデータ量 — エンティティに数千から数百万件程度のドキュメントを保持します。適切にインデックスを設定すれば、MD はこの規模を適切に処理できます。
- 購入処理のクリティカルパスに含まれない — MD は 10 ミリ秒未満のレイテンシー向けに最適化されていません。チェックアウト、カート、決済フローで MD を同期的に読み取ると、MD が遅い場合にコンバージョンへ悪影響を及ぼすおそれがあります。
- より適したネイティブの保存先がない — データが Catalog(商品/SKU 属性)、OMS(注文データ)、CL/AD(顧客プロフィール/住所)、VBase(アプリ固有のキャッシュ/状態)に該当しません。
Master Data を使用しない場合
| データの種類 | より適した保存先 | 理由 |
|---|---|---|
| 商品属性、仕様 | Catalog(仕様、非構造化仕様) | ネイティブのインデックス設定、検索との統合、カタログ API |
| 注文データ、注文履歴 | OMS(OMS API + BFF キャッシュ経由) | 信頼できる唯一の情報源です。MD に複製すると不整合が生じます |
| 顧客プロフィール、住所 | CL/AD のネイティブエンティティ | プラットフォームによって管理され、すでにインデックスとキャッシュが設定されています |
| アプリ固有のキャッシュまたは一時的な状態 | VBase | アプリごとの一時的なストレージ向けに設計されており、スキーマのオーバーヘッドがありません |
| アプリケーションログ、デバッグトレース | ctx.vtex.logger | 構造化ログのインフラストラクチャであり、データベースではありません |
| 高スループットの時系列データ | 外部データベース(SQL、NoSQL、時系列データベース) | MD は 1 日あたり数百万件の書き込み向けに設計されていません |
| 結合を必要とするリレーショナルデータ | 外部 SQL データベース | MD は結合をサポートしていません。非正規化するか、リレーショナルデータベースを使用してください |
| 強い整合性が必要なデータ | 外部データベース | MD のインデックス付きフィールドは結果整合性です |
スキーマ設計の原則
- 概念ごとにエンティティを1つ作成する — 関連のないデータを1つのエンティティに混在させないでください。各エンティティは明確なビジネス概念(例:
reviews、wishlists、legacyOrders)を表す必要があります。 - クエリで使用するフィールドにインデックスを設定する —
where句で使用できるのはv-indexedに含まれるフィールドのみです。ただし、インデックスを過剰に設定しないでください。インデックスはドキュメントが変更されるたびに更新されるため、インデックス付きフィールドが増えると、書き込みのレイテンシとストレージ使用量が増加します。 v-default-fieldsは最小限にする — デフォルトで返すのは、ほとんどの利用者が必要とするフィールドのみにしてください。デフォルトのペイロードが大きいと、帯域幅が無駄になります。v-cacheはワークロードに合わせる — 読み取りが多いエンティティではtrue(デフォルト)のままにしてください。書き込み頻度が高く、利用者が書き込み直後の即時整合性を必要とするエンティティではfalseに設定してください。v-securityを明示する — 未認証での一覧アクセスが意図的な場合を除き、allowGetAll: falseに設定してください。publicRead、publicWrite、publicFilterは、認証なしでアクセス可能にする必要があるフィールドにのみ使用してください。
VTEX スキーマ拡張(v-* フィールド)— リファレンス
Master Data v2 は、インデックス作成、キャッシュ、セキュリティ、デフォルト値、トリガー、スキーマ継承を制御する v-* プロパティで、標準 JSON Schema を拡張します。これらは VTEX 固有のものであり、標準 JSON Schema バリデーターでは無視されます。
v-indexed
Master Data がセカンダリインデックスを作成するフィールド名の配列です。
searchDocumentsとscrollDocumentsのwhere句に指定できるのは、インデックス付きフィールドのみです。インデックスのないフィールドをクエリすると、ドキュメント全体のスキャンが実行され、大規模なデータセットではタイムアウトします。- インデックスはドキュメントが書き込まれるたびに更新されます。インデックスを過剰に設定すると、書き込みのレイテンシとストレージコストがそれに比例して増加します。
- インデックスを設定するフィールド:
whereフィルター、ソート式、publicFilterで使用するフィールド。インデックスを設定しないフィールド:大きなテキストフィールド(description、notes)、クエリで使用しないフィールド、ドキュメント ID でのみ読み取るフィールド(インデックスを設定してもgetDocumentの利点はありません)。
{ "v-indexed": ["email", "status", "createdAt"] }
v-cache
ブール値(デフォルトは true)です。Master Data が個々のドキュメントの GET レスポンスをキャッシュするかどうかを制御します。
true(デフォルト) — 読み取りが多いエンティティはキャッシュの恩恵を受けます。ほとんどのエンティティではデフォルトのままにしてください。false— 書き込み頻度が高く、利用者が書き込み直後に最新の読み取り結果を必要とするエンティティ(例:リアルタイムカウンター、構成フラグ、セッションのような状態)で使用します。
{ "v-cache": false }
v-default-fields
API リクエストで呼び出し元が fields パラメーターを指定しない場合に返されるフィールド名の配列です。
- 最小限にしてください。デフォルトで必要とする利用者が最も多いフィールドのみを指定します。
- 一般的なクエリのペイロードサイズを削減します。
{ "v-default-fields": ["email", "status", "score", "createdAt"] }
v-security
未認証(公開)でのフィールドアクセスを制御するオブジェクトです。デフォルトでは、すべてのフィールドで認証が必要です。
| プロパティ | 型 | 説明 |
|---|---|---|
allowGetAll | boolean | true の場合、未認証ユーザーはすべてのドキュメントを一覧表示できます。デフォルトは false です。意図的な場合を除き、無効のままにしてください。 |
publicRead | string[] | 認証なしで読み取り可能なフィールド |
publicWrite | string[] | 認証なしで書き込み可能なフィールド |
publicFilter | string[] | 認証なしで where 句に使用できるフィールド(v-indexed にも含まれている必要があります) |
{
"v-security": {
"allowGetAll": false,
"publicRead": ["status", "displayName", "rating"],
"publicWrite": [],
"publicFilter": ["status"]
}
}
publicRead または publicFilter に、個人を特定できる情報(メールアドレス、電話番号、住所)、内部 ID、ビジネス上の機密データを絶対に含めないでください。
v-triggers
ドキュメントが作成または更新され、指定された条件を満たしたときに実行される自動アクションを定義するトリガーオブジェクトの配列です。
| プロパティ | 型 | 説明 |
|---|---|---|
name | string | 一意のトリガー名 |
active | boolean | トリガーの有効/無効を切り替えます |
condition | string | where 形式のフィルター(例:"approved=false"、"status=pending AND priority>3") |
action.type | string | "email"、"http"(Webhook)、または "action" |
action.provider | string | メールプロバイダー名(メールタイプの場合) |
action.uri | string | Webhook URL(http タイプの場合) |
action.method | string | Webhook の HTTP メソッド(http タイプの場合) |
retry.times | number | 失敗時の再試行回数 |
retry.delay | object | 再試行までの待機時間(例:{ "addMinutes": 5 }) |
{
"v-triggers": [
{
"name": "notify-on-creation",
"active": true,
"condition": "status=new",
"action": {
"type": "email",
"provider": "default",
"subject": "New record: {{title}}",
"to": ["[email protected]"],
"body": "Record {{id}} created by {{author}}"
},
"retry": { "times": 3, "delay": { "addMinutes": 5 } }
},
{
"name": "webhook-on-approval",
"active": true,
"condition": "approved=true",
"action": {
"type": "http",
"uri": "https://my-integration.example.com/webhook",
"method": "POST",
"headers": { "X-Custom-Header": "value" }
},
"retry": { "times": 2, "delay": { "addMinutes": 10 } }
}
]
}
v-canonicalto
スキーマ継承のために、同じエンティティ内の別のスキーマを指す URL です。現在のスキーマは、参照先のプロパティと制約を継承します。
{
"v-canonicalto": "https://{host}/api/dataentities/{entity}/schemas/{base-schema}"
}
additionalProperties
標準の JSON Schema プロパティですが、注目すべき点があります。false に設定すると、properties で宣言されていないフィールドが拒否されます。デフォルトでは、Master Data は余分なフィールドを検証せずに保持します。
ハード制約
制約:where 句またはソート式で使用するフィールドのみインデックス化する
v-indexed 内のすべてのフィールドにセカンダリーインデックスが作成され、ドキュメントの書き込みのたびに更新されます。一度もクエリされないフィールドをインデックス化すると、書き込みスループットとストレージが無駄になります。
重要な理由 — 書き込み頻度の高いエンティティで過剰にインデックス化すると(例:クエリ対象が 3 フィールドだけなのに 15 フィールドをインデックス化する)、書き込みレイテンシーが 2 倍または 3 倍になることがあります。数百万件のドキュメントを持つエンティティでは、不要なインデックスによってストレージコストも増加します。
検出方法 — v-indexed のフィールドを、コードベース内の実際の where 句と比較します。where 句やソートで参照されていないインデックス付きフィールドは、不要である可能性が高いです。
正しい例 — フィルターまたはソートに使用するフィールドのみをインデックス化します。
{
"properties": {
"email": { "type": "string" },
"status": { "type": "string" },
"score": { "type": "integer" },
"notes": { "type": "string" },
"createdAt": { "type": "string", "format": "date-time" }
},
"v-indexed": ["email", "status", "createdAt"]
}
誤った例 — 「念のため」にすべてのフィールドをインデックス化します。
{
"properties": {
"email": { "type": "string" },
"status": { "type": "string" },
"score": { "type": "integer" },
"notes": { "type": "string" },
"createdAt": { "type": "string", "format": "date-time" }
},
"v-indexed": ["email", "status", "score", "notes", "createdAt"]
}
制約:v-security publicRead を介して機密フィールドを公開しない
v-security.publicRead 配列に含まれるフィールドは、認証なしでアクセス可能になります。個人を特定できる情報(メールアドレス、電話番号、住所)、内部 ID、または業務上機密性の高いデータをこのリストに含めないでください。
重要な理由 — 公開フィールドは、エンティティ名とドキュメント ID または検索クエリを知っている誰もがアクセスできます。個人を特定できる情報を公開すると、データ保護規制に違反し、セキュリティ上の脆弱性が生じます。
検出方法 — v-security.publicRead と publicFilter を確認し、ユーザーデータ、内部参照、または認証を必須にすべき情報を含むフィールドがないか調べます。
正しい例 — 機密性のない、表示を目的としたフィールドのみを公開します。
{
"v-security": {
"allowGetAll": false,
"publicRead": ["status", "displayName", "rating"],
"publicWrite": [],
"publicFilter": ["status"]
}
}
誤った例 — 個人を特定できる情報や内部フィールドを公開します。
{
"v-security": {
"allowGetAll": true,
"publicRead": ["email", "phone", "cpf", "internalScore", "organizationId"],
"publicWrite": ["email"],
"publicFilter": ["email", "phone"]
}
}
制約:エンティティごとのスキーマ数は60個まで
Master Data v2 のエンティティには、スキーマ数が60個までという厳格な上限があります。masterdata ビルダーは、リンクまたはインストールされたアプリのバージョンごとに新しいスキーマを作成します。上限に達すると、新しいバージョンをデプロイできなくなります。
重要な理由 — vtex link を頻繁に実行する活発な開発では、スキーマがすぐに蓄積します。上限に達すると、古いスキーマを手動で削除するまでデプロイができなくなります。
検出方法 — link/publish サイクルを何度も実行しているアプリが対象です。GET /api/dataentities/{entity}/schemas でスキーマ数を確認してください。
正しい方法 — 使用されていないスキーマを定期的にクリーンアップします。CI/CD でのクリーンアップを自動化してください。
# List schemas to identify stale ones
curl "https://{account}.vtexcommercestable.com.br/api/dataentities/{entity}/schemas" \
-H "X-VTEX-API-AppKey: {key}" -H "X-VTEX-API-AppToken: {token}"
# Delete unused schemas
curl -X DELETE "https://{account}.vtexcommercestable.com.br/api/dataentities/{entity}/schemas/{old-schema}" \
-H "X-VTEX-API-AppKey: {key}" -H "X-VTEX-API-AppToken: {token}"
誤った方法 — 上限に達してデプロイがブロックされるまで、開発中にスキーマを一切クリーンアップしないこと。
推奨パターン
VTEX のすべての拡張機能を含む完全なスキーマ例
{
"$schema": "http://json-schema.org/schema#",
"title": "product-review-v1",
"type": "object",
"properties": {
"productId": { "type": "string" },
"author": { "type": "string" },
"email": { "type": "string", "format": "email" },
"rating": { "type": "integer", "minimum": 1, "maximum": 5 },
"title": { "type": "string", "maxLength": 200 },
"text": { "type": "string", "maxLength": 5000 },
"approved": { "type": "boolean" },
"createdAt": { "type": "string", "format": "date-time" }
},
"required": ["productId", "rating", "title", "text"],
"v-indexed": ["productId", "approved", "rating", "createdAt"],
"v-default-fields": [
"productId",
"author",
"rating",
"title",
"approved",
"createdAt"
],
"v-cache": true,
"v-security": {
"allowGetAll": false,
"publicRead": [
"productId",
"author",
"rating",
"title",
"text",
"approved"
],
"publicWrite": [],
"publicFilter": ["productId", "approved", "rating"]
},
"v-triggers": [
{
"name": "notify-moderator",
"active": true,
"condition": "approved=false",
"action": {
"type": "email",
"provider": "default",
"subject": "New review pending moderation",
"to": ["[email protected]"],
"body": "Review for product {{productId}} by {{author}}: {{title}}"
},
"retry": {
"times": 3,
"delay": { "addMinutes": 5 }
}
}
]
}
トリガー:使用する場合と使用しない場合
次の場合はトリガーを使用してください:
- ドキュメントの変更時にメール通知が必要な場合(モデレーションのアラートなど)
- ドキュメントが条件を満たしたときに、外部のウェブフックを呼び出す必要がある場合
- アクションが単純で、実行後の結果を待つ必要がなく、複雑なエラー処理も不要な場合
次の場合はトリガーを使用しないでください:
- 複雑なオーケストレーション、バックオフを伴う再試行、またはエラー復旧が必要な場合 — IO イベントを使用してください
- 変更に対する1秒未満の応答が必要な場合 — トリガーには組み込みの遅延があります
- アクションが連鎖的に他の MD エンティティを変更する場合 — トリガーが連鎖してループするリスクがあります
whereスタイルのフィルターより複雑な条件ロジックが必要な場合
全件取得せずにドキュメント数を数える
REST-Content-Range ヘッダーを使うと、ドキュメント数を効率的に取得できます:
# Count documents without fetching them
curl "https://{account}.vtexcommercestable.com.br/api/dataentities/{entity}/search?_fields=id" \
-H "REST-Range: resources=0-0" \
-H "X-VTEX-API-AppKey: {key}" -H "X-VTEX-API-AppToken: {token}"
# Response header: REST-Content-Range: resources 0-0/12345
# The number after "/" is the total document count
Search と Scroll の比較
| 用途 | 使用する場合 | 最大ページサイズ |
|---|---|---|
searchDocuments | 結果数に上限がある検索、UI のページネーション、少量だとわかっている場合 | 1ページあたり100件 |
scrollDocuments | 大規模なエクスポート、一括処理、反復回数に上限がない場合 | 設定可能なバッチ数 |
よくある障害パターン
- 過剰なインデックス設定 — 書き込み頻度の高いエンティティの10個以上のフィールドにインデックスを設定すること。書き込みのたびにすべてのインデックスが更新されるため、レイテンシーとストレージ使用量が増加します。
- インデックスの不足 — インデックスのないフィールドをクエリすると、全件スキャンが発生します。開発環境では100件のドキュメントで動作しても、本番環境では10万件でタイムアウトします。
- デフォルトで
v-cache: false— 読み取りの多いエンティティでキャッシュを無効にすると、すべての GET リクエストがデータベースに到達します。書き込み頻度の高いエンティティでのみ無効にしてください。 - PII を含む状態での
allowGetAll: true— 認証されていないユーザーが、機密データを含むすべてのドキュメントを一覧表示できてしまいます。 - スキーマの蓄積 — 開発サイクルでスキーマが60個に達すると、本番デプロイがブロックされます。
- トリガーの連鎖 — トリガー A がエンティティ B を変更し、エンティティ B のトリガーがエンティティ A を変更することで、無限ループが発生します。
- ログストアとしての MD — トラフィック量に応じてエンティティが際限なく増加します。代わりに
ctx.vtex.loggerを使用してください。 - 重要な処理経路での MD — タイムアウトやフォールバックがない状態で、チェックアウト時に MD を同期的に読み取ること。
レビューチェックリスト
- ストレージ適合性のレビューを実施しましたか?(MD、Catalog、OMS、VBase、外部 DB の比較)
-
v-indexedに含まれるのはクエリ対象のフィールドのみですか?不要なインデックスはありませんか? - エンティティの読み取り/書き込み比率に応じて
v-cacheを適切に設定していますか? -
v-securityで、公開アクセスを機密情報以外のフィールドのみに制限していますか? - 明示的に必要な場合を除き、
allowGetAllをfalseにしていますか? - トリガーは単純で、連鎖していませんか?トリガーループのリスクはありませんか?
- 60個のスキーマ上限に対応するスキーマのクリーンアップ戦略がありますか?
- 購入に関わる重要な処理経路(チェックアウト、カート、支払い)でこのエンティティを使用していませんか?
- 大規模なデータセット(10万件以上のドキュメント)では、ページネーション付き検索ではなく
scrollDocumentsを使用していますか? -
v-default-fieldsを最小限にしていますか?(デフォルトですべてを返さない)
関連スキル
- vtex-io-masterdata — IO アプリの統合:MasterDataClient、
masterdataビルダー、CRUD パターン - vtex-io-application-performance — MD データを公開する際のキャッシュレイヤーと BFF パターン
- architecture-well-architected-commerce — ストレージとアーキテクチャに関する横断的な原則
リファレンス
- Master Data v2 での JSON スキーマの使用 — v-indexed、v-cache、v-security、v-triggers の設定
- Master Data v2 の基本 — 中核となる概念とデータモデル
- Master Data スキーマのライフサイクル — スキーマのバージョン管理と 60 スキーマの上限
- Master Data v2 のトリガーの設定 — トリガーの設定とパターン
- Master Data v2 API リファレンス — API の完全な仕様
- Master Data v2 のドキュメント保存フロー — 検証、インデックス作成、トリガー実行の順序