Skip to content

Categories

Feature deep dive. Service identity lives in Commerce Overview; category field tables in Domain Model §3.9.

Categories organize products into a hierarchy. They come in two types: SYSTEM categories (read-only, shared across merchants of the same business type) and custom categories (merchant-owned, fully editable).

REST Endpoints

Full reference: live OpenAPI at /v1/api/commerce/doc/openapi.json. Standard CRUD via ControllerFactory; base path /categories.

There are no custom routes (no /categories/tree, no /categories/{id}/move, no /categories/{id}/breadcrumb). There is no CategoryService — the controller handles all custom logic directly.

Category Types

TypemerchantIdEditableVisible to
SYSTEMnullNo (403 on update/delete)Merchants matching the category's businessType
CustomMerchant IDYesOnly that merchant

Category Roles (Category.type)

Category.type (CategoryTypes: REGULAR / COMBO / ADDON / FBT) is an FE-grouping label only — it is not an inventory discriminator. Nothing in the inventory or sale path branches on it.

The structural "is this a virtual combo?" decision lives on ProductVariant.type (ProductVariantTypes.COMBO, beside KIT). The COMBO / ADDON / FBT relations live in the single ProductBundler table (ProductBundler.type). See ADR-0003 and the cross-package combo explosion ADR.

Because Category.type no longer drives behavior, re-typing a category or moving a Product between categories is safe — it cannot flip a variant between physical and virtual.

Merchant-Scoped Access

For non-admin users, find() requires a merchantId in the filter. The controller then returns the union of:

  1. SYSTEM categories where businessType matches the merchant's businessType
  2. Custom categories where merchantId matches

SYSTEM Category Protection

  • updateById() blocks updates to categories where type === 'SYSTEM' → HTTP 403
  • deleteById() blocks deletion of SYSTEM categories → HTTP 403

Deletion Policy

Category deletion goes through DeletionPolicyService.deleteCategoryById():

Merchant's strictCategoryDeletionCategory has productsBehavior
true (default)YesHTTP 409 — cannot delete
trueNoDelete allowed
falseYesProducts' categoryId set to null, then category deleted
falseNoDelete allowed

The deletion policy is configured per merchant via PUT /merchants/{id}/deletion-policy.

PageDescription
Commerce OverviewService identity + catalog
Domain ModelCategory field tables
ProductsProduct catalog
ADR-0003Why Category.type is grouping-only

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