Croctコンテンツのモデリング
コンポーネントとスロットを設計し、スキーマを記述し、モデルの進化に合わせて有効なコンテンツ定義を埋めていきます。スキーマで表現できるものはすべてダッシュボードのビジュアルエディターにも存在します。JSONは同じモデルであり、手書きで記述します。
メンタルモデル
| 概念 | 説明 | スコープ |
|---|---|---|
| コンポーネント | 名前付きの再利用可能なスキーマ。structureとunionのみがコンポーネント(トップレベルのスキーマ)になれます。 | スロットや他のコンポーネント間で再利用可能 |
| 通常コンポーネント | 固定された属性セットを持つstructure。 | |
| ユニオンコンポーネント | 最大10個の通常コンポーネントからなるunion。エディターはコンテンツごとに1つのバリアントを選択します。 | |
| スロット | アプリ内のプレースホルダー(ヒーロー、アナウンスバー)で、1つのコンポーネントに結びついています。ロケールごとにデフォルトコンテンツを持ちます。 | コンテキスト固有。1つのコンポーネントが複数のスロットを支えることができます。 |
| コンテンツ定義 | スキーマの実際の値を保持するJSON。静的または動的(CQL)な値を持ちます。 |
コンテンツレイヤー(ベースからトップへ):スロットデフォルトコンテンツ(エクスペリエンスや実験が一致しない場合、ボットフィルタリングで除外されたボット、静的レンダリング時、エラー時に表示)→ エクスペリエンスコンテンツ(オーディエンスごと)→ 実験コンテンツ(バリアントごと。少なくとも2バリアント)→ コンテンツグループ(エクスペリエンスまたは実験バリアント内のサブセグメント向けの追加バリエーション)。フォールバックコンテンツはアプリケーションのソースコードに存在し、フェッチが失敗したときやサービスが停止したときにレンダリングされます。CLIはスロットデフォルトコンテンツをフォールバックとしてダウンロードします。
形状の決定
| 必要なもの | 使用するもの |
|---|---|
| 固定レイアウト(どこでも同じフィールド) | 通常コンポーネント(structure) |
| エディターが選択するバリエーション(テキストバナー vs 画像バナー vs フォームバナー) | 通常コンポーネントを参照するユニオンコンポーネント |
| 多くの場所での一貫性(どこでも使われるCTAボタン) | 別のコンポーネントとして作成し、{"type": "reference", "id": "..."}で埋め込む |
| 1つの場所での柔軟性(タイトル+画像ペア) | 新しいコンポーネントではなく、ネストされたstructure属性またはプリミティブ |
| 独立したセクションが複数あるページ | 1つの巨大なコンポーネントではなく、セクションごとに1コンポーネント、1スロット |
| 事前定義されたオプションセット | choicesを持つtext(列挙型) |
| 画像、動画、音声、ドキュメントのアップロード | @croct/fileへのreference(ファイルは5MBに制限。長時間の動画は代わりにurlテキスト属性を検討) |
| ツールバーエディターで編集されるフォーマット済みテキスト | @croct/richTextへのreference |
| プレーンテキストフィールドでの軽い書式設定 | text + アプリ内のMarkdownパーサー(下記のMarkdownを参照) |
| 他の値の計算にのみ使用される値(例:動的変数) | "private": trueの属性 |
ドキュメントからの経験則:「柔軟性が必要か、一貫性が必要か?」と自問してください。柔軟性 → 構造属性。一貫性 → 新しいコンポーネント。
ワークフロー
- ページのセクションをリストアップします。独立して配置される各セクションがスロットになります。
- 各スロットに対して、通常コンポーネントまたはユニオンコンポーネントを選択します。構造が一貫していることが意図されている場合は既存のコンポーネントを再利用します。
- スキーマを記述します:属性に
type、label、description、position、およびコンテンツが存在しない可能性がある場合はoptionalを含めます。実際のUIの制約からバリデーション(maximumLength、minimum、pattern、allowedTypes、リストのmaximumLength)を追加します。 - ネストを追加する前に制限(下記)を確認します。複雑度スコアを計算します。
- サポートされる各ロケールと各スロットのデフォルトコンテンツ定義を記述します。
- コンポーネントが既に使用中の場合、破壊的変更があると新しいバージョンが作成されます。影響を受けるコンテンツを更新した後、アプリ内のスロットバージョンを更新します(バージョニングを参照)。
スキーマの骨格
通常コンポーネントはstructureです。各属性はそのスキーマをtypeでラップします:
{
"type": "structure",
"title": "CTA Button",
"description": "A call-to-action button that links to a page.",
"attributes": {
"label": {
"label": "Label",
"description": "The text displayed on the button.",
"position": 0,
"type": {
"type": "text"
}
},
"link": {
"label": "Link",
"description": "The URL of the page to link to.",
"position": 1,
"type": {
"type": "text"
}
}
}
}
ユニオンコンポーネントはコンポーネントIDを参照にマッピングします。キーは参照するIDと等しくなければなりません:
{
"type": "union",
"types": {
"text-hero": {"type": "reference", "id": "text-hero"},
"image-hero": {"type": "reference", "id": "image-hero"}
}
}
アプリ内では、ユニオンスロットコンテンツは判別共用体です。各バリアントにはコンポーネントID(上記のtext-heroまたはimage-hero)に設定された_typeプロパティがあります。_typeで分岐してレンダリングします。croct-integrate-sdkスキルを参照してください。
属性タイプ一覧
| ダッシュボードタイプ | スキーマJSON |
|---|---|
| テキスト | {"type": "text"}(+ minimumLength、maximumLength、pattern) |
| 複数行テキスト | {"type": "text", "format": "multiline"} |
| URL | {"type": "text", "format": "url"} |
| 色 | {"type": "text", "format": "color"} |
| 日付/時刻 | {"type": "text", "format": "instant"}(local-date、local-time、local-date-time、zoned-date-timeも) |
| 列挙型 | {"type": "text", "choices": {"left": {"label": "Left"}}} |
| リッチテキスト | {"type": "reference", "id": "@croct/richText", "properties": {}} |
| ファイル | {"type": "reference", "id": "@croct/file", "properties": {}} |
| 数値 | {"type": "number"}(+ integer、minimum、maximum) |
| ブール | {"type": "boolean"}(+ label.true、label.false、default) |
| リスト | {"type": "list", "items": {...}}(+ itemLabel、minimumLength、maximumLength) |
| 構造 | {"type": "structure", "attributes": {...}} |
| コンポーネント | {"type": "reference", "id": "<component-id>"} |
formatとchoicesはtype以外のテキストプロパティと組み合わせることはできません。全プロパティリスト、デフォルト値、制限については:references/schema.mdを参照してください。
スキーマの制限
| 制限 | 値 |
|---|---|
| 構造あたりの属性数 | 30 |
| ネストの深さ | 6レベル |
| ユニオン内のコンポーネント数 | 10 |
| スキーマ複雑度スコア | 50 |
リストアイテム数(スキーマのデフォルトmaximumLength) | 30 |
テキストのmaximumLength | シングルライン300、マルチラインとURLは1000(暗黙的な制限:プレーンなtextはformatとmaximumLengthを組み合わせることはできません。リッチテキストは可能) |
テキストのchoices | 20 |
| ファイルサイズ | 5 MB(5242880 バイト) |
| ラベル/説明 | 60 / 160 文字 |
複雑度スコア:structure = 1 + その属性数;list = 1 + そのアイテムタイプ;プリミティブ = 1;union = 最も複雑なメンバー;reference = 参照先コンポーネント。タイトル、画像、価格を持つ製品のリストは5になります。
コンテンツ定義の骨格
すべての値は{"type": "<content type>", "value": {"type": "static" | "dynamic", ...}}です。Structureはattributesをネストし、listはitemsを保持し、unionコンテンツはnameがユニオンキーに設定されたstructureです。
{
"type": "structure",
"attributes": {
"message": {
"type": "text",
"value": {"type": "static", "value": "Welcome, {{variable}}!"}
},
"variable": {
"type": "text",
"value": {
"type": "dynamic",
"expression": "user's name",
"nullable": true,
"default": "guest"
}
}
}
}
expressionはCQLです(croct-cqlスキルを参照)。defaultは評価が失敗した場合、nullを返した場合、または互換性のない型を返した場合に使用されます。nullableはスキーマ内の属性のoptionalフラグと一致する必要があります。variableのようなヘルパー属性は、アプリに配信されるコンテンツから除外するために、スキーマ内で"private": trueとマークします。- コンテンツの制限:最大30リストアイテム、最大10動的値、複雑度スコア50。
- 動的値はScaleプランの機能です。ローカライゼーションはアドオンです。
プレースホルダー、相対パス、フォールバック、およびすべてのコンテンツタイプについては:references/definition.mdを参照してください。
ローカライゼーション
- ロケールはワークスペースごとに設定されます。ローカライゼーションはアドオンです。
- サポートされるロケールごとに各スロットにデフォルトコンテンツを指定します。デフォルトコンテンツがないロケールはデフォルトロケールのコンテンツを使用します。エクスペリエンスと実験もロケールごとにコンテンツを保持します。ロケールのコンテンツがないエクスペリエンスは、スロットのデフォルトコンテンツを提供します。
- 要求されたロケールがない場合のフォールバック:SDKのドキュメントはワークスペースのデフォルトロケールを、Content APIのドキュメントはスロットのデフォルトロケールを参照しています。ドキュメント間で矛盾があるため、どちらかを選択せずにその旨を伝えてください。
- コード内でロケールを要求するには、
preferredLocale(フェッチごと)、defaultPreferredLocale(アプリ全体)、またはNext.js/HydrogenのlocaleResolverを使用します。croct-integrate-sdkスキルを参照してください。 croct.jsonはdefaultLocaleとlocalesを保持します。croct installはスロット、バージョン、ロケールごとにフォールバックコンテンツをダウンロードします。croct-use-cliスキルを参照してください。
バージョニングと破壊的変更
- コンポーネントスキーマを変更します(例:属性の追加や既存属性の変更)。
- 変更がアプリを壊す可能性のある方法でコンテンツ構造に影響を与える場合、Croctは新しいバージョンを作成し、アプリには古いバージョンを提供し続けます。
- 影響を受けるすべてのスロットとエクスペリエンスのコンテンツを更新します(ダッシュボードがそれらをフラグします)。
- アプリコード内のスロットバージョンを更新します(例:
fetchContent('home-hero@2'))。
すべきこと:
- アプリコードではスロットバージョンを固定します(
home-hero@1)。バージョンなしのIDは最新バージョンを使用するため、破壊的変更が即座に反映され、アプリを壊す可能性があります。 - スロットデフォルトコンテンツだけでなく、エクスペリエンスや実験のコンテンツも更新してください。
- 変更後はフォールバックコンテンツを同期してください(
croct-use-cliスキルで同期をカバーしています)。
してはいけないこと:
- どの編集が破壊的変更かを推測しないでください。ドキュメントにはリストされていません。ダッシュボードのスロット影響アラートに依存してください。
リファクタリング
| 状況 | テクニック |
|---|---|
| 変更をほとんどの使用箇所に適用するが、すべてではない場合 | 複製して修正:コンポーネントを複製し、少数の例外をコピーに向け、元のコンポーネントを変更する |
| 変更を少数の使用箇所にのみ適用する場合 | 複製して置換:複製、コピーを変更、該当する使用箇所をコピーに向ける |
| コンポーネントが最大ネストレベルに達した場合 | フラット化してマージ:ネストされた構造の属性を親にマージし、使用されているすべての場所でコンテンツを更新する |
| コンポーネントが複雑度制限に近い場合 | 別々のスロットでレンダリングされる小さなコンポーネントに分割する |
| 構造が30属性の制限に達した場合 | ガイダンス(ドキュメントにガイドはありません):関連する属性をネストされた構造にグループ化し、ネストレベル6以内に収めるか、別々のコンポーネントとスロットに分割する。固定されたスロットバージョン(home-hero@1)は、移行中、ライブアプリを古いスキーマで維持します |
詳細、手順、ベストプラクティス:references/design-and-refactoring.mdを参照してください。
テキスト属性内のMarkdown
- リッチテキスト(
@croct/richText)は、ツールバーエディターによって生成されたMarkdownを保存します。allowedFormattingで制限します。 - プレーンな
text属性は生の文字列を保存します。そこでMarkdownをサポートするには、アプリ内で例えば@croct/md-lite(太字、斜体、取り消し線、コード、リンク、画像、段落)を使ってパースします。 - シンプルな書式設定に留めてください。CTAラベル、URL、製品データは別々の属性としてモデリングしてください。
- サポートされる構文を属性の
descriptionに記載してください。例:「Supports markdown syntax for **bold**, *italic*, and [links](https://example.com\).」
レンダリングコード:references/design-and-refactoring.mdを参照してください。
落とし穴
- 単一のタイトルや画像に対してコンポーネントを作成すること。プリミティブまたはstructureを使用してください。
- 一般的な名前(「Product cards」)。目的に応じた名前(「Product feature list」)を優先してください。
- ユニオンの
typesキーが参照するコンポーネントIDと異なる場合:スキーマは無効です。 - 複数の
choicesエントリに"default": trueがある場合:スキーマは無効です。 - 秒を含まない日付/時刻値:
10:15は無効、10:15:00は有効です。 nullableがスキーマのoptionalフラグと一致しない動的値。- エクスペリエンスのスロットバージョンがアプリのバージョンと異なる場合、プレビューに古いコンテンツが表示されます。
ライブドキュメント
- インデックス:
https://docs.croct.com/llms.txt - 任意のページをMarkdownとして:
https://docs.croct.com/reference/content/schema/introduction.md - 検索:
https://docs.croct.com/api/search.md?term=<terms>