关于 masterdata-storage-strategy
Master Data storage strategy 是面向 VTEX 开发者的技能,用于设计或审查 Master Data 实体。它帮助判断 Master Data 是否适用、设计 JSON Schema,并权衡索引、缓存、安全、触发器、容量与生命周期。
主要功能
- 01评估 Master Data 与其他存储方案
- 02设计 JSON Schema 和实体边界
- 03选择索引字段和默认返回字段
- 04配置缓存、安全规则和触发器
- 05规划 Schema 容量与清理
适用场景
- 01为新数据实体选择存储方式
- 02根据查询模式审查索引
- 03审计公开字段和访问规则
- 04规划应用开发中的 Schema 生命周期
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vtex/ skills --skill masterdata-storage-strategy安装后在 Claude Code 中输入/masterdata-storage-strategy确认已加载。
详细介绍
主数据存储策略
适用场景
在创建任何新的主数据实体之前,或审查现有用法时,请使用此技能。它可以帮助你回答以下问题:
- 主数据是否适合存储这些数据,还是 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
决策规则
何时使用主数据
在以下条件全部满足时,主数据很适用:
- 面向文档的访问——你的数据天然是键值型或文档型(具有可变架构的 JSON 文档)。你按索引字段查询,并获取完整或部分文档。
- 与平台集成——你能受益于 VTEX 原生功能:用于自动化工作流的
v-triggers、用于逐字段公开访问控制的v-security、用于搜索和筛选的v-indexed,以及用于架构即代码的masterdatabuilder。 - 中等数据量——实体将包含数千到数百万级别的文档。正确建立索引后,MD 可以很好地处理此类数据量。
- 不在购买关键路径上——MD 并未针对低于 10 毫秒的延迟进行优化。如果 MD 响应较慢,在结账、购物车或支付流程中同步读取 MD 可能会影响转化率。
- 没有更合适的原生存储——数据不属于 Catalog(商品/SKU 属性)、OMS(订单数据)、CL/AD(客户资料/地址)或 VBase(应用专用缓存/状态)。
何时不应使用主数据
| 数据类型 | 更合适的存储 | 原因 |
|---|---|---|
| 商品属性、规格 | Catalog(规格、非结构化规格) | 原生索引、搜索集成和目录 API |
| 订单数据、订单历史 | OMS(通过 OMS API + BFF 缓存) | 单一事实来源;复制到 MD 会导致数据偏差 |
| 客户资料、地址 | CL/AD 原生实体 | 由平台管理,并且已建立索引和缓存 |
| 应用专用缓存或临时状态 | VBase | 专为每个应用的临时存储而设计,无需承担架构开销 |
| 应用日志、调试追踪 | ctx.vtex.logger | 结构化日志基础设施,并非数据库 |
| 高吞吐量时序数据 | 外部数据库(SQL、NoSQL、时序数据库) | MD 并非为每天数百万次写入而设计 |
| 需要联接的关系型数据 | 外部 SQL 数据库 | MD 不支持联接;应对数据进行反规范化,或使用关系型数据库 |
| 要求强一致性的数据 | 外部数据库 | 对于已建立索引的字段,MD 采用最终一致性 |
Schema 设计原则
- 每个概念对应一个实体 — 不要在同一个实体中混合无关数据。每个实体都应表示一个明确的业务概念(例如
reviews、wishlists、legacyOrders)。 - 为查询字段建立索引 — 只有
v-indexed中的字段才能用于where子句。但不要过度建立索引:每增加一个索引字段,写入延迟和存储开销都会增加,因为每次文档变更都会更新索引。 - 尽量精简
v-default-fields— 默认只返回大多数使用者需要的字段。过大的默认响应会浪费带宽。 - 让
v-cache与工作负载匹配 — 对读操作密集型实体保留true(默认值)。对于写入频率较高、且使用者需要在写入后立即读取最新数据的实体,设为false。 - 明确设置
v-security— 除非有意允许未经身份验证的列表访问,否则将allowGetAll设为false。仅对必须允许未经身份验证访问的字段使用publicRead、publicWrite和publicFilter。
VTEX Schema 扩展(v-* 字段)— 参考
Master Data v2 使用 v-* 属性扩展标准 JSON Schema,用于控制索引、缓存、安全、默认值、触发器和 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 中包含个人身份信息(PII)(电子邮件、电话、地址)、内部 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 | 电子邮件提供商名称(用于 email 类型) |
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 中的每个字段都会创建一个二级索引,并在每次写入文档时更新。为从未查询过的字段创建索引会浪费写入吞吐量和存储空间。
为何重要 — 对写入频繁的实体过度创建索引(例如,索引 15 个字段,但只查询 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 数组中的字段无需任何身份验证即可访问。切勿在此列表中包含 PII(电子邮件、电话号码、地址)、内部 ID 或业务敏感数据。
为何重要 — 任何知道实体名称以及文档 ID 或搜索查询的人都可以访问公开字段。暴露 PII 会违反数据保护法规并造成安全漏洞。
检测方式 — 检查 v-security.publicRead 和 publicFilter,确认其中没有包含用户数据、内部引用或任何需要身份验证才能访问的字段。
正确做法 — 仅公开非敏感且面向展示的字段。
{
"v-security": {
"allowGetAll": false,
"publicRead": ["status", "displayName", "rating"],
"publicWrite": [],
"publicFilter": ["status"]
}
}
错误做法 — 公开暴露 PII 和内部字段。
{
"v-security": {
"allowGetAll": true,
"publicRead": ["email", "phone", "cpf", "internalScore", "organizationId"],
"publicWrite": ["email"],
"publicFilter": ["email", "phone"]
}
}
约束:遵守每个实体 60 个 schema 的限制
Master Data v2 实体的 schema 数量硬性上限为 60 个。masterdata 构建器会为每个已关联或安装的应用版本创建一个新 schema。达到上限后,新版本将无法部署。
为何重要 — 在活跃开发期间,频繁执行 vtex link 会迅速累积 schema。达到上限后,部署会被阻止,直到手动删除旧 schema。
检测 — 经历过多次 link/publish 周期的应用。通过 GET /api/dataentities/{entity}/schemas 检查 schema 数量。
正确做法 — 定期清理未使用的 schema。在 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}"
错误做法 — 开发期间从不清理 schema,直到达到上限、部署被阻止。
推荐模式
包含所有 VTEX 扩展的完整 schema 示例
{
"$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 }
}
}
]
}
触发器:何时使用,何时不使用
在以下情况下使用触发器:
- 需要在文档变更时发送电子邮件通知(例如审核提醒)
- 需要在文档满足条件时调用外部 webhook
- 操作简单,触发后无需等待结果,也不需要复杂的错误处理
在以下情况下不要使用触发器:
- 需要复杂的编排、带退避的重试或错误恢复——改用 IO 事件
- 需要在变更发生后亚秒级响应——触发器有内置延迟
- 操作会链式修改其他 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 分页、已知的小规模数据集 | 每页 100 个 |
scrollDocuments | 大规模导出、批量操作、无界迭代 | 可配置批次大小 |
常见故障模式
- 索引过多 — 在高写入量实体上为 10 个以上字段建立索引。每次写入都会更新所有索引,增加延迟和存储开销。
- 缺少索引 — 查询未建立索引的字段会触发全表扫描。在有 100 个文档的开发环境中可正常运行,但在生产环境中面对 100k 个文档时会超时。
- 默认
v-cache: false— 在读操作较多的实体上禁用缓存,会导致每次 GET 都访问数据库。仅对高写入量实体禁用缓存。 - 包含 PII 的
allowGetAll: true— 未经身份验证的用户可以列出所有文档,包括敏感数据。 - Schema 累积 — 开发周期产生的 60 个 schema 会阻止生产部署。
- 触发器链 — 触发器 A 修改实体 B,而实体 B 的触发器又修改实体 A——形成无限循环。
- 将 MD 用作日志存储 — 实体随流量增长而无限扩张。请改用
ctx.vtex.logger。 - MD 位于关键路径上 — 在结账流程中同步读取 MD,却没有超时或回退机制。
审查清单
- 是否已完成存储适用性审查?(MD、Catalog、OMS、VBase 还是外部数据库)
-
v-indexed中是否仅包含会被查询的字段?是否有不必要的索引? -
v-cache是否根据实体的读写比例正确设置? -
v-security是否将公开访问限制为仅包含非敏感字段? - 除非明确需要,
allowGetAll是否设为false? - 触发器是否简单且不会形成链式调用?是否不存在触发器循环的风险?
- 是否制定了符合 60 个 schema 限制的 schema 清理策略?
- 实体是否不在购买关键路径上(结账、购物车、支付)?
- 对于大型数据集(100k+ 个文档),是否使用
scrollDocuments而非分页搜索? -
v-default-fields是否保持精简(默认不返回所有内容)?
相关技能
- vtex-io-masterdata — IO 应用集成:MasterDataClient、
masterdatabuilder、CRUD 模式 - vtex-io-application-performance — 暴露 MD 数据时的缓存层和 BFF 模式
- architecture-well-architected-commerce — 跨领域的存储和架构原则
参考资料
- 在 Master Data v2 中使用 JSON Schema — v-indexed、v-cache、v-security、v-triggers 配置
- Master Data v2 基础知识 — 核心概念和数据模型
- Master Data Schema 生命周期 — Schema 版本控制和 60 个 Schema 的上限
- 在 Master Data v2 中设置触发器 — 触发器配置和模式
- Master Data v2 API 参考 — 完整的 API 规范
- Master Data v2 文档保存流程 — 验证、索引和触发器执行顺序