SportRx Customizer — Data Model

Visual anatomy of the net-new data entities for the product customizer. Companion to the flow wireframes — these frames answer "what does the data actually look like?" rather than "what does the user see?" Stub values use Huckson goggle (5-step template) and Oakley Sport frame as reference products. Schema reconciled against wireframes v2 + the Magento dependency export CSVs.

Aurora (AWS) Shopify Metaobject Shopify Metafield Shopify Order Attribute Net-new (no Magento equivalent) Magento → Shopify (Logan's sheet)
D0

Entity map — all customizer entities + relationships

Overview
Aurora (AWS)
Build Aurora ULID PK
build_idULIDOnly ref allowed outside Aurora
product_idID
product_handlestring
frame_variant_idIDRuntime ref (can churn)
frame_variant_skustringStable join key → Logan's sheet
selections[]SelectionItem[]step + option + sku + price_delta
prescription_idULID?Null if plano order
goggle_insert_variant_idID?Goggle orders only
price_totaldecimal
marketMarketContextCountry + currency + lang snapshot
ruleset_versionstringLocked at Build creation
customer_idID?Null for guest checkout
statusenumdraft → carted → ordered → cancelled
created_attimestamp
ordered_attimestamp?Set on status flip to ordered
SelectionItem (array element)
stepstringStep key, e.g. lens_feature
keystringOption key, e.g. build-your-own
skustringStable → Logan's sheet
price_deltadecimal
dvi_codestring
Shopify Metaobjects (net-new)
rule_template Metaobject
namestringe.g. "Oakley Sport Template"
applies_to_product_typestringe.g. "goggle", "frame"
steps[]StepObject[]Embedded — see D1
rules_by_step{stepKey: rule refs}Per-step rule map; chunked fields (rules_vision_type, …) to dodge 256-ref cap
Step object (embedded in metaobject field)
keystringe.g. lens_feature
titlestringDisplay label
oos_behaviorenumhide / disable — step-level default
options[]configurator_option refsMax 256 per step
configurator_option Metaobject
step_keystringWhich step this option belongs to
titlestringTile label
imageimage refTile image
product_refhandleShopify product
variant_idIDRuntime (can churn)
dvi_codestring?Nullable
out_of_stock_behaviorenumshow/hide/disable — also on step (see OQ-7)
price_deltadecimalConfirmed in v2 schema
No explicit option_key field — rules reference options via OptionRef (metaobject handle = Magento option_type_sku, e.g. feat-polarized).
rule Metaobject
keystringe.g. byo-shows-coating
typeenumdependency / independence
triggerOptionRef[]Metaobject refs, not strings
effectenumshow / hide / require
targetsOptionRef[]Metaobject refs, not strings
priorityintegerEval order when rules conflict
goggle_insert_compatibility Metaobject
goggle_frame_upcstringGoggle frames only
compatible_insert_upcs[]string[]
Shopify (metafields + order)
Product metafields Metafield
ruleset.templateMO refPoints to rule_template
ruleset.overrides_by_stepJSONPer-product step overrides
ruleset.removed_rule_keysstring[]Rules suppressed for this product
Filterable attrs (reflective, polarized, acetate type) → must be Metafield (not Metaobject) for Algolia indexability. Level: product vs. variant TBD — Karan.
Order attributes Order
build_idULIDImmutable post-order
prescription_idULID?Nullable
_pdstringFittingBox — binocular PD
_pd_leftstringFittingBox — monocular
_pd_rightstringFittingBox — monocular
spec_rolestring ⚠️In wireframe C10, not in SA — confirm

Key relationships

  • Build.frame_variant_sku → Logan's Products & Variants tab (stable join)
  • Build.selections[].sku → same tab (each selected option's SKU)
  • Build.prescription_id → Prescription entity (OM model)
  • rule_template.steps[].options[]configurator_option metaobject refs
  • rule_template.rules_by_step[stepKey]rule metaobject refs (chunked per step)
  • rule.trigger[] and rule.targets[]configurator_option metaobject refs (not string keys)
  • Product.ruleset.templaterule_template metaobject
  • build_id (order attr) ← shopify-sync Lambda stamps on orders/create

What's NOT here

  • Rx clinical values (Aurora rx_* schema) — OM model only
  • Base product / variant data — Logan's migration sheet
  • Order management portal tables — OM model only
⚠️ Option handle convention — no explicit option_key field; rules reference options via OptionRef. The string key (Magento option_type_sku, e.g. feat-polarized) becomes the metaobject handle. Confirm 1:1 mapping + uniqueness. OQ-3.
D1

rule_template anatomy — Huckson goggle (5-step)

Schema anatomy
Metaobject structure
rule_template: huckson-goggle-v1
applies_to_product_type: "goggle"
steps: [StepObject × 5]
▸ Step 1 vision_type
title: "Vision Type"
oos_behavior: hide
options: [plano, rx]
▸ Step 2 lens_material
title: "Lens Material"
oos_behavior: hide
options: [polycarbonate, trivex, …]
▸ Step 3 lens_feature
title: "Lens Feature"
oos_behavior: disable
options: [sport-optimized, build-your-own, polarized]
▸ Step 4 coating
title: "Coating"
oos_behavior: disable
options: [ar-scratch, no-coating]
▸ Step 5 add_ons
title: "Add-ons"
oos_behavior: hide
options: [goggle-insert-rx, …]
Rules — referenced via rule_template.rules_by_step (sample)
rule: byo-shows-coating
type: dependency
trigger: [→ build-your-own OptionRef]
effect: show
targets: [→ ar-scratch OptionRef]
priority: 1
rule: sport-opt-hides-coating
type: independence
trigger: [→ sport-optimized OptionRef]
effect: hide
targets: [→ ar-scratch OptionRef]
priority: 2
rule: rx-requires-insert
type: dependency
trigger: [→ rx OptionRef]
effect: require
targets: [→ goggle-insert-rx OptionRef]
configurator_option metaobjects (Step 3 — lens_feature)
sport-optimized Metaobject
handlesport-optimized (= option_type_sku)
product_refsport-optimized-lens
variant_id→ resolves to SO-LNS-001 SKU
price_delta+$0
dvi_code(TBD — Logan)
build-your-own Metaobject
handlebuild-your-own
product_refbuild-your-own-lens
variant_id→ resolves to BYO-LNS-001 SKU
price_delta+$10
polarized Metaobject
handlepolarized (= feat-polarized)
product_refpolarized-lens
variant_id→ resolves to POL-LNS-001 SKU
price_delta+$20
Per-product override (product metafield)
ruleset.template → huckson-goggle-v1
ruleset.overrides_by_step → {} (none for base product)
ruleset.removed_rule_keys → []

How steps are stored

  • Steps are embedded as structured objects within rule_template — not a separate metaobject type
  • Each step's options[] is a Shopify reference field (max 256 refs per field)
  • The Lambda chunks writes per step to stay under the 256-ref cap when saving or duplicating
  • Rules are separate rule metaobjects, stored alongside (not inside) steps

How rules are evaluated

  • Rules engine: evaluate(ruleset, current_selections, market, inventory)
  • Independence rules run before dependency rules
  • ~87 rules on Huckson; ~142 on complex brand frames (why eval must be server-side)
  • Result: { visible[], disabled:[{key,reason}], next_step, price_total }
⚠️ OOS behavior on step AND option — v2 carries an out-of-stock setting on both the step (oos_behavior) and the option (out_of_stock_behavior). Confirm precedence — likely option overrides step default. OQ-7.
D2

Build record lifecycle — field values at each status

Schema anatomy
Rows highlighted in green = newly set at this status. amber = changed from prior status. blue = immutable from this point.
draft Created at first step call
build_id01HXN3P4Q5R6S7T8…
status"draft"
product_idgid://shopify/Product/…
product_handle"huckson-goggle"
frame_variant_idgid://shopify/…
frame_variant_sku"HUCK-BASE-STD"
ruleset_version"huckson-goggle-v1"
market{country:"US", currency:"USD"}
selections[] → grows with each step
prescription_idnull
price_total149.00 (frame base)
customer_idnull (guest)
ordered_atnull
carted After cart add (C10)
build_id01HXN3P4Q5R6S7T8…
status"carted"
product_idgid://shopify/Product/…
frame_variant_sku"HUCK-BASE-STD"
ruleset_version"huckson-goggle-v1"
selections[{step_key:"lens_feature", variant_id:"gid://…", sku:"BYO-LNS-001", price_delta:10}, {step_key:"coating", variant_id:"gid://…", sku:"AR-SCR-001", price_delta:29}, …]
prescription_id01RXN3P4Q5R6S7T8…
goggle_insert_variant_idgid://shopify/…
price_total237.00
ordered_atnull
ordered After shopify-sync Lambda
build_id01HXN3P4Q5R6S7T8…
status"ordered"
frame_variant_sku"HUCK-BASE-STD"
ruleset_version"huckson-goggle-v1"
selections[…immutable…]
prescription_id01RXN3P4Q5R6S7T8…
price_total237.00
ordered_at2026-05-28T18:34:00Z
What shopify-sync Lambda does at orders/create: reads build_id from order line-item properties → flips Build.status = "ordered" → sets ordered_at → stamps build_id + prescription_id onto Shopify order metafield → starts Step Functions lifecycle workflow. Build is now immutable.
D3

Import sheet tab map — stub rows + join keys

Import sheet
Tab origin rule: Products & Variants is Logan's tab (Magento → Shopify). Customizer Config, Options, Rules, and Compatibility are entirely net-new — no Magento counterpart. The only place the two workstreams touch is variant_sku.

Products & Variants

Logan's sheet (Magento → Shopify)
handletitleproduct_typevariant_title variant_sku ↔ join key pricedvi_code
huckson-goggleHuckson GogglegoggleStandardHUCK-BASE-STD149.00(Logan)
sport-optimized-lensSport Optimized LenslensDefaultSO-LNS-00189.00(Logan)
build-your-own-lensBuild Your Own LenslensDefaultBYO-LNS-00179.00(Logan)
polarized-lensPolarized LenslensDefaultPOL-LNS-001109.00(Logan)
ar-scratch-coatingAR + Scratch CoatingcoatingDefaultAR-SCR-00129.00(Logan)
goggle-insert-rxGoggle Rx InsertinsertDefaultINS-RX-00149.00(Logan)

Customizer Config

Net-new — no Magento equivalent
template_keyapplies_to_product_type step_keystep_titlestep_orderoos_behavior
huckson-goggle-v1gogglevision_typeVision Type1hide
huckson-goggle-v1gogglelens_materialLens Material2hide
huckson-goggle-v1gogglelens_featureLens Feature3disable
huckson-goggle-v1gogglecoatingCoating4disable
huckson-goggle-v1goggleadd_onsAdd-ons5hide
oakley-sport-v1framevision_typeVision Type1hide
oakley-sport-v1framelens_typeLens Type2hide
oakley-sport-v1framecoatingCoating3disable

Options

Net-new — no Magento equivalent
variant_sku here must match a row in Products & Variants ↑
handle (option_type_sku)step_keytemplate_key product_refvariant_sku ↔price_delta
sport-optimizedlens_featurehuckson-goggle-v1sport-optimized-lensSO-LNS-001+$0
build-your-ownlens_featurehuckson-goggle-v1build-your-own-lensBYO-LNS-001+$10
polarizedlens_featurehuckson-goggle-v1polarized-lensPOL-LNS-001+$20
ar-scratchcoatinghuckson-goggle-v1ar-scratch-coatingAR-SCR-001+$29
no-coatingcoatinghuckson-goggle-v1+$0
goggle-insert-rxadd_onshuckson-goggle-v1goggle-insert-rxINS-RX-001+$49

Rules

Net-new — no Magento equivalent
trigger and targets here are option_key slugs; Lambda resolves to OptionRef metaobject refs at import time
rule_keytemplate_keytypetriggereffecttargetspriority
byo-shows-coatinghuckson-goggle-v1dependencybuild-your-ownshowar-scratch1
sport-opt-hides-coatinghuckson-goggle-v1independencesport-optimizedhidear-scratch2
rx-requires-inserthuckson-goggle-v1dependencyvision_type:rxrequiregoggle-insert-rx1

Compatibility

Net-new — goggle frames only
goggle_frame_upccompatible_insert_upcs
HUCK-UPC-001INS-UPC-001, INS-UPC-002, INS-UPC-003

Consolidation call agenda

  • Products & Variants: Logan brings real SKUs; swap in for stub values in Options tab
  • Confirm template_key naming convention (OQ-6)
  • Confirm option handle = Magento option_type_sku, unique across templates (OQ-3)
  • Confirm step-vs-option OOS precedence (OQ-7)
  • Identify any Magento option/rule data that could seed the Rules tab

What Logan doesn't need to touch

  • Customizer Config, Options, Rules, Compatibility — all net-new
  • Rx / prescription data — AWS-only (OM model)
  • Build records — runtime, not imported
price_delta confirmed in v2 schema — stays on the Options tab. The bigger seeding question is whether the Rules tab is hand-built or generated from the Magento dependency CSVs (see the migration tracks doc).
D4

Cart line-item schema — what C10 produces (Huckson Rx build)

Cart + order
One cart add per spec component. All lines share build_id. Cart Transform Function collapses these into one nested line at checkout. Stub values from a completed Huckson goggle + Rx build.
frame Huckson Goggle — Matte Black / L SKU: HUCK-BASE-STD · $149.00
build_id01HXN3P4Q5R6S7T8U9V0W1X2Y3
spec_role ⚠️"frame"
prescription_idnull (frame line carries null)
_pdnull (set on Rx slot line)
lens Build Your Own Lens SKU: BYO-LNS-001 · +$10
build_id01HXN3P4Q5R6S7T8U9V0W1X2Y3
spec_role ⚠️"lens"
prescription_idnull
_pdnull
coating AR + Scratch Coating SKU: AR-SCR-001 · +$29
build_id01HXN3P4Q5R6S7T8U9V0W1X2Y3
spec_role ⚠️"coating"
prescription_idnull
insert Goggle Rx Insert SKU: INS-RX-001 · +$49
build_id01HXN3P4Q5R6S7T8U9V0W1X2Y3
spec_role ⚠️"insert"
prescription_idnull (Rx slot line carries it)
rx slot Prescription Rx (slot) SKU: RX-SLOT-001 · +$0
build_id01HXN3P4Q5R6S7T8U9V0W1X2Y3
spec_role ⚠️"rx"
prescription_id01RXN3P4Q5R6S7T8U9V0W1X2Y3
_pd"63"
_pd_left"31.5"
_pd_right"31.5"
After Cart Transform Function: Customer sees one nested line — "Huckson Goggle — custom build · $237.00" with child items collapsible. Underlying cart retains all 5 lines. shopify-sync reads build_id from any line and looks up the Build record in Aurora.
What stamps on the Shopify Order (after checkout):
Order attribute: build_id = 01HXN3P4Q5R6S7T8…
Order attribute: prescription_id = 01RXN3P4Q5R6S7T8…
(Attribution attrs — _channel, _initial_rep, _current_rep — moved to Order Management scope in v2; not stamped by the customizer.)

Systems involved

Theme (cart add orchestration) Shopify Cart AJAX API Cart Transform Function Aurora builds FittingBox (PD props)

Property contract notes

  • build_id — on every line, always
  • prescription_id — on the rx slot line only
  • _pd* — on the rx slot line (set by FittingBox at add-to-cart)
  • Cart Transform reads line properties only — no outbound HTTP calls
  • Plano builds: no rx slot line; no prescription_id
⚠️ spec_role — present in wireframe C10, absent from FigJam cart diagram and Karan's SA schema. If dropped, Cart Transform groups by build_id alone (product type + parent/child structure distinguishes lines). Confirm with Karan before writing Cart Transform stories. OQ-1.
OQ

Open questions — items blocking stories or import sheet finalization

Reference
ID Question Blocks Owner Appears in
OQ-1 spec_role line-item property — in wireframe C10 (frame / lens / coating / add-on / insert / rx), absent from FigJam + Karan's SA. Canonical model leaves role implicit. Keep or drop? Cart Transform stories; D4 Justin + Karan D4
OQ-2 Product-level vs variant-level for filterable attrs (reflective, polarized, acetate type). Must be Metafield (not Metaobject) for Algolia. Level determines metafield namespace. Product metafield schema; Logan's migration sheet Karan + Logan D0
OQ-3 Option handle convention — no explicit option_key field; rules reference options via OptionRef. The string key (Magento option_type_sku, e.g. feat-polarized) becomes the metaobject handle. Confirm 1:1 mapping + uniqueness across templates. CSV import handle resolution; rule authoring Karan + Logan D1, D3
OQ-7 Step vs option OOS behavior — v2 carries an out-of-stock setting on both the step (oos_behavior) and the option (out_of_stock_behavior). Confirm precedence (likely option overrides step default) and whether both are authored. Rule authoring UI; rules engine OOS handling Karan D0, D1
OQ-5 Options-per-step count vs 256-ref cap — 256-ref limit is per step's options list. Do any product types exceed this? Unlikely given Huckson's 87 rules total, but confirm for goggle frames. rule_template implementation Justin + Logan D1
OQ-6 template_key naming convention — working assumption: human-readable slug + version suffix (e.g. huckson-goggle-v1). Confirm versioning scheme before populating the import sheet. Customizer Config tab; ruleset_version in Build Karan + SportRx D2, D3