vtex-io-app-contract

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

App Contract & Builder Boundaries

应用契约与Builder边界

When this skill applies

本技能适用场景

Use this skill when the main decision is about what a VTEX IO app is, what capabilities it declares, and which integration boundaries it publishes through
manifest.json
.
  • Creating a new VTEX IO app and defining its initial contract
  • Adding or removing builders to match app capabilities
  • Choosing between
    dependencies
    and
    peerDependencies
  • Deciding whether a capability belongs in the current app or should move to another app
  • Troubleshooting link or publish failures caused by manifest-level contract issues
Do not use this skill for:
  • service runtime behavior such as
    service.json
    , memory, workers, or route exposure
  • HTTP handler implementation, middleware composition, or event processing
  • GraphQL schema, resolver, or data-fetching implementation
  • storefront, admin, or render-runtime frontend behavior
  • policy modeling and security boundary enforcement
当你需要决策VTEX IO应用的定位、声明的能力、以及通过
manifest.json
对外发布的集成边界时,可使用本技能:
  • 创建新的VTEX IO应用并定义其初始契约
  • 添加或移除builder以匹配应用能力
  • 选择使用
    dependencies
    还是
    peerDependencies
  • 决策某项能力是否应该归属当前应用,还是迁移到其他应用
  • 排查manifest层面契约问题导致的link或发布失败
请勿将本技能用于以下场景:
  • 服务运行时行为配置,比如
    service.json
    、内存、worker或路由暴露
  • HTTP handler实现、中间件组合或事件处理
  • GraphQL schema、resolver或数据获取实现
  • 店铺前端、管理后台、render-runtime前端行为
  • 权限建模与安全边界执行

Decision rules

决策规则

  • Treat
    manifest.json
    as the app contract. It declares identity, builders, dependencies, peer dependencies, and high-level capabilities that other apps or the platform rely on.
  • Add a builder only when the app truly owns that capability. Builders are not placeholders for future work.
  • Keep the contract narrow. If a manifest starts to represent unrelated concerns, split those concerns into separate apps instead of creating a catch-all app.
  • Use
    dependencies
    only for apps that can be safely auto-installed as part of the current app contract. Use
    peerDependencies
    for apps that must already exist in the environment, remain externally managed, or declare
    billingOptions
    .
  • Keep naming and versioning publishable:
    vendor
    ,
    name
    , and
    version
    must form a stable identity that can be linked, published, and consumed safely.
  • Keep
    billingOptions
    aligned with the commercial contract of the app. If the app has billing implications, declare them explicitly in the manifest rather than leaving pricing behavior implicit.
  • Apps that declare
    billingOptions
    cannot be consumed through
    dependencies
    . If the current app requires a billable app, model that relationship with
    peerDependencies
    and require manual installation by the account or edition owner.
  • Edition apps are compositions of app contracts, not exceptions to them. Keep each underlying app contract explicit, narrow, and semver-safe so composition stays predictable across host environments.
  • manifest.json
    can also declare app-level permissions and configuration surfaces, but detailed policy modeling belongs in security-focused skills and detailed
    settingsSchema
    design belongs in app-settings skills.
This is not an exhaustive list of builders; see the official Builders docs for the full catalog.
Builder ownership reference:
BuilderOwn this builder when the app contract includes
node
backend runtime capability owned by this app
graphql
GraphQL schema exposure owned by this app
react
React bundles owned by this app
admin
Admin UI surfaces owned by this app
store
Store Framework block registration owned by this app
messages
localized message bundles owned by this app
pixel
storefront pixel injection owned by this app
masterdata
Master Data schema assets owned by this app
Contract boundary heuristic:
  1. If the capability is shipped, versioned, and maintained with this app, declare its builder here.
  2. If the capability is only consumed from another app, declare a dependency instead of duplicating the builder.
  3. If the capability introduces a separate runtime, security model, or release cadence, consider splitting it into another app.
  • manifest.json
    视作应用契约,它声明了应用标识、builder、依赖、peer依赖,以及其他应用或平台依赖的上层能力
  • 仅当应用实际拥有对应能力时才添加builder,builder不能作为未来功能的占位符
  • 保持契约范围收敛:如果manifest开始承载无关的职责,将这些职责拆分到独立应用,而不是创建一个包揽所有功能的应用
  • 仅对可以作为当前应用契约的一部分、能够安全自动安装的应用使用
    dependencies
    ;对于必须提前存在于环境中、由外部管理、或者声明了
    billingOptions
    的应用,使用
    peerDependencies
  • 保持命名和版本可发布:
    vendor
    name
    version
    必须构成稳定的标识,能够被安全地link、发布和消费
  • 保持
    billingOptions
    与应用的商业契约对齐:如果应用涉及计费,必须在manifest中显式声明,而不是让定价行为隐式存在
  • 声明了
    billingOptions
    的应用不能通过
    dependencies
    被依赖:如果当前应用需要一款付费应用,使用
    peerDependencies
    来声明该关系,并要求账号或版本所有者手动安装
  • 版本应用是应用契约的组合,而非契约的例外:保持每个底层应用契约显式、范围收敛、符合semver规范,这样组合后的应用在不同宿主环境中行为都是可预测的
  • manifest.json
    也可以声明应用级别的权限和配置项,但详细的权限建模属于安全相关技能的范畴,详细的
    settingsSchema
    设计属于应用配置相关技能的范畴
