Development

8 min read

What 10,000 Catalogs Taught Us About Product Characteristics

Jon Fleschler

Written by Jon Fleschler

Published on Oct 13, 2026

Ecommerce admin product inventory screen showing SKU, product title, characteristics, price, and stock level columns

A merchandiser at a telecom provider needs every new handset to carry a Network value, such as 5G or 5G Advanced. It has to show on the product page, be filterable in search, and be something a promotion can target. In most commerce platforms that request becomes a ticket: someone adds a column, updates an API contract, redeploys, and reindexes.

Now suppose the same need comes from a school-products manufacturer. It runs one central catalog that feeds roughly 10,000 affiliate shop catalogs, and each shop needs its own subset of colors, its own defaults, and the option to override a single value without losing every future update from headquarters.

Those two scenarios shaped Broadleaf's Characteristics, part of the merchandising suite, over the last two major releases. We introduced characteristics as part of our telco merchandising work. This post covers what happened next: what we added, what broke under real-world scale, and how the model works today.

Extending the catalog with data, not schema

A Characteristic is a typed property defined on a Business Type (also called a Product Type), such as Phone, Apparel, or Service Plan. Each characteristic has:

  • A value type: string, HTML, enum, decimal, number, integer, date, boolean, or variant option, with individual values taggable as colors or sizes.
  • Minimum and maximum cardinality, so a field can be required or hold multiple values
  • Default values that pre-fill new products
  • Optional regex validation with a custom error message
  • Translations for labels, and for values of text-based types

The admin builds each product's maintenance form from its Business Type's characteristics. Add one, and the new field appears with no code change and no deployment.

That was a deliberate choice, made while building toward TM Forum's TMF620 catalog standard: extend the product model via a schema that changes with the business, or a stable schema that carries the extensions as data. Teams on the schema path told the same story: every product-model change meant reprogramming the middleware around it. Characteristics keep the API contract fixed while the product model keeps changing. That choice paid off: Business Types already gave us a data-driven product model, so characteristics extended it rather than replacing it, and TMF620's product specifications map naturally onto both.

Business Types that inherit

Real catalogs are hierarchical. A Flagship Phone is still a Phone: same battery, memory, camera, and display characteristics, plus a few of its own.

Business Types can now inherit from other Business Types. A child type receives every characteristic from its ancestors, and the admin grid labels each one so it's clear where it came from:

  • Inherited: it comes from an ancestor type and follows that ancestor's changes.
  • Overridden: it started as inherited, and someone changed it for this type (for example, narrower memory options).
  • Custom: it was created on this type and exists only here.

When the same characteristic is defined at more than one level of the hierarchy, the closest ancestor wins. Form customizations made on a parent type, such as groupings and field placement, also carry down to its children. You define Phone once and adjust it only where the flagship is actually different.

Characteristics that do real work

A characteristic that only displays on a product page is useful. One that drives pricing and shopper choice is much more valuable. Characteristics can now do all of this:

  • Let the shopper choose. A configurable characteristic becomes a standard product option, with enum, boolean, color, and size values generating their choices automatically.
  • Generate variants. A variant option characteristic, such as Color or Capacity, becomes a variant-distinguishing option and drives SKU generation.
  • Change the price. A characteristic marked as a pricing target can change a product's price, for example a limited-edition gold finish costing more.
  • Feed rule builders automatically. Starting with Release Train 3.0, a targetable characteristic adds itself to the rule builders for offers and rule-based categories, so merchandisers can target it in a promotion without touching metadata.
  • Power search and facets. Any characteristic can become a search field and a facet after a reindex.
  • Import in bulk. Product imports carry characteristics in a single column, multi-valued ones included.

What a multi-tier catalog taught us

