Skip to content

PRD: Product options & variants

ModuleProduct (CORE-05)PRD IDPRD-OPT-001
StatusShippedOwnerProduct squad
Date2026-06-12Versionv1.0
Packages@nx/commerce · @nx/coreURDOPT · VAR

TL;DR

Lets a merchant describe what makes each variant different. The owner sets up the options a product varies by - size, ice level, colour - each with its list of values. Every sellable variant then takes one value per option it uses. The system keeps these combinations honest: a value must belong to the product, and no two variants may share the same combination - so the POS, online channels, and reporting can all treat a variant's combination as its identity.

1. Context & Problem

Variants say a product has several sellable forms, not why they differ. With only free-text names, nothing stops two identical variants, the POS can't tell that size and ice are separate choices, and there's no stable combination to match an external listing against. This feature makes options, their values, and each variant's combination explicit - declared by the owner and kept consistent by the system.

2. Goals & Non-Goals

Goals

  • Options per product, each with an ordered list of values, shown in the owner's chosen order.
  • Each variant takes one value per option it uses.
  • The system guarantees a variant's combination is valid and unique among the product's variants.
  • A merchant setting decides whether every variant must use options, or whether option-free variants are allowed.

Non-Goals

  • No automatic generation of every possible combination - owners declare only the variants they actually sell.
  • No price or stock on a value - pricing stays in fares (FAR), stock stays in Inventory.

3. Success Metrics

MetricTarget
Duplicate combinationsZero - a variant repeating an existing combination is always refused
Invalid combinationsZero - a value that isn't part of the product is never accepted
Strict-options enforcementWhen the setting is on, no option-free variant can be created

4. Personas & Use Cases

PersonaGoal
Owner / ManagerDefine how a product varies and the choices offered, in display order
CashierPick a variant by choosing size, then ice - not from one long list
Channel integrationMatch an external listing to the right variant by its combination

Core scenario: an owner sets up a milk tea with size (S/M/L) and ice (100/50/0), then declares the five combinations actually sold. Adding a second "Large, 50%" variant is refused; so is choosing two sizes for one variant.

5. User Stories

  • As an owner, I define a product's options and values once, so every variant just picks from them.
  • As an owner, I want a combination that already exists to be refused, so the catalogue never holds two entries for the same thing.
  • As an owner, I choose whether option-free variants are allowed, so simple products stay simple and complex menus stay disciplined.
  • As a cashier, I see variants grouped by option, so ordering is a few taps.

6. Functional Requirements

#RequirementURD
FR-1Owner can define a product's options, each shown in a chosen orderURD-OPT-001
FR-2Each option carries an ordered list of valuesURD-OPT-002
FR-3An option and its values are created or changed together in one stepURD-OPT-003
FR-4A variant declares its value on each option it uses; an unknown option or value is refusedURD-OPT-004
FR-5A variant takes at most one value per optionURD-OPT-005
FR-6Every value a variant uses must belong to the product and to its optionURD-OPT-006
FR-7No two variants of a product may share the same combination; option-free variants are exemptURD-OPT-007
FR-8A merchant setting can require every variant to use options, refusing option-free onesURD-OPT-008
FR-9An owner can change which options a variant usesURD-OPT-009
FR-10An option offered with no values, or a duplicated choice, is refusedURD-OPT-010

Every refusal returns a clear, specific reason so the owner can correct it.

7. Non-Functional Requirements

  • All-or-nothing - options and a variant's values are saved together: either every change is saved or none is.
  • Per-merchant isolation - options belong to one product under one merchant; never shared across products or merchants.
  • Languages - option and value names are shown in multiple languages.
  • Order-independent - a combination is the same regardless of the order its values are listed.

8. UX & Flows

9. Data & Domain

ConceptRole
OptionA way a product varies (size, ice level), with an ordered list of values
Option valueOne choice on an option (S / M / L)
CombinationThe set of values a variant takes - its identity within the product

An option's name is unique within its product; a value is unique within its option; a variant's combination is unique among the product's variants.

10. Dependencies & Assumptions

  • Variants exist per PRD-PRD-001 - options attach to variants, they don't replace them.
  • The strict-options setting is read when a variant is saved; turning it on later does not re-check variants that already exist.

11. Risks & Open Questions

  • Removing a value still used by a variant follows the Commerce deletion policy.
  • Turning the strict setting on leaves existing option-free variants in place - only new changes are checked.

12. Release Plan & Launch Criteria

Released together with the variant capability. Launch criteria: invalid and duplicate combinations are refused with a clear reason; uniqueness holds even when variants are created at the same time; the POS shows options in the owner's order.

13. FAQ

Why not generate every combination for me? Most merchants don't sell the full matrix. Declaring only what's sold keeps the catalogue equal to what's actually sellable - no phantom variants to hide, price, or track stock for.

Can two products share options? No - options belong to one product. Shared templates may come later if duplication becomes painful.

Where do option-dependent prices go? On the variant's fare. The combination identifies the variant; the fare prices it.

References

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.