Model A - Single Product
Learn how to set up a sample product data model to display a single electronic product including all its variants.
After completing this page, you should be able to:
- Configure the product data model for a single product listing with its variants.
A key factor that you need to consider when deciding how to configure your product data model is to define how your product will be displayed on the Product detail, Product listing, and Search results pages. These decisions will impact the Product Type you create along with the Products and Product Variants.
We will explore this process as part of this module on advanced product data modeling, and explain various solutions depending on different business requirements.
We will use the iPhone 16 Product as a case study to learn about these decisions in more detail. In addition, we will analyze various iPhone 16 models to understand how to configure advanced product data modeling in Composable Commerce for different use cases.
The iPhone has a hierarchical structure of variations, starting with the base model and progressing through increasingly specific attributes:
- Base model: iPhone 16
- Model variant: Base (6.1 inch)
- colors (5)
- storage (3)
- colors (5)
- Model variant: Plus (6.7 inch)
- colors (5)
- storage (3)
- colors (5)
- Model variant: Pro (6.3 inch)
- colors (4)
- storage (4)
- colors (4)
- Model variant: Max (6.9 inch)
- colors (4)
- storage (3)
- colors (4)
- Model variant: Base (6.1 inch)
Now let’s explore how we can model the iPhone 16 Product in Composable Commerce.
Model A: Single product listing with full variant display
Our first product data model needs to meet the following requirements:
- Product listing page: display as a single Product.
- Search result: display as a single Product.
- Product detail page: display all Product combinations.
Set up Product data model in Composable Commerce
To fulfill the requirements of setting up our product data model in Composable Commerce, we need to configure the following settings and understand the resulting impact:
- Number of Products: 1
- Number of URLs: 1
- Number of Product Variants per Product: 58
- Price display: We will need to use a price range on the Product card (showing the lowest and highest price across all variants $1,399.00 - 2,849.00 AUD).
We can meet the above requirements by creating a Product Type with the following listed Product Attributes:
Product Type and Product Attributes summary
Attribute | Label | Type | Required | Searchable | Possible Values |
---|---|---|---|---|---|
color | color | enum | True | True | Ultramarine, Teal, Pink, White, Black, Black Titanium, White Titanium, Natural Titanium, Desert Titanium |
storage | storage | number | True | True | 128000, 256000, 512000, 1000000 |
display | Display Size | enum | True | True | 6.1, 6.3, 6.7, 6.9 |
display-type | Display Type | enum | False | False | Super Retina XDR |
promotion | ProMotion technology | boolean | False | True | |
always-on-display | Always-On Display | boolean | False | True | |
processor | Processor | enum | False | False | A18, A18 Pro |
gpu-cores | GPU Cores | number | False | False | |
camera-control | Camera Control | boolean | False | False | |
camera-system | Camera System | enum | False | False | Advanced dual, Pro |
main-camera | Main Camera | enum | False | False | 48MP Fusion |
telephoto | Telephoto | number | False | False | |
megapixels | Megapixels | number | False | False | |
emergency-sos | Emergency SOS | boolean | False | False | |
emergency-satellite-sos | Emergency Satellite SOS | boolean | False | False | |
video-playback | Video Playback | text | False | False | |
usb-version | USB Version | enum | False | False | 2, 3 |
cellular | Cellular | enum | False | False | 5G |
model | Model | enum | False | False | Base, Plus, Pro, Max |
Product Type JSON
The Product Type JSON object is shown here. You can use this JSON object in your own Project to create the same Product Type.
{"id": "7bf93cac-94ed-44ad-b8c0-91fb67103f42","version": 1,"versionModifiedAt": "2024-10-30T02:18:49.579Z","createdAt": "2024-10-30T02:18:49.579Z","lastModifiedAt": "2024-10-30T02:18:49.579Z","lastModifiedBy": {"clientId": "ViXgKzquNw-cXDYLmXagUH3Q","isPlatformClient": false},"createdBy": {"clientId": "ViXgKzquNw-cXDYLmXagUH3Q","isPlatformClient": false},"name": "iphone-a","description": "iphone-a","classifier": "Complex","attributes": [{"name": "color","label": {"en-AU": "color","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": true,"type": {"name": "enum","values": [{"key": "ultramarine","label": "Ultramarine"},{"key": "teal","label": "Teal"},{"key": "pink","label": "Pink"},{"key": "white","label": "White"},{"key": "black","label": "Black"},{"key": "black-titanium","label": "Black Titanium"},{"key": "white-titanium","label": "White Titanium"},{"key": "natural-titanium","label": "Natural Titanium"},{"key": "desert-titanium","label": "Desert Titanium"}]},"attributeConstraint": "CombinationUnique","isSearchable": true,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "storage","label": {"en-AU": "storage","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": true,"type": {"name": "number"},"attributeConstraint": "CombinationUnique","isSearchable": true,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "display","label": {"en-AU": "Display Size","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": true,"type": {"name": "enum","values": [{"key": "6.1","label": "6.1"},{"key": "6.3","label": "6.3"},{"key": "6.7","label": "6.7"},{"key": "6.9","label": "6.9"}]},"attributeConstraint": "CombinationUnique","isSearchable": true,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "display-type","label": {"en-AU": "Display Type","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "enum","values": [{"key": "super-retina-xdr","label": "Super Retina XDR"}]},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "promotion","label": {"en-AU": "ProMotion technology","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "boolean"},"attributeConstraint": "None","isSearchable": true,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "always-on-display","label": {"en-AU": "Always-On Display","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "boolean"},"attributeConstraint": "None","isSearchable": true,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "processor","label": {"en-AU": "Processor","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "enum","values": [{"key": "a18","label": "A18"},{"key": "a18-pro","label": "A18 Pro"}]},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "gpu-cores","label": {"en-AU": "GPU Cores","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "number"},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "camera-control","label": {"en-AU": "Camera Control","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "boolean"},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "camera-system","label": {"en-AU": "Camera System","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "enum","values": [{"key": "advanced-dual","label": "Advanced dual"},{"key": "pro","label": "Pro"}]},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "main-camera","label": {"en-AU": "Main Camera","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "enum","values": [{"key": "48mp-fusion","label": "48MP Fusion"}]},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "telephoto","label": {"en-AU": "Telephoto","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "number"},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "megapixels","label": {"en-AU": "Megapixels","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "number"},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "emergency-sos","label": {"en-AU": "Emergency SOS","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "boolean"},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "emergency-satellite-sos","label": {"en-AU": "Emergency Satellite SOS","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "boolean"},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "video-playback","label": {"en-AU": "Video Playback","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "text"},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "usb-version","label": {"en-AU": "USB Version","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "enum","values": [{"key": "2","label": "2"},{"key": "3","label": "3"}]},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "cellular","label": {"en-AU": "Cellular","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "enum","values": [{"key": "5g","label": "5G"}]},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"},{"name": "model","label": {"en-AU": "Model","en-NZ": "","zh": ""},"inputTip": {"en-AU": "","en-NZ": "","zh": ""},"isRequired": false,"type": {"name": "enum","values": [{"key": "base","label": "Base"},{"key": "plus","label": "Plus"},{"key": "pro","label": "Pro"},{"key": "max","label": "Max"}]},"attributeConstraint": "None","isSearchable": false,"inputHint": "SingleLine","displayGroup": "Other"}],"key": "iphone-a"}
iPhone 16 Product JSON example
Here's the JSON of the Product that we need to create in our module: iPhone 16 Product JSON.
- Total: 252 KB
- Per Projection (each Staged and Current): 126 KB each
- Variants: 124 KB
- Attributes: 48.7 KB
- Assets: 50.4 KB
- Prices: 20.0 KB
Attribute decisions
In this use case, we have chosen the storage
capacity to be modeled by using a number
attribute with a consistent unit of measure, specifically megabytes (MB). While iPhones offer storage options such as 125 GB, 256 GB, 512 GB, and 1 TB, storing these values as their megabyte equivalents (for example, 128000 MB, 256000 MB, 512000 MB, and 1024000 MB) provides us greater flexibility in filter and query operations.
This approach enables range-based searches, which help to filter products on Product listing pages and search results. For example, a customer could search for devices with storage greater than 200000 MB
(effectively 200 GB), which wouldn't be possible with mixed units like GB and TB. To ensure accurate comparisons and to simplify queries, all storage values in this project are measured in megabytes (MB) - the smallest common unit.
While the underlying data uses MB, you can easily and dynamically convert the displayed values for customers to more user-friendly units like GB or TB at the time of presentation. This provides a seamless customer experience while maintaining the backend's query capabilities. You can manage this conversion either in the frontend application logic, BFF, or some other layer. Alternatively, you could use a separate attribute to determine the displayed value, while still using storage for query operations.
We have used a similar strategy for megapixels
, telephoto
, and gpu-cores
; as a result, they are of type number
.
usb-version
is of type enum
because neither does the usb standard follow a strict numerical naming convention nor does it follow a clear naming convention. For example, USB 2.0, USB 3.0, USB 3.1 Gen 2, USB4, and USB4 Version 2.0 are all valid versions. This field could also be of type text
, but the benefit of using enum
is that we are ensuring that only valid values are used for the product. This is useful if Product data is updated by personnel across the Merchant Center. We have configured cellular
and display
in the same manner for similar reasons.
We use enum values for display-type
, model
, color
, processor
, main-camera
, and camera-system
to ensure data integrity. Although display
represents screen size in inches, it is also an enum. This helps include foldable phones with two screen sizes, storing both within a single attribute for simplified display. If filtering by screen size becomes necessary, we can refactor the display into separate attributes for standard and foldable phones.
promotion
, always-on-display
, camera-control
, emergency-sos
, and emergency-satellite-sos
are all boolean
values.
Note that display
is an enum field that stores number values. In this use case, it is not required to query this field with the same level of flexibility as is necessary for storage
. We can meet these requirements by using either type number
or enum
.
video-playback
is of type text
because there can be large variations in the string values. For example, up to 33 hours, 16 hours of video playback, and 20 hours of high power mode.
Attribute constraints
A specific Product Variant is a combination of color
, storage
, and display
. These are the important differentiating attributes for Product Variants. As a result, each Product Variant contains the CombinationUnique
constraint.
Attribute setting: Searchable
You might have noticed in the earlier sections that we have made only a few of the Product Attributes searchable. This is to ensure:
Searchable attribute optimization: marking only essential attributes as isSearchable
improves the speed of Product Search indexing.
The isSearchable
attribute only affects Product Projection Search and Product Search endpoints, not Products or Product Projections.
Note: For optimal performance with large or low-latency Product or Product Projection queries, refer to our indexes and query optimization guide.
External Search vs. Product Search
External search engines can provide more control and flexibility over data management and querying as compared with in-built product search solutions. This advantage is due to the ability of external search engines to customize how your data is structured and indexed.
For instance, a single iPhone 16 Product representing all variations can be broken down into searchable entities by model (Pro, Pro Max), color (Space Black, Silver), storage (128 GB, 256 GB), or any other combination. This granular approach enables more precise control over the Product cards that are displayed on the search and category pages. Instead of a single iPhone 16 search result, users can view distinct entries for each model (such as iPhone 16 Pro and iPhone 16 Pro Max) on the Search results and Product listing pages.
Conversely, you can aggregate disparate iPhone 16 product data in an external search engine. Instead of indexing 18 separate products (one for each model-storage combination), you could consolidate them into a single record or four model-specific records.
In addition, you can control the data synced to an external search engine to minimize the number of synchronized fields and reduce the query payload size, thereby improving the search performance.