The school-products manufacturer's deployment was the stress test: one central catalog feeding about 10,000 affiliate catalogs through catalog inheritance, each affiliate customizing what it sells. Supporting that produced three new capabilities:

  • Value narrowing. For characteristics with narrowing turned on, a child catalog can turn off values it doesn't sell, for example limiting colors to a school's own. Only the enabled values are offered to shoppers.
  • Placeholder characteristics. Some values don't belong on the type definition at all. A placeholder leaves the definition empty and makes the product-level value the source of truth.
  • Form positioning. Any characteristic can be placed anywhere on the product form instead of in one generic "Characteristics" section.

The biggest lesson was structural. Product characteristics used to be stored as part of the product's own record and tracked together as a single unit. At 10,000-catalog scale that broke: an affiliate changing one characteristic overrode all of them for that product, and updates from the central catalog stopped reaching any of them.

In 2.3.0, each Product Characteristic became an independently tracked record, so each one can be overridden on its own. An affiliate can change one value and keep inheriting everything else. The upgrade's migration also cleans up values that only repeated what a parent catalog already had, so those products go back to inheriting.

This produced a principle we now apply throughout: persist only real overrides. When a product is created, values matching its Business Type's defaults aren't stored on it, so upstream changes keep flowing to it. Imports store only what you actually supply, and an override always takes precedence over an inherited value.

Performance at variant scale

Characteristics that generate variants also generate volume. Products with hundreds of variants meant loading a page or adding to cart pulled far more variant data than the request needed.

In 2.3.0, callers can choose how much variant data to load: all of it, a lite version through the Lite Variants API, or none. Lite variants give the storefront a compact map from option selections to SKUs, resolving a shopper's choice without loading every variant in full. A call for a phone with Color and Capacity options looks like this:

GET /lite-variants/{productId}

[
  {
    "id": "VAR-4471-BLK-256",
    "productId": "PROD-4471",
    "sku": "SKU-4471-BLK-256",
    "optionValues": { "color": "Black", "capacity": "256GB" }
  },
  {
    "id": "VAR-4471-BLU-512",
    "productId": "PROD-4471",
    "sku": "SKU-4471-BLU-512",
    "optionValues": { "color": "Blue", "capacity": "512GB" }
  }
]

The storefront never has to pull full variant records just to answer "does this combination exist, and what's its SKU." In our performance testing on a product with 561 variants, add-to-cart time dropped from about 4 seconds to about 1.6 seconds.

Where this model has limitations

Characteristics are flexible, and that flexibility has trade-offs to know about up front:

  • Renaming or removing a value that a rule targets. The rule can quietly stop matching. We're designing guardrails for this.
  • Resetting an override. No one-click way yet to reset a product's characteristic back to its inherited value.
  • Enum facet labels. Facets on enum characteristics can show the value's key instead of its label; facet on the label field instead.
  • Changing a product's Business Type. It's fixed once the product is created, since that type defines the product's whole form and data.
  • Upgrading to 2.3.0. The independently-tracked-records migration doesn't touch sandbox records, so promote and deploy pending changes first.

What this means if you're evaluating the platform

If your product model changes often, because you sell telecom plans, configurable goods, or anything where "just add a field" comes up weekly, characteristics let your merchandising team make those changes without an engineering release. Inheritance keeps large type hierarchies manageable, and rule-builder and pricing targeting mean a new field starts working the day it's created. The lessons from a 10,000-catalog deployment are now part of the core framework, not left for each implementation to work out.

Want to see the data model yourself? The Characteristic and Business Type reference docs are on Broadleaf Dev Central.

Sources

  • Broadleaf Catalog Service release notes and docs, version 2.3.0 on Broadleaf Dev Central — basis for the domain-separation migration, "persist only real overrides" behavior, variant-option/characteristic field definitions, and Lite Variants API details
  • The 561-variant add-to-cart figure comes from our own performance testing, not an independently audited benchmark

For the original introduction of characteristics and the Business Type model they extend, see Beyond the Default: Customizing Product Data Models with Characteristics.

Related Resources