本列表并未涵盖所有builder,完整目录请查阅官方Builder文档。
builder所有权参考:
Builder当应用契约包含以下内容时可使用该builder
node
本应用拥有的后端运行时能力
graphql
本应用拥有的GraphQL schema暴露能力
react
本应用拥有的React包
admin
本应用拥有的Admin UI界面
store
本应用拥有的Store Framework块注册能力
messages
本应用拥有的国际化文案包
pixel
本应用拥有的店铺前端pixel注入能力
masterdata
本应用拥有的Master Data schema资源
契约边界判断规则:
  1. 如果某项能力是随当前应用一起发布、版本管理和维护的,在这里声明对应的builder
  2. 如果某项能力只是从其他应用消费的,声明依赖即可,不要重复声明builder
  3. 如果某项能力引入了独立的运行时、安全模型或发布节奏,考虑将其拆分到另一个应用

Hard constraints

硬性约束

Constraint: Every shipped capability must be declared in the manifest contract

约束:所有发布的能力都必须在manifest契约中声明

If the app ships a processable VTEX IO capability,
manifest.json
MUST declare the corresponding builder. Do not rely on folder presence alone, and do not assume VTEX IO infers capabilities from the repository structure. Symmetrically, do not declare builders for capabilities that are not actually shipped by this app.
Why this matters
VTEX IO compiles and links apps based on declared builders, not on intent. If the builder is missing, the platform ignores that capability and the app contract becomes misleading. The app may link partially while the expected feature is absent.
Detection
If you see a maintained
/node
,
/react
,
/graphql
,
/admin
,
/store
,
/messages
,
/pixel
, or
/masterdata
directory, STOP and verify that the matching builder exists in
manifest.json
. If the builder exists but the capability does not, STOP and remove the builder or move the capability back into scope.
Correct
json
{
  "vendor": "acme",
  "name": "reviews-platform",
  "version": "0.4.0",
  "builders": {
    "node": "7.x",
    "graphql": "1.x",
    "messages": "1.x"
  }
}
Wrong
json
{
  "vendor": "acme",
  "name": "reviews-platform",
  "version": "0.4.0",
  "builders": {
    "messages": "1.x"
  }
}
The app ships backend and GraphQL capabilities but declares only
messages
, so the runtime contract is incomplete and the platform ignores the missing builders.
如果应用发布了可处理的VTEX IO能力,
manifest.json
必须 声明对应的builder。不要仅依赖文件夹存在,也不要假设VTEX IO会从仓库结构推断能力。 反过来,也不要声明应用实际未发布的能力对应的builder。
为什么这很重要
VTEX IO基于声明的builder而非意图来编译和link应用,如果缺少对应的builder,平台会忽略相关能力,应用契约也会产生误导。应用可能只会部分link成功,而预期的功能会缺失。
检查方式
如果你看到有维护中的
/node
/react
/graphql
/admin
/store
/messages
/pixel
/masterdata
目录,停下检查
manifest.json
中是否存在对应的builder。如果声明了builder但实际没有对应能力,停下移除builder,或者将对应能力放回当前应用范围。
正确示例
json
{
  "vendor": "acme",
  "name": "reviews-platform",
  "version": "0.4.0",
  "builders": {
    "node": "7.x",
    "graphql": "1.x",
    "messages": "1.x"
  }
}
错误示例
json
{
  "vendor": "acme",
  "name": "reviews-platform",
  "version": "0.4.0",
  "builders": {
    "messages": "1.x"
  }
}
该应用发布了后端和GraphQL能力,但仅声明了
messages
,因此运行时契约不完整,平台会忽略缺失的builder。

Constraint: App identity and versioning must stay publishable and semver-safe

约束:应用标识和版本必须可发布,且符合semver规范

The
vendor
,
name
, and
version
fields MUST identify a valid VTEX IO app contract. Use kebab-case for the app name, keep the vendor consistent with ownership, and use full semantic versioning.
Why this matters
Consumers, workspaces, and release flows rely on app identity stability. Invalid names or incomplete versions break linking and publishing, while identity drift creates unsafe upgrades and hard-to-debug dependency mismatches.
Detection
If you see uppercase characters, underscores, non-semver versions, or vendor/name changes mixed into unrelated work, STOP and validate whether the change is intentional and release-safe.
Correct
json
{
  "vendor": "acme",
  "name": "order-status-dashboard",
  "version": "2.1.0"
}
Wrong
json
{
  "vendor": "AcmeTeam",
  "name": "Order_Status_Dashboard",
  "version": "2.1"
}
This identity is not safely publishable because the name is not kebab-case and the version is not valid semver.
vendor
name
version
字段必须标识一个有效的VTEX IO应用契约。应用名称使用kebab-case格式,vendor与归属方保持一致,使用完整的语义化版本号。
为什么这很重要
消费者、工作区和发布流程依赖稳定的应用标识。无效的名称或不完整的版本会破坏link和发布流程,而标识变更会导致不安全的升级和难以排查的依赖不匹配问题。
检查方式
如果你看到大写字符、下划线、不符合semver的版本,或者与无关变更混合在一起的vendor/名称变更,停下确认该变更是否是故意的、是否符合发布安全要求。
正确示例
json
{
  "vendor": "acme",
  "name": "order-status-dashboard",
  "version": "2.1.0"
}
错误示例
json
{
  "vendor": "AcmeTeam",
  "name": "Order_Status_Dashboard",
  "version": "2.1"
}
该标识无法安全发布,因为名称不符合kebab-case格式,版本也不是有效的semver版本。

Constraint: Dependencies and peerDependencies must express installation intent correctly

约束:dependencies和peerDependencies必须准确表达安装意图

Use
dependencies
only for apps that this app should install as part of its contract and that can be auto-installed safely. Use
peerDependencies
for apps that must already be present in the environment, should remain externally managed, or declare
billingOptions
.
Why this matters
This is the core contract boundary between your app and the rest of the VTEX IO workspace. Misclassifying a relationship causes broken installations, hidden coupling, and environment-specific behavior that only appears after link or publish. In particular, builder-hub rejects dependencies on apps that declare
billingOptions
.
Detection
If the app requires another app to function in every environment, STOP and confirm whether it belongs in
dependencies
or
peerDependencies
. If the target app declares
billingOptions
, STOP and move it to
peerDependencies
. If the app only integrates with a platform capability, host app, edition-managed app, or paid app that the account is expected to manage manually, STOP and keep it out of
dependencies
.
Correct
json
{
  "dependencies": {
    "vtex.search-graphql": "0.x"
  },
  "peerDependencies": {
    "vtex.store": "2.x",
    "vtex.paid-app-example": "1.x"
  }
}
Wrong
json
{
  "dependencies": {
    "vtex.store": "2.x",
    "vtex.paid-app-example": "1.x"
  },
  "peerDependencies": {}
}
This contract hard-installs a host app that should usually be externally managed and also attempts to auto-install a billable app, which builder-hub rejects.
仅对作为当前应用契约的一部分、可以安全自动安装的应用使用
dependencies
;对于必须提前存在于环境中、由外部管理、或者声明了
billingOptions
的应用,使用
peerDependencies
为什么这很重要
这是你的应用与VTEX IO工作区其他部分的核心契约边界。错误分类依赖关系会导致安装失败、隐式耦合,以及只有在link或发布后才会出现的环境特定行为。尤其是builder-hub会拒绝对声明了
billingOptions
的应用的依赖。
检查方式
如果应用需要依赖另一个应用才能在所有环境中运行,停下确认它应该属于
dependencies
还是
peerDependencies
。如果目标应用声明了
billingOptions
,停下将其移动到
peerDependencies
。如果应用仅与平台能力、宿主应用、版本管理的应用、或者需要账号手动管理的付费应用集成,停下不要将其放入
dependencies
正确示例
json
{
  "dependencies": {
    "vtex.search-graphql": "0.x"
  },
  "peerDependencies": {
    "vtex.store": "2.x",
    "vtex.paid-app-example": "1.x"
  }
}
错误示例
json
{
  "dependencies": {
    "vtex.store": "2.x",
    "vtex.paid-app-example": "1.x"
  },
  "peerDependencies": {}
}
该契约强制安装了通常应该由外部管理的宿主应用,还尝试自动安装付费应用,会被builder-hub拒绝。

Preferred pattern

推荐模式

Start by deciding the smallest useful contract for the app, then declare only the identity and builders required for that contract.
Recommended manifest for a focused service-plus-GraphQL app:
json
{
  "vendor": "acme",
  "name": "reviews-platform",
  "version": "0.1.0",
  "title": "Reviews Platform",
  "description": "VTEX IO app that owns review APIs and review GraphQL exposure",
  "builders": {
    "node": "7.x",
    "graphql": "1.x",
    "messages": "1.x"
  },
  "billingOptions": {
    "type": "free"
  },
  "dependencies": {
    "vtex.search-graphql": "0.x"
  },
  "peerDependencies": {
    "vtex.store": "2.x"
  }
}
Recommended contract split:
text
reviews-platform/
├── manifest.json        # identity, builders, dependencies, peerDependencies
├── node/                # backend capability owned by this app
├── graphql/             # GraphQL capability owned by this app
└── messages/            # app-owned translations

reviews-storefront/
├── manifest.json        # separate release surface for storefront concerns
├── react/
└── store/
Use this split when the backend/API contract and the storefront contract have different ownership, release cadence, or integration boundaries.
首先确定应用最小的可用契约,然后仅声明该契约所需的标识和builder。
聚焦于服务+GraphQL能力的应用的推荐manifest配置:
json
{
  "vendor": "acme",
  "name": "reviews-platform",
  "version": "0.1.0",
  "title": "Reviews Platform",
  "description": "VTEX IO app that owns review APIs and review GraphQL exposure",
  "builders": {
    "node": "7.x",
    "graphql": "1.x",
    "messages": "1.x"
  },
  "billingOptions": {
    "type": "free"
  },
  "dependencies": {
    "vtex.search-graphql": "0.x"
  },
  "peerDependencies": {
    "vtex.store": "2.x"
  }
}
推荐的契约拆分方式:
text
reviews-platform/
├── manifest.json        # 标识、builder、dependencies、peerDependencies
├── node/                # 本应用拥有的后端能力
├── graphql/             # 本应用拥有的GraphQL能力
└── messages/            # 应用自有翻译文案

reviews-storefront/
├── manifest.json        # 店铺前端相关的独立发布入口
├── react/
└── store/
当后端/API契约和店铺前端契约归属不同、发布节奏不同、或者集成边界不同时,使用这种拆分方式。

Common failure modes

常见失败场景

  • Declaring builders for aspirational capabilities that the app does not yet own, which makes the contract broader than the real implementation.
  • Using one large manifest to represent backend runtime, frontend rendering, settings, policies, and integration concerns that should be separated into multiple skills or apps.
  • Putting host-level apps in
    dependencies
    when they should remain
    peerDependencies
    .
  • Pinning exact dependency versions instead of major-version ranges such as
    0.x
    ,
    1.x
    , or
    3.x
    .
  • Treating
    manifest.json
    as a dumping ground for runtime or security details that belong in more specific skills.
  • Modeling
    settingsSchema
    here instead of using this skill only to decide whether app-level configuration belongs in the contract at all.
  • 为应用尚未拥有的预期能力声明builder,导致契约范围大于实际实现
  • 使用一个大的manifest承载本应拆分到多个技能或应用的后端运行时、前端渲染、配置、权限、集成相关的职责
  • 将宿主级应用放在
    dependencies
    中,而它们本应属于
    peerDependencies
  • 固定依赖的精确版本,而不是使用主版本范围比如
    0.x
    1.x
    3.x
  • manifest.json
    当作运行时或安全细节的回收站,这些内容本应属于更具体的技能范畴
  • 在这里设计
    settingsSchema
    ,而本技能仅用于决策应用级配置是否应该属于契约的一部分

Review checklist

审核检查清单

  • Does the manifest describe only capabilities this app actually owns and ships?
  • Does every shipped capability have a matching builder declaration?
  • Is the app identity publishable: valid
    vendor
    , kebab-case
    name
    , and full semver
    version
    ?
  • If the app has billing behavior, is
    billingOptions
    explicit and aligned with the app contract?
  • Are
    dependencies
    and
    peerDependencies
    separated by installation intent?
  • Would splitting the contract into two apps reduce unrelated concerns or release coupling?
  • Are runtime, route, GraphQL implementation, frontend, and security details kept out of this skill?
  • manifest是否仅描述了应用实际拥有和发布的能力?
  • 所有发布的能力都有对应的builder声明吗?
  • 应用标识可发布吗:
    vendor
    有效、
    name
    符合kebab-case、
    version
    是完整的semver版本?
  • 如果应用有计费逻辑,
    billingOptions
    是否显式声明且与应用契约对齐?
  • dependencies
    peerDependencies
    是否按照安装意图正确区分?
  • 将契约拆分为两个应用是否能减少无关职责或发布耦合?
  • 运行时、路由、GraphQL实现、前端、安全相关的细节是否没有出现在本技能范围内?

Reference

参考